研发效能 记录一次还算优雅代码设计

京东云开发者 · 2023年02月09日 · 2339 次阅读

作者:京东零售 常文标

商卡聚合服务是一个小巧的 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());
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册