内容简介:占小狼,转载请注明原创出处,谢谢!想在现有的接口加上熔断降级或者限流的功能,比较好的方式是通过注解的方式,并基于动态代理进行实现,下面代码是Rhino的实现通过在方法添加@Degrade注解,很方便的赋予了method方法熔断降级功能,在该方法的失败率达到阈值时,就自动熔断,并调用降级方法。
占小狼,转载请注明原创出处,谢谢!
背景
想在现有的接口加上熔断降级或者限流的功能,比较好的方式是通过注解的方式,并基于动态代理进行实现,下面代码是Rhino的实现
@Rhino public class ServiceImpl { @Degrade(rhinoKey = "syncMethod-0", fallBackMethod = "fallbackMethod") public void method() throws Exception { int i = 1 / 0; } private String fallbackMethod() throws Exception { return "fallback"; } }
通过在方法添加@Degrade注解,很方便的赋予了method方法熔断降级功能,在该方法的失败率达到阈值时,就自动熔断,并调用降级方法。
这里的动态代理并没有使用Spring的AOP,而是自己实现了 BeanPostProcessor
和 BeanFactoryPostProcessor
接口,另外也实现了 PriorityOrdered
接口。
在生成动态代理对象的时候,根据类是否有实现接口,选择使用JDK的Proxy还是使用Cglib。
/** * @param clazz * @param origin * @return */ private static Object createProxyService(Class clazz, Object origin) { Object proxy; if (clazz.getInterfaces().length > 0) { proxy = Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), new RhinoInvocationHandler(origin, clazz)); } else { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clazz); enhancer.setCallback(new RhinoInvocationHandler(origin, clazz)); proxy = enhancer.create(); } return proxy; }
这样的实现,在99%的情况下,是没有问题的,直到有一天,有两个业务同时反馈了一个问题,使用了熔断降级的注解之后,发现自身的Spring AOP注解失效了或者是直接启动异常,WTF,这个问题一直都没考虑过好吗...
为什么会这样?Spring AOP到底干了什么,或者是自己的注解到底有什么问题?
复现问题
为了快速定位问题,并解决问题,最好的办法就是复现该问题,写了一个简单的Spring AOP的例子。
@Component //加入到IoC容器 @Aspect //指定当前类为切面类 public class Aop { @Pointcut("execution(* com.dianping.rhino.aop.*.*(..))") public void pointCut(){ } @Before("pointCut()") public void begin(){ System.out.println("begin"); } @After("pointCut()") public void close(){ System.out.println("close"); } @Around(value = "@annotation(MethodLog)") public void around() { System.out.println(" MethodLog "); } }
这里简短的解释下Spring AOP各个注解的作用
@Aspect
切面,标识该类是一个切面类
@Pointcut
切入点,用来标识哪些方法是需要被添加切面的
@Before
在切入点,执行方法之前进行增强
@After
在切入点,执行方法之后进行增强
@Around
在该例子中,只有添加了@MethodLog注解的方法才会被增强
一切准备就绪,开启DEBUG之旅,Spring的内部逻辑有点复杂,整个过程需要一点耐心。
通过Debug发现,Rhino的代理对象Processor排在Spring AOP的Processor,意味着Rhino生成的代理对象,会传给Spring AOP的Processor再做一层代理,在Spring AOP生成代理对象的内部逻辑中,有这么一段判断逻辑。
在createProxy方法中,会根据传入的beanClass,即上一个Processor处理过的对象,判断是否有实现接口。
回到Rhino的实现,因为ServiceImpl类没有实现接口,所以内部会采用CGLIB的方式创建代理对象,我们来看下这个对象的接口。
好家伙,默默的给加了一个Factory接口,这样在Spring AOP的处理中就当做有接口的情况进行实现了。这里最大的问题是,最终生成的代理对象是Factory类型的对象,在赋值给ServiceImpl变量时就会抛异常了。
解决问题
终于找到了问题的所在,那么改如何解决呢?
无法修改Spring AOP的逻辑,但是可以控制Rhino的逻辑,只需要把Rhino的Processor移到Spring AOP的Processor之后,这样就可以在Rhino的Proccessor中处理经过Spring AOP的代理过的beanClass对象了,有效的避免这个问题。
如何有效调整Processor的处理顺序?
通过分析发现Spring AOP的内部实现基于Ordered接口,而Rhino的实现是基于 PriorityOrdered
接口,而且处理器在初始化完成后会进行排序,实现 PriorityOrdered
接口的会放到前面,相同类型的再根据设置的order进行排序。很显然,Rhino的Processor被放在前面,找到了问题,解决方法也很简单,Rhino也换成Order接口,并且order设置成 LOWEST_PRECEDENCE
,即排在最后面。
结论
经过上面的调整之前,这个问题确实被有效的解决了,以前遇到Spring的异常,都是一脸懵逼。所以,遇到类似的问题,最好通过DEBUG源码去发现问题,并解决问题,这样可以有效的防止后续的继续挖坑。
以上所述就是小编给大家介绍的《一次动态代理的填坑之旅》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
信息论、推理与学习算法
麦凯 / 高等教育出版社 / 2006-7 / 59.00元
本书是英国剑桥大学卡文迪许实验室的著名学者David J.C.MacKay博士总结多年教学经验和科研成果,于2003年推出的一部力作。本书作者不仅透彻地论述了传统信息论的内容和最新编码算法,而且以高度的学科驾驭能力,匠心独具地在一个统一框架下讨论了贝叶斯数据建模、蒙特卡罗方法、聚类算法、神经网络等属于机器学习和推理领域的主题,从而很好地将诸多学科的技术内涵融会贯通。本书注重理论与实际的结合,内容组......一起来看看 《信息论、推理与学习算法》 这本书的介绍吧!