zoukankan      html  css  js  c++  java
  • 基于AOP的优惠券发送异常哨兵监控


    本文来自网易云社区

    作者:王贝


     最近总是发现支付发红包优惠券发完的情况,但是发现的比较迟缓,于是乎,想加一个哨兵监控,统计了一下,组内不少需求都有发送优惠券的行为,也是经常遇到发送异常的情况,所以,想针对优惠券发送封装一个公共的方法进行调用,下面是封装的公共方法:

    public CouponResponse<BatchDispatchResult> sendCoupon(List<String> reedcodeList,String accountId,AntiInfoVO antiInfoVO){
        //发券dubbo接口
        CouponResponse<BatchDispatchResult> couponResponseVo = couponComposeFacade.batchDispatchCouponByRedeemCodeList(reedcodeList, accountId, antiInfoVO, 1);
        //哨兵监控
        if(couponResponseVo != null && couponResponseVo.isNotSuccess()){
            StatsTool.onIntegerKey1Value1Stats("couponSendFail", 1);
        }
        return couponResponseVo;
    }

    方法很简单,就是封装一下,加个监控,但是发现代码中调用发券dubbo接口的地方很多,要改不少地方,于是想起来面向切面编程AOP,这里先简单介绍一下AOP(引用自http://www.cnblogs.com/hongwz/p/5764917.html)。

    AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系。对于其他类型的代码,如安全性、异常处理和接口调用监控也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

    AOP技术恰恰相反,它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。  

    使用"横切"技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。  

    于是乎,说干就干,很快代码出炉  

    @Aspect
    public class CouponWarnAspect {
    
        /**
         * 单发
         */
        @Pointcut(value = "execution(com.netease.kaola.compose.coupon.vo.CouponResponse<java.util.List<com.netease.kaola.compose.coupon.vo.CouponVO>>" +
                " com.netease.kaola.compose.coupon.provider.CouponComposeFacade.*(..))")
        public void couponWarnPoint(){}
    
        /**
         * 批量发送
         */
        @Pointcut(value = "execution(com.netease.kaola.compose.coupon.vo.CouponResponse<com.netease.kaola.compose.coupon.vo.BatchDispatchResult>" +
                " com.netease.kaola.compose.coupon.provider.CouponComposeFacade.*(..))")
        public void couponBatchWarnPoint(){}
    
        @Around(value = "couponWarnPoint()")
        public CouponResponse<List<CouponVO>> couponWarn(ProceedingJoinPoint pjp) throws Throwable {
            try {
                CouponResponse<List<CouponVO>> couponResponse = (CouponResponse) pjp.proceed();
                if(couponResponse != null && couponResponse.isNotSuccess()){
                    StatsTool.onIntegerKey1Value1Stats("couponSendFail",1);
                }
                return couponResponse;
            } catch (Throwable throwable) {
                StatsTool.onIntegerKey1Value1Stats("couponSendError",1);
                throw throwable;
            }
        }
    
        @Around(value = "couponBatchWarnPoint()")
        public CouponResponse<BatchDispatchResult> couponBatchWarn(ProceedingJoinPoint pjp) throws Throwable {
            try {
                CouponResponse<BatchDispatchResult> couponResponse = (CouponResponse) pjp.proceed();
                if(couponResponse != null && couponResponse.isNotSuccess()){
                    StatsTool.onIntegerKey1Value1Stats("couponBatchSendFail",1);
                }
                return couponResponse;
            } catch (Throwable throwable) {
                StatsTool.onIntegerKey1Value1Stats("couponBatchSendError",1);
                throw throwable;
            }
        }
    }

    多么干净利落,这样子就不用通过动原来逻辑来添加哨兵监控了。首先定义pointcut切入点,切入点就是指定一个Adivce将被引发的一系列连接点的集合,这里根据正则匹配定义了两个切入点,第一个切入点匹配的是  com.netease.kaola.compose.coupon.provider.CouponComposeFacade里返回类型是  com.netease.kaola.compose.coupon.vo.CouponResponse<java.util.List<com.netease.kaola.compose.coupon.vo.CouponVO>>的方法,从目前系统dubbo调用看,匹配的是单个发券的方法,第二个切入点匹配的是  com.netease.kaola.compose.coupon.provider.CouponComposeFacade里返回类型是com.netease.kaola.compose.coupon.vo.CouponResponse<com.netease.kaola.compose.coupon.vo.BatchDispatchResult>的方法,从目前系统dubbo调用看,匹配的是批量发券的方法。

    切入点定义好之后,开始在Advice中进行引用,当然,也可以直接在Advice中通过正则匹配指定切入点。Advice( 通知),包括前置(before)、后置(AfterReturning)、异常(AfterThrowing)、环绕(around)等这几类常用的。我们这里用到了环绕,环绕就是对调用的目标方法进行包裹,可以在调用前后做一些我们需要的操作,比如上述,就是在调用目标方法后通过判断返回码和异常来添加哨兵监控。  

    最后通过spring配置和aspect注解创建代理类:  

    	<aop:aspectj-autoproxy proxy-target-/>
    
    	<bean id="couponWarnAspect" ></bean>

    测试一下,优惠券发送失败的情况完美的呈现在哨兵上了:

                 

    通过这次,大概总结了一下,编程不能按部就班,多思考一下,就会发现有很多捷径可走,从而高效率的把系统设计的更具健壮性和可维护性,技术上也得到突破和提升,何乐而不为。


    网易云免费体验馆,0成本体验20+款云产品! 

    更多网易研发、产品、运营经验分享请访问网易云社区


    相关文章:
    【推荐】 网易云 MySQL实例迁移的技术实现
    【推荐】 深情留不住,套路得人心- -聊聊套路那些事儿

  • 相关阅读:
    Mvc+三层(批量添加、删除、修改)
    js中判断复选款是否选中
    EF的优缺点
    Git tricks: Unstaging files
    Using Git Submodules
    English Learning
    wix xslt for adding node
    The breakpoint will not currently be hit. No symbols have been loaded for this document."
    Use XSLT in wix
    mfc110ud.dll not found
  • 原文地址:https://www.cnblogs.com/zyfd/p/9722621.html
Copyright © 2011-2022 走看看