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

京东云开发者
• 阅读 389

作者:京东零售 常文标

商卡聚合服务是一个小巧的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[]作为入参。

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

2. 如何容易维护

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

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

@Override
public void fillResponseInfo(List<BftInfo> bftInfoList) {    
    if (MapUtils.isNotEmpty(areaStockMap)) {        
        for (BftInfo result : bftInfoList) {            
            String skuId = result.getBaseInfo().getSkuId();            
            if (areaStockMap.containsKey(skuId)) {                
                result.getCommonInfo().setAreaStock(areaStockMap.get(skuId));            
            }        
        }    
    }
}
handlerList.forEach(h -> h.fillResponseInfo(bftInfoList));

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

List<String> handlerNames = Lists.newArrayList("areaStockHandler", "partitionProductsHandler");
List<AbstractBenefitHandler> handlerList = handlerNames.stream()        
.map(handlerName -> applicationContext.getBean(handlerName, AbstractBenefitHandler.class).setBenefitRequestAndBizName(request, "demoAppName"))        
.filter(AbstractBenefitHandler::isSwitchOn).collect(Collectors.toList());
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
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
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
3年前
PPDB:今晚老齐直播
【今晚老齐直播】今晚(本周三晚)20:0021:00小白开始“用”飞桨(https://www.oschina.net/action/visit/ad?id1185)由PPDE(飞桨(https://www.oschina.net/action/visit/ad?id1185)开发者专家计划)成员老齐,为深度学习小白指点迷津。
Wesley13 Wesley13
3年前
VBox 启动虚拟机失败
在Vbox(5.0.8版本)启动Ubuntu的虚拟机时,遇到错误信息:NtCreateFile(\\Device\\VBoxDrvStub)failed:0xc000000034STATUS\_OBJECT\_NAME\_NOT\_FOUND(0retries) (rc101)Makesurethekern
Wesley13 Wesley13
3年前
FLV文件格式
1.        FLV文件对齐方式FLV文件以大端对齐方式存放多字节整型。如存放数字无符号16位的数字300(0x012C),那么在FLV文件中存放的顺序是:|0x01|0x2C|。如果是无符号32位数字300(0x0000012C),那么在FLV文件中的存放顺序是:|0x00|0x00|0x00|0x01|0x2C。2.  
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
PHP创建多级树型结构
<!lang:php<?php$areaarray(array('id'1,'pid'0,'name''中国'),array('id'5,'pid'0,'name''美国'),array('id'2,'pid'1,'name''吉林'),array('id'4,'pid'2,'n
Wesley13 Wesley13
3年前
Java日期时间API系列36
  十二时辰,古代劳动人民把一昼夜划分成十二个时段,每一个时段叫一个时辰。二十四小时和十二时辰对照表:时辰时间24时制子时深夜11:00凌晨01:0023:0001:00丑时上午01:00上午03:0001:0003:00寅时上午03:00上午0
Stella981 Stella981
3年前
Jenkins 插件开发之旅:两天内从 idea 到发布(上篇)
本文首发于:Jenkins中文社区(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fjenkinszh.cn)!huashan(https://oscimg.oschina.net/oscnet/f499d5b4f76f20cf0bce2a00af236d10265.jpg)
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_