基于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过检项
基于AOP的优惠券发送异常哨兵监控的更多相关文章
- 基于AOP的MVC拦截异常让代码更优美
与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’.回过头来,‘漂 ...
- 基于Spring AOP实现对外接口的耗时监控
AOP是Spring的核心,Spring不但自身对多种框架的集成是基于AOP,并且以非常方便的形式暴露给普通使用者.以前用AOP不多,主要是因为它以横截面的方式插入到主流程中,担心导致主流程代码不够清 ...
- Logstash+ Kafka基于AOP 实时同步日志到es
Logstash是一个开源数据收集引擎,具有实时管道功能.Logstash可以动态地将来自不同数据源的数据统一起来,并将数据标准化到你所选择的目的地,logstash丰富的插件(logstash-in ...
- Spring基于AOP的事务管理
Spring基于AOP的事务管理 事务 事务是一系列动作,这一系列动作综合在一起组成一个完整的工作单元,如果有任何一个动作执行失败,那么事务 ...
- Java实战之03Spring-05Spring中的事务控制(基于AOP)
五.Spring中的事务控制(基于AOP) 1.Spring中事务有关的接口 1.1.明确: JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案 1. ...
- 从壹开始前后端分离 40 || 完美基于AOP的接口性能分析
旁白音:本文是不定时更新的.net core,当前主线任务的Nuxt+VueAdmin教程的 nuxt.js 之 tibug项目已上线,大家可以玩一玩:http://123.206.33.109:70 ...
- Spring AOP声明式事务异常回滚(转)
转:http://hi.baidu.com/iduany/item/20f8f8ed24e1dec5bbf37df7 Spring AOP声明式事务异常回滚 近日测试用例,发现这样一个现象:在业务代码 ...
- 邮件发送异常, [Errno 110] Connection timed out
邮件发送异常, [Errno 110] Connection timed out SMTP 服务地址(华东 1): smtpdm.aliyun.com SMTP 服务地址(新加坡):smtpdm-a ...
- 配置哨兵监控Redis运行情况
Redis的主从架构,如果master发现故障了,还得手动将slave切换成master继续服务,手动的方式容易造成失误,导致数据丢失,那Redis有没有一种机制可以在master和slave进行监控 ...
随机推荐
- 缓存List并写入文件持久化
LIfe is half spent before we know what is it. 缓存List并写入文件持久化 需要缓存一个List集合,比如缓存一个输入框中用户之前输入过的内容,下次当用户 ...
- IO(字节流、字符流)
第1章 字节流 在前面的学习过程中,我们一直都是在操作文件或者文件夹,并没有给文件中写任何数据.现在我们就要开始给文件中写数据,或者读取文件中的数据. 1.1 字节输出流OutputStream ...
- CF1157D N Problems During K Days
思路: 在1, 2, 3, ... , k的基础上贪心构造. 实现: #include <bits/stdc++.h> using namespace std; typedef long ...
- iOS VIPER架构(二)
第一篇文章对VIPER进行了简单的介绍,这篇文章将从VIPER的源头开始,比较现有的几种VIPER实现,对VIPER进行进一步的职责剖析,并对各种细节实现问题进行挖掘和探讨.最后给出两个完整的VIPE ...
- 【javascript类库】zepto和jquery的md5加密插件
[javascript类库]zepto和jquery的md5加密插件 相信很多人对jQuery并不陌生,这款封装良好的插件被很多开发者使用. zepto可以说是jQuery在移动端的替代产品,它比jQ ...
- TCP/IP协议分析含义与功能
TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即网络接口层.IP层.传输层和应用层.TCP/IP是一组专业化协议,包括IP.TCP.UDP.ARP.ICMP以及其它的一些被称为子协 ...
- String | StringBuffer | StringBuilder 比较
2016的第一天,我决定写一篇博客来纪念这一天,希望一年好运吧. String|StringBuffer|StringBuilder这三者在我们学习JAVASE核心API的时候常常出来,而且大多数入门 ...
- javaSe-字符型和布尔型
其实java数据类型一节就可以全部写完了,为什么还需要字符型和布尔型呢,原因是这俩个都很重要: 字符型用char表示,字符分三种: 普通字符:char a = 'a',普通字符表示一个普通的字符,没有 ...
- javaSe数据类型
在学完了java程序的结构以及注释后呢按照一般的教程我们应该学点什么呢? 没错就是变量和数据类型[其实我的内心是拒绝的,又是无聊的一大堆,不仅无聊,还得掌握] 好了首先介绍什么是变量: 变量:变量 ...
- 使用EventLog组件读写事件日志
实现效果: 知识运用: Eventlog类的SourceExists方法 //确定指定的事件源是否已在本地计算机注册 public static bool SourceExists(string s ...