Design of a Lightweight RPC Service for Product Benefit Aggregation with Low CPU Usage and High Maintainability
This article explains the design of a compact RPC service that aggregates product benefit information, detailing how Sirector thread scheduling reduces CPU consumption and how abstract handlers and Spring prototype beans improve code maintainability and extensibility.
The "Merchant Card Aggregation Service" is a small RPC application that unifies queries for product promotions, free‑shipping, pricing, regional stock, delivery eligibility, and other benefit points. The article focuses on its code design, emphasizing low CPU usage through efficient Sirector thread scheduling and high maintainability via abstract handler architecture.
The service uses the sirector‑core component to execute EventHandler.onEvent methods in parallel threads. Common upstream request logic such as UMP monitoring and feature switches are encapsulated in an abstract class AbstractBenefitHandler . Concrete benefit handlers inherit from this class and only need to implement their specific logic.
How CPU usage is reduced
The isSwitchOn method in AbstractBenefitHandler decides whether a handler should be scheduled. By checking feature switches (e.g., DUCC) and request parameters (e.g., presence of a four‑level address), unnecessary thread creation is avoided, cutting down scheduler overhead and CPU consumption. Example implementation:
@Override
public boolean isSwitchOn() {
boolean superSwitchOn = super.isSwitchOn();
if (!superSwitchOn) {
return false;
} else {
// Only enable when a four‑level address is provided
String area = seckillBenefitRequest.getSeckillParam().getArea();
return !StringUtils.isBlank(area) && area.contains("_") && area.split("_").length >= 4;
}
}Handlers are collected, filtered by isSwitchOn , and then executed by a Sirector<MiaoShaEvent> instance:
List
handlerNames = Lists.newArrayList("areaStockHandler", "partitionProductsHandler");
List
handlerList = handlerNames.stream()
.map(name -> applicationContext.getBean(name, AbstractBenefitHandler.class)
.setBenefitRequestAndBizName(request, "demoAppName"))
.filter(AbstractBenefitHandler::isSwitchOn)
.collect(Collectors.toList());
Sirector
sirector = new Sirector<>(bigSeckillEventProcessThreadPool);
AbstractBenefitHandler[] eventHandlersArr = new AbstractBenefitHandler[handlerList.size()];
handlerList.toArray(eventHandlersArr);
sirector.begin(eventHandlersArr);
sirector.ready();
sirector.publish(new MiaoShaEvent(), 500); // parallel execution of onEventHow maintainability is improved
Common UMP monitoring code resides in the parent class onEvent , which delegates to child‑specific onEvent0 . This reduces repetitive boilerplate across handlers. Each handler implements a short fillResponseInfo method to populate a ResponseVO with its benefit data, keeping individual handler code concise.
@Override
public void fillResponseInfo(List
bftInfoList) {
if (MapUtils.isNotEmpty(areaStockMap)) {
for (BftInfo result : bftInfoList) {
String skuId = result.getBaseInfo().getSkuId();
if (areaStockMap.containsKey(skuId)) {
result.getCommonInfo().setAreaStock(areaStockMap.get(skuId));
}
}
}
}Batch processing is performed with a simple stream call:
handlerList.forEach(h -> h.fillResponseInfo(bftInfoList));Handlers are defined as Spring prototype beans, retrieved via applicationContext.getBean , eliminating hard‑coded new statements. Adding a new benefit point only requires implementing a new AbstractBenefitHandler subclass and registering it as a bean.
Overall, the design achieves low CPU overhead through conditional thread scheduling and promotes maintainability by centralizing common logic, using short handler implementations, and leveraging Spring's prototype bean mechanism.
JD Retail Technology
Official platform of JD Retail Technology, delivering insightful R&D news and a deep look into the lives and work of technologists.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.