乐趣区

关于后端:记录一次还算优雅的代码设计

作者:京东批发 常文标

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

@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());
退出移动版