• 基于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+款云产品! 

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


    相关文章:
    【推荐】 网易云易盾朱星星:最容易被驳回的10大APP过检项

  • 相关阅读:
    Selenium WebDriver-actionchain模拟键盘左键长按
    Selenium WebDriver-actionchain模拟鼠标右键操作
    CSS3 box-sizing:content-box | border-box
    gulp 使用指南
    MAC 使用指南
    移动端布局Rem
    JS面向对象编程
    webstorm 使用指南
    js 获取 touch length
    css 内容居中
  • 原文地址:https://www.cnblogs.com/163yun/p/9722619.html
Copyright © 2020-2023  润新知