项目场景:
由于针对单品的线上抢购导致库存中心出现不可控的宕机危机,出于系统安全等方面考虑,为此需要添加到单品维度的流控功能。
问题描述:
基于此次宕机危机,从中也是有可以分析的地方,通过对日志或监控信息的分析,发现是针对具体商品进行的高频率调用,为此,流控方案从这个方面入手,限制同一维度并发处理量,对限制的处理明细直接封装返回信息。鉴于不同系统的判断及处理标准并不一致,因此对于同一维度的判断及返回信息的处理由项目组具体实现。
@Override
public void run() {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(READ_DATA, bytes, -1, buffer).sendToTarget();
}
实现方案:
实现逻辑基于以下几点考虑
1.当批量请求出现一个超出阀值的记录,则认为该次请求异常,全部封装异常返回
2.基于ConcurrentHashMap构建缓存用以存储接口各明细并发处理量,容器以Key=判断维度,Value=并发处理记录为Entry。
在每次请求进入处理方法时增加,离开处理方法时减少或移除(value=0时)
3.基于SNF-SCM配置,可以在生产环境中及时调整阀值处理
![在这里插入图片描述](https://img-blog.csdnimg.cn/e5cc2c52d75c4e3c97c318feca23832a.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5p625p6E6ICB5YW1,size_20,color_FFFFFF,t_70,g_se,x_16)
使用:
1.依赖Jar包
依赖Jar包为库存项目中使用到的关联Jar包,实际Jar包版本可视项目组具体调整.
Spring-aop-3.1.2.RELEASE.jar+
aop-alliance-1.0.jar
aspectjweaver-1.7.0.jar
cglib-nodep-2.2.2.jar
commons-collections-3.2.1.jar+
commons-lang3-3.1.jar+
snf-scm-client-2.1.0.jar
slf4j-api.16.4.jar+
2.SCM配置
1.SCM申请及其使用
A002-016-统一配置管理平台(SCM)
2.在SCM平台创建属性文件
在scm平台创建属性文件,文件路径无特别要求,属性文件配置如下属性
属性 key | 属性值 | 说明 |
---|---|---|
capacity | 10000 | 缓存容纳请求上限,多于capacity的请求忽略流控功能 |
threshold | 5 | 单个JVM限制的并发处理量 |
powerOn | true/false | 流控功能开关true表启用false表关闭 |
3.本地SCM文件及使用
U002-016–统一配置管理平台(SCM)
3.注册组件
1.实现CacheKeyGenerator接口,该组件用于构建缓存键值,该键值表示流控判断的维度信息。
2.实现ResponseFaker接口,该组件用于模拟返回结果,超过阀值的请求将通过该组件模拟返回结果。
3.新建有且只有一个类继承GenericThrottleCompFactory,且通过@Component等注解交由Spring管理,用于注册上述组件,支持注册多组组件,用于对多个接口进行流控。
4.Spring配置
1.添加切面
2.通过AOP实现代码切入,因此需要在Spring配置文件中增加aop命名空间
<aop:aspectj-autoproxy proxy-target-class=“true” />
5.文件调整
1.在需要流控的服务类上添加@Filter,注解name=缓存名称,用于绑定组件,获取缓存信息等,path=SCM配置文件路径.
2.注解属性说明
name=缓存名称,用于绑定组件,获取监控信息,内容必填
path=SCM配置文件路径,内容必填
requestIndex=请求参数索引,从0开始,用于在多个入参时绑定需要拦截请求参数,默认值为0
当需要拦截的方法存在多个入参时,如
Resp serve(Cond cond,Req reqs),此时需要关注的业务请求为reqs,需要设置requestIndex=1,便于切面获取业务参数
caller=绑定上层调用类,保证Service组件复用的纯净性,默认为“”
由于我们的拦截方法可能存在多个模块的复用功能,但我们仅希望对接口过来的请求进行拦截时,可通过caller参数绑定其调用类
例如com.suning.ebuy.cis.web.controller.ATPCheckController
6.监控功能
2.0.0版本提供主动探测缓存功能,展示信息格式为TimeStamp|HostIP|Interface|key|Threshold,
e.g.
2015-02-13 11:27:36:648|10.19.214.9| PromotionaArticleMgmt-searchSalesShowService | 000000000120128737|20
ThrottleMonitor为线程安全的,若需多次创建,建议创建final对象,以供使用.
ThrottleMonitor#defaultInfos(String cacheName,int topSize)
cacheName为缓存名称
topSize为展示的Top * 条目数
7.案例介绍
1.在SCM平台上创建属性文件(SCM申请及本地配置此处并不介绍,可参考相关页面进行配置)
0.0.8新增功能配置文件中添加
distinct=true
该配置将启用排重功能,即同一个请求中相同KEY的行项目会排重之后进行流控。
2.增加Spring配置
<aop:aspectj-autoproxy proxy-target-class=“true” />
3.继承GenericThrottleCompFactory并注册组件
0.07版本新增功能
使用ExcludeCacheKeyGenerator,实现其中exclude方法,可自定义添加排除功能,该方法返回true时对应的行项目将不进行流控
4.添加注解@Filter绑定缓存及SCM配置
5.测试一把
<?xml version="1.0"?>
<MbfService>
<output1>
<MbfHeader>
<ServiceCode>InventoryInquire</ServiceCode>
<Operation>checkATP</Operation>
<UId>65297457222546a7b3e70f96bddb0068e3fa2280b62142ed</UId>
<ServiceResponse>
<Status>COMPLETE</Status>
</ServiceResponse>
</MbfHeader>
<MbfBody>
<list>
<ATPCheckResp>
<orderNo>9411374476</orderNo>
<orderItemNo>000010</orderItemNo>
<plantCode>D025</plantCode>
<cmmdtyCode>000000000101051959</cmmdtyCode>
<invLocat>0001</invLocat>
<supplierCode>0010045247</supplierCode>
<arrivalDate>2013-09-08 00:00:00.0 CST</arrivalDate>
<arrivalQty>0.000</arrivalQty>
<supplierInvLocat />
<failMsg>系统繁忙</failMsg>
<successFlag>N</successFlag>
</ATPCheckResp>
<ATPCheckResp>
<orderNo>9411374476</orderNo>
<orderItemNo>000010</orderItemNo>
<plantCode>D025</plantCode>
<cmmdtyCode>000000000101051959</cmmdtyCode>
<invLocat>0001</invLocat>
<supplierCode>0010045247</supplierCode>
<arrivalDate>2013-09-08 00:00:00.0 CST</arrivalDate>
<arrivalQty>0.000</arrivalQty>
<supplierInvLocat />
<failMsg>系统繁忙</failMsg>
<successFlag>N</successFlag>
</ATPCheckResp>
</list>
</MbfBody>
</output1>
</MbfService>
6.添加监控信息
private final ThrottleMonitor throttleMonitor = new ThrottleMonitor();
@ResponseBody
@RequestMapping("topInfos.htm")
public String getCacheTopInfo(int size) {
StringBuilder sb = new StringBuilder();
List<String> records = throttleMonitor.defaultInfos("ATPCheck", size);
for (String record : records) {
sb.append(record + "\n");
}
return sb.toString();
}
日志记录:
SCM配置调整后,通过以下内容显示各接口生效内容
LOGGER.info(this.name + “'s powerOn is=” + powerOn);
LOGGER.info(this.name + “'s threshold is=” + threshold);
LOGGER.info(this.name + “'s capacity is=” + capacity);
e.g.ATPCheck‘s powerOn is=true
当发生超出阀值限制时,通过以下内容显示超出记录
LOGGER.info(“接口” + throttle.getName() + “键值” + r + “超出阀值” + throttle.getThreshold());
e.g. 接口ATPCheck键值101002405#10042005#D025#0001#5超出阀值5
LOGGER.warn(info(throttle.getName(), r, throttle.getThreshold()));
输出格式为:
TimeStamp|IpAddress|CacheName|Key|Threshold
8.备注
关于组件构建的一些说明:
GenericThrottleCompFactory非线程安全,作为提供组件的注册容器,因此不允许在初始化后添加新的组件,需要使用者在构造方法内添加需要使用的组件.
关于CacheKeyGenerator接口及ResponseFaker实现的一些小说明,目前支持形如 Resp serve(Req )的服务接口,其中关于Req及Resp支持的结构可以支持Object和List。
由于基于Cglib作为AOP动态代理,因此对于final方法和私有方法无法进行拓展
目前该版本的代码基于SNF-SCM-2.1.0.jar(下称2.1)进行开发测试,但在推广过程中发现部分项目组使用的为SNF-SCM-2.0.0.jar(下称2.0),通过比对两个版本的jar包,2.1将ZNode节点的监听器动作从2.0的Client剥离放入ZNode中,并通过sync()动作处理读取滞后最新更新的可能性.
9.Maven支持
目前在苏宁Maven私服上已上传flowCtrl.jar包
其中包含两个版本flowCtrl和flowCtrl_scm2.0(后者基于snf-scm-client-2.0.0版本)
最新版本
Maven依赖
<dependency>
<groupId>com.suning.cis.component</groupId>
<artifactId>flowCtrl</artifactId>
<version>0.0.8-SNAPSHOT</version>
</dependency>
scm2.0Maven依赖
<dependency>
<groupId>com.suning.cis.component</groupId>
<artifactId>flowCtrl_scm2.0</artifactId>
<version>0.0.8-SNAPSHOT</version>
</dependency>
Jar包上传私服参考maven私服jar包管理
五、后期维护
增加开发监控API,支持针对具体维度,Top * 展示(在0.0.2中已完成)
目前针对不同接口需配置多个SCM配置文件,后期将通过name.property整合归并为一个配置文件,通过name属性绑定各个缓存
目前更多的场景是将过滤动作横切至Service层,这样在Service组件复用中会出现多次横切限流,通过堆栈信息绑定上层调用类在注解绑定的基础上增加流程横切(在0.0.1中已完成)
增加具体明细的业务信息过滤功能(在0.0.7中已完成)
六、更新版本日志
0.0.1:流控基本功能
0.0.2:丰富日志及开发主动探测API
0.0.3:格式化日志输出格式
0.0.4:Fix the BUG Committed By 牛广,当从SCM切换开关true->false时,存在已经加锁的缓存记录未移除(由于开关已经关闭.)(Deprecated)
0.0.5:Fix the BUG Committed BY 何石山,0.0.4版本由于加载顺序问题,导致启动失败,同时废弃0.0.4
0.0.6:调整超过阀值时日志输出格式及级别
0.0.7:添加流控排除功能,自定义满足指定条件不进行流控
0.0.8:添加流控排重功能,可添加配置实现,同一个请求中相同KEY的请求排重之后进行流控