记录一次还算优雅代码设计

作者:京东零售 常文标

商卡聚合服务是一个小巧的rpc应用,功能是统一查询商品的促销、自营包邮、价格信息、区域库存、区域可配送等等利益点或其他信息。本文重点分享商卡聚合服务的代码设计,包括合理的Sirector线程调度(cpu使用率低),和可维护性的设计。 简版代码示例如下: git@github.com:changwenbiao/demosoa.git 

代码使用sirector-core组件并行调度(使用线程并行执行EventHandler的onEvent方法)请求上游rpc接口获取各利益点或其他商品信息。因为请求上游有些通用处理逻辑比如ump监控、调用开关等,所以抽象出一个通用的EventHandler名为AbstractBenefitHandler。具体调用利益点的实现类只需继承AbstractBenefitHandler并重写其抽象方法。

接下来重点讲代码如何节省cpu使用率和易于维护的设计。

1.如何节省cpu

AbstractBenefitHandler提供isSwitchOn方法,用于决定是否使用sirector组件分配线程执行调度EventHandler。相对于分配线程执行全部的EventHandler,判断是否需要调用才分配线程调用的方式可有效减少线程调度从而减少cpu使用率。isSwitchOn方法中可加入cms控制开关逻辑比如使用ducc开关,也可加入根据用户参数判断开关的逻辑,比如查询区域库存需要四级地址,若用户不传四级地址则关闭调用EventHandler(请求上游rpc)。代码实现如下:其中ducc开关在父类中的isSwitchOn中实现,sirector.begin方法接受可变参数列表参数,可将List<AbstractBenefitHandler>转化为AbstractBenefitHandler[]作为入参。

1@Override 2public boolean isSwitchOn() { 3 boolean superSwitchOn = super.isSwitchOn(); 4 if (!superSwitchOn) { 5 return false; 6 } else { 7 //正常为四级地址,如果少于四级则关闭调用 8 String area = seckillBenefitRequest.getSeckillParam().getArea(); 9 return !StringUtils.isBlank(area) && area.contains("_") && area.split("_").length >= 4; 10 } 11}
1List<String> handlerNames = Lists.newArrayList("areaStockHandler", "partitionProductsHandler"); 2List<AbstractBenefitHandler> handlerList = handlerNames.stream() 3.map(handlerName -> applicationContext.getBean(handlerName, AbstractBenefitHandler.class).setBenefitRequestAndBizName(request, "demoAppName")) 4.filter(AbstractBenefitHandler::isSwitchOn).collect(Collectors.toList()); 5Sirector<MiaoShaEvent> sirector = new Sirector<MiaoShaEvent>(bigSeckillEventProcessThreadPool); 6AbstractBenefitHandler[] eventHandlersArr = new AbstractBenefitHandler[handlerList.size()]; 7handlerList.toArray(eventHandlersArr); 8sirector.begin(eventHandlersArr); 9sirector.ready(); 10sirector.publish(new MiaoShaEvent(), 500); //这里开始使用线程并行执行EventHandler的onEvent方法

2. 如何容易维护

减少一些模版代码(如ump监控):所有Handler的实现类的ump监控都写在父类中的onEvent中,父类的onEvent调用子类实现的onEvent0(处理具体利益点rpc请求处理)方法。

短小代码的实现:AbstractBenefitHandler提供fillResponseInfo方法以向“ResponseVO”中填数据,具体填利益点数据的代码则由相应handler实现类处理。因此各个handler填充利益点“ResponseVO”的代码都是短小的,避免了代码写在一起的长代码。单个handler填充利益点数据和批量统一填充利益点数据代码分别如下:

1@Override 2public void fillResponseInfo(List<BftInfo> bftInfoList) { 3 if (MapUtils.isNotEmpty(areaStockMap)) { 4 for (BftInfo result : bftInfoList) { 5 String skuId = result.getBaseInfo().getSkuId(); 6 if (areaStockMap.containsKey(skuId)) { 7 result.getCommonInfo().setAreaStock(areaStockMap.get(skuId)); 8 } 9 } 10 } 11}
handlerList.forEach(h -> h.fillResponseInfo(bftInfoList));

减少一些硬编码:handler实现类配置为原型模式(scope="prototype")的spring bean,通过applicationContext.getBean方法统一获取,避免一些创建(new关键字)具体实现类的代码,若新增利益点调用只需编码AbstractBenefitHandler实现类并配置为spring bean即可。批量获取handler代码如下

1List<String> handlerNames = Lists.newArrayList("areaStockHandler", "partitionProductsHandler"); 2List<AbstractBenefitHandler> handlerList = handlerNames.stream() 3.map(handlerName -> applicationContext.getBean(handlerName, AbstractBenefitHandler.class).setBenefitRequestAndBizName(request, "demoAppName")) 4.filter(AbstractBenefitHandler::isSwitchOn).collect(Collectors.toList());
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

PPDB:今晚老齐直播

【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。

VBox 启动虚拟机失败

在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern

FLV文件格式

1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.