得物商家下载中心优化实践
《得物商家下载中心优化实践》共 0.5 万字,通篇围绕 需求、吞吐、架构、成本 展开,归在「后端与系统架构」这一层。
适合给 AI 系统搭底座、要管得住的人
目录骨架(4 节)
- 同步执行 - 通用配置处理
- 调度执行 - 业务针对调整
- 业务隔离
- 任务调度
分享内容
- 集中式:流程扩展
- 平台化:配置注册
- 本地化:任务上报
一、背景
在电商平台上,二八定律尤为明显,20% 的高价值商家往往创造了 80% 以上的销售额。而这些商家通常拥有大量的订单、商品、出价等管理需求,推动了他们对批量操作功能的迫切需求。批量操作能够帮助这些商家高效地处理商品信息、库存和订单管理,显著提升运营效率。
通过批量操作,商户可以在短时间内对多个产品进行修改,如统一调价、调整促销策略等,从而快速响应市场变化,优化用户体验。此外,批量操作还降低了人工出错的风险,确保了数据的一致性,让商家能够更加专注于战略规划和客户关系管理。总之,对于这些商户而言,批量操作不仅是提升管理效率的关键工具,也是实现业务增长的重要保障。
在得物的商家后台中,商家的所有批量操作都承载在批处理系统(批处理中心),商家可以通过在功能页面操作批量导入或是批量导出来完成批量操作。操作后的文件将展示在下载中心。
此外,批处理中心还维护了交易后台、客服、汇金、门店等多个域的批量操作任务。截止目前,批处理中心维护了十个域的上千种批量任务,日均处理数万个相关任务,数亿条相关数据。
随着得物体量的不断上升,批处理系统也在不断演进。简单来说,批处理系统经历了从分散到耦合、再到集中与隔离的多个发展阶段。接下来,我们以批处理的开发者小王的视角,介绍批处理系统的这三种设计,并探讨它们各自的特点与适用场景。
二、集中式:流程扩展
假设小王接到了一个批量操作的需求,要求在商家后台能进行批量出价。需求很简单,小王仅用时两天半就完成了基本流程的搭建。

业务上线后,商家反馈非常好,产品要求立刻上线一个批量修改出价的需求。于是小王照葫芦画瓢写又写了一条流程。

两条几乎一样的流程,有代码洁癖的小王表示无法接受。经过分析后,小王发现,不管是什么导入流程,有些步骤总是固定的,因此决定代码复用。

代码复用后,出价和修改之间只有格式校验和业务逻辑不同。其余的文件下载、内容解析、结果保存和上传均使用相同的节点。既然各个业务之间的差异主要集中在数据处理,小王决定直接将其开成扩展点。不同的业务场景只需要实现各自的数据处理扩展,就能无缝接入批处理流程。业务扩展的示意图如下:

在具体实现的时候,小王在代码里面通过业务身份来进行扩展点的选择,可以建立一个相关的工厂类进行。
@Component
public class BpcProcessHandlerFactory {
@Autowired
private ApplicationContext applicationContext;
private static ConcurrentHashMap<String, BpcProcessDefine> templateMap = new ConcurrentHashMap<>();
@PostConstruct
private void init() {
Map<String, ImportService> importServiceMap = applicationContext.getBeansOfType(ImportService.class);
for (ImportService importService : importServiceMap.values()) {
initImportService(importService);
}
}
private void initImportService(ImportService importService) {
// ...
}
public BpcProcessHandler getBpcProcessHandler(String templateCode) {
if (StringUtils.isBlank(templateCode)) {
return null;
}
if(!templateMap.containsKey(templateCode)) {
return null;
}
return templateMap.get(templateCode).newProcessHandler();
}
}
对于导入的任务处理,简化的代码流程如下:
@Service
public class BpcProcessService {
@Autowired
private BpcProcessHandlerFactory bpcProcessHandlerFactory;
public String doBpcProcess(BpcProcessReq req) throws BpcProcessException {
// 获取扩展点
BpcProcessHandler bpcProcessHandler = bpcProcessHandlerFactory.getBpcProcessHandler(req.getTaskTemplateCode());
if (bpcProcessHandler == null) {
throw new BpcProcessException("找不到模版定义");
}
// 1. 创建任务
createTask();
// 2. 文件下载 && 文件保存
downloadFromOss();
// 3. 数据解析
int loopCnt = 0;
int maxLoopCnt = bpcProcessHandler.getMaxLoopCnt();
while(loopCnt++ < maxLoopCnt) {
// 调用扩展点处理
bpcProcessHandler.process();
// 更新任务
updateTaskProcess();
}
// 更新任务
updateTaskStatus();
return taskId;
}
}