《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 每日优鲜 「陈国民」欢迎转载,保留摘要,谢谢!

本文主要介绍每日优鲜五年来交易平台实践的心路历程

一、前言

1.1 每日优鲜简介

每日优鲜定位于自营全品类线上综合超市,成立于2014年11月,致力于让每个家庭买得省心,吃的放心。目前,每日优鲜已经完成水果、蔬菜、肉蛋、水产、乳品、零食、速食、饮品、轻食、粮油、日百等全品类布局,通过首创的生鲜到家“前置仓”模式,在中国16个城市为数千万家庭提供“3000款商品,最快30分钟送达”的服务。凭借着上游供应链和智慧连锁核心能力上的长期投入,每日优鲜业务规模已超过百亿。2020年7月,每日优鲜完成由中金资本领投的4.95亿美元融资,在生鲜电商细分领域,市场份额、营收增长、用户规模以及盈利能力均保持行业第一。

1.2 交易平台简介

跟大多数互联网电商一样,随着公司业务的发展,每日优鲜也陆续拓展出了多业务线(极速达、云超、厂家直送、便利购、菜市场等)。各业务线发展初期,为了快速跑通MVP,组建临时团队、从0开始搭建系统,但这就导致烟囱式系统林立、数据相互割裂,不仅成本高企,也为业务整合带来困难,技术架构从烟囱式系统走向平台化架构成为必然选择;对交易系统来说,各业务形态的订单数据模型差异不大、交易业务流程类似,构架一套交易平台来支撑多业务形态的交易业务具有可行性,因此“每日优鲜交易平台”应运而生。目前每日优鲜交易平台拥有数十亿的海量订单数据、能够稳定地支撑公司数十种业务形态、也能够在3天内高效支持新业务的接入。

二、背景

2.1 没有交易平台时面临的困难是什么?

2.1.1 成本高企

各业务团队都有交易系统,重复造轮子,造成运维成本、研发成本的浪费。

2.1.2 数据割裂

各业务团队的订单数据的割裂存储、数据模型的不统一,造成依赖于订单数据的大数据、财务、客服等服务为了适配不同数据模型,冗余了大量的适配逻辑和兼容逻辑,增加了这些系统的复杂度;复杂度的增加,不仅仅是沟通成本、研发成本的增加,也降低了系统稳定性。

2.1.3 业务迭代需求响应慢

交易系统功能定制化,抽象性、扩展性、复用性都较差,需求响应慢,影响业务迭代效率。

2.1.4 创新业务试错成本高

创新项目不能利用现有交易系统,只能组建临时团队,搭建新的交易系统,拉高了试错成本。而较高的试错成本,会阻碍创新项目启动的机会,延误商机。

2.2 交易平台要解决的核心问题是什么?

2.2.1 什么是交易平台?

交易平台是支持多业务形态下的正向、逆向交易流程的一套软件系统。它具有较高的数据模型抽象性、业务功能抽象性,通过对基础功能(能力域)的配置化流程编排,可支持差异化的业务流程,也可快速支持新业务;能够隔离业务间的相互影响,保障多业务平稳运行。

2.2.2 交易平台要解决的核心问题

2.2.2.1 效率问题

如何快速支持业务需求?说到底就是如何抽象数据模型和业务流程,提高系统的扩展性和复用性,包括如下几个面向:

  • 不同业务形态的数据模型的差异如何存储、传递?
  • 不同业务形态的功能的差异如何处理?
  • 不同业务形态、不同类型的订单,业务流程差异如何支持?
  • 如何定义和管理不同业务形态、不同订单类型?

2.2.2.2 稳定问题

如何保障系统平稳运行?包括系统稳定性和业务正确性,具体面向如下:

  • 多业务在一套系统运行,存在相互影响的可能,如何避免?
  • 出现问题如何快速定位?如何确定是哪个业务出了问题?
  • 业务的正确性如何保障?如何快速主动发现问题?有问题的数据如何快速修复?

2.2.2.3 性能问题

作为交易系统,管理海量正向数据、逆向数据的读写,如何满足C端、B端的读写性能要求?具体面向如下:

  • 海量数据怎么存储才能保证读写性能?是否要分库分表?
  • 多维度的复杂查询如何支持?在分库分表情况下,如何支持复杂查询?
  • 读写流量如何避免相互影响?
  • C端、B端流量如何避免相互影响?

2.3 交易平台的价值

2.3.1 降低成本

收敛各业务团队的烟囱式交易系统,降低运维成本、研发成本。

2.3.2 提高效率

高扩展性、高复用性,对内对外大幅提高研发效率。交易平台自身对接新业务需求仅需3天;数据模型的统一,降低大数据、财务等外部关联服务的复杂度,提高的这些关联服务支持新需求的效率。

2.3.3 业务赋能

创新项目可以低成本跑通 MVP,降低试错成本;体现技术驱动业务发展的能力。

2.3.4 标杆系统

交易平台率先完成平台化,对标行业领先;随着交易平台的价值的逐步体现,在公司内部树立标杆形象,促进达成建设平台化架构的共识,引领兄弟部门平台化建设的步伐;最终形成业务中台化,打造全链路的高效、稳定的业务赋能能力。

三、技术实现

3.1 业务架构

交易平台承接各业务前台的交易应用的请求,依赖商品、营销、支付等基础服务,处理各业务形态的正向交易、逆向交易,具体业务包括正向交易、逆向交易、履约分发、订单中心、数据处理、运营工具、公共组件等。

图片

3.2 系统架构

按照功能,交易平台内部系统划分为4层,包括存储层、数据处理层、业务层和应用层。其中业务层按照订单生命周期划分为生单模块、支付模块、履约模块、逆向模块以及查询模块。

图片

3.3 技术保障

3.3.1 差异管理方案

3.3.1.1 业务身份定义

作为平台化架构,定义业务身份是首要任务。业务身份的定义,是用于区分不同业务、不同品类的差异功能,是业务管理单元;需要定义一套合理且具有扩展性的业务身份方便业务管理。基于业务场景,我们使用3个维度定义业务身份:

  • 业务线:区分不同运营团队所运营的业务,比如主商城、便利购、开放平台等
  • 渠道:同一个业务线,有不同的流量渠道,比如便利购业务下有普通柜、智能柜等
  • 订单类型:根据商品类型、履约形态的不同,定义不同的订单类型,比如极速达订单、云超订单等

图片

3.3.2 扩展性方案

3.3.2.1 数据模型扩展

订单数据模型,除了通用的买家信息、卖家信息、商品信息、支付信息、买家履约信息(支付信息)、卖家履约信息等基本信息之外,不同业务形态的订单还有差异化的数据需要存储和传递。我们的解决方案是通用信息结构化存储+差异信息JSON存储,具体如下:

  • 通用信息:通用信息结构化存储

  • 差异信息:服务间传递、存储用JSON格式, 后续处理由业务自定义逻辑处理,与通用逻辑解耦

  • 金额类信息:预期到有频繁的扩展需求,金额类型和金额值采用KV结构化存储,提高可扩展性

  • 图片

3.3.2.2 业务功能扩展

支持多业务问题本质上是业务功能如何抽象的问题,而差异逻辑处理则是在抽象通用基础能力的基础上,如何在部分处理环节支持差异性逻辑。我们采用的是抽象通用能力+预留扩展点的方式,具体如下:

  • 抽象通用能力:调研业务场景,对功能进行统一抽象,形成业务通用能力域,方便多业务复用

  • 预留扩展点:在通用能力域中针对可能存在差异地方预留扩展点,方便差异逻辑的实现;差异逻辑可通过配置化解决,也可用差异化代码实现。

图片

3.3.2.3 业务流程扩展

差异化流程的本质是串联能力节点形成业务流程,信息可在能力节点之间传递。我们采用流程编排+标准上下文的方式实现,具体如下:

  • 流程编排:我们团队自研流程引擎ark-maze组件,进行业务流程编排
  • 标准上下文:流程节点之间数据通过上下文传递,定义业务流程通用的上下文结构

流程引擎ark-maze组件,以“责任链”模式来组织复杂处理流程的执行过程,保障服务节点能够按照顺序执行,并且能够传递执行结果;发生异常可逆序进行回滚操作;支持节点的串行、并行执行。ark-maze组件架构图如下:

图片

3.3.3 稳定性方案

3.3.3.1 服务隔离

一套系统支持多个业务形态,导致业务间相互影响的原因如下:

  • 应用资源相互影响:某业务占用资源过多,影响其它业务

  • 存储相互影响:共享存储,导致业务间相互影响 我们采用多级别隔离机制,具体如下:

  • 线程池隔离:我们的分布式服务框架采用的是阿里开源框架dubbo,不同业务使用不同的dubbo的group,达到dubbo线程池的隔离,避免多业务争抢线程池的情况。

  • 服务器隔离:服务器按业务线分组独立部署,不同业务请求路由到相应的服务器集群。基于业务线配置不同profile文件,服务器按profile分组部署,不同的proflie注册不同group的dubbo服务,dubbo消费者使用不同group的服务。

  • 存储隔离:使用分库策略,不同业务数据路由到不同数据库集群。权衡业务流程规模、系统复杂度、运维成本,目前每日优鲜在【线程池隔离】级别。

图片

3.3.3.2 分业务监控

多业务监控本质上是在原有监控指标增加业务维度即可,相关监控指标包括:应用指标:异常日志、调用量等;业务指标:下单数、支付数、取消数等;

图片

3.3.3.3 业务保障系统

如何保障多业务正常运行?其本质就是如何快速发现问题,并且具有一定的自动修复的能力。我们搭建了业务保障平台,实时地发现业务数据的一致性和正确性问题,某些异常场景能做到自动修复,如果不能自动修复,第一时间报警给相关负责人。业务保障平台通过事件触发执行校验规则,规则支持自定义脚本和自定义api接口。

3.3.4 高性能方案

3.3.4.1 存储高性能

作为交易系统,存储海量订单数据,对外提供高可用、高性能的读写服务是必须要支持的。为了保障高可用、高性能,我们采用如下方案:

  • 分库分表:为了解决单库单表的存储上限和并发处理上限的问题,我们在设计初期以当时预估的5年内的订单规模,设计为N个分库N个分表。以订单号后N位取模作为分库分表的路由规则。
  • 读写分离:Mysql集群采用1主+1备+N从的高可用架构。写流量走主库,主库配置备库,保障高可用;读流量走从库,有实时性要求的读流量也走主库,由上游应用根据业务场景来决定;B端、C端读流量分离,避免B端流量影响C端用户流量。
  • ES搜索引擎:Mysql采用分库分表的方案,导致Mysql不能提供多表联合查询和多维度的复杂查询,采用其他搜索引擎提供复杂查询能力成为必然。我们采用分布式文档数据库Elasticsearch搜索引擎。监听Mysql数据库的binlog实时同步增量异构数据到Elasticsearch。为了保障ES集群的高可用,搭建2套ES集群作为互备,也能做到B端、C端流量的分离;Mysql数据同步ES有秒级的延迟,有实时性要求的C端流量不走ES,只能走Mysql主库查询,例如:待支付订单列表页查询Mysql,而其它实时性要求不高的或者用户无需感知实时状态的订单列表页(如:待配送、配送中的订单列表页)查询ES。

3.3.4.2 流程异步化

非核心业务处理如果混杂在核心链路中,影响核心流程的性能和稳定性。非核心处理剥离出来,异步化处理,提高整体的性能和稳定性。以订单取消流程为例:

图片

3.3.5 一致性方案

3.3.5.1 最终一致性事务框架miss-tfc

通常在核心业务流程中存在发送MQ,发起RPC调用第三方、刷新ES等场景,如:在生单流程中,需要在保存数据库后发送一条MQ消息。而以上这些场景是需要保证一定要执行的。当机器遇到宕机这类故障后,这些任务则有几率不会被执行。为了解决以上问题,通常的做法是每个业务写一个任务表定期执行;但是这样做带来的问题是巨多的任务表,维护和操作也不方便,重复的操作逻辑也会散落在各个业务处理中。我们团队miss-tfc框架则是为了解决以上问题而生的,它会把需要执行的任务持久化到数据库中,然后不断的重试补偿动作,直到任务处理成功。架构原理:通过拦截器配置指定类的指定方法保存起来镜像存储到任务表,并且与当前业务的写库事务绑定在一个事务中。等事务提交后,可以选择性的配置实时当前同步线程做、实时异步线程做、异步调度做等。

3.3.6 灰度方案

3.3.6.1 增量数据双向同步

交易平台收敛原来的各业务团队的烟囱式老交易系统,涉及读写流量的平滑切换处理,我们采用【灰度写流量->写全量->灰度读流量->读全量->老系统下线 】的方案。在这个过程中,考虑到用户列表等业务场景需要全量订单数据的情况,新老数据库都需要持有全量订单数据,我们用【增量数据双向同步】机制来保障。增量数据双向同步:交易平台的增量数据同步到老订单数据库,老订单系统的增量数据同步到交易平台数据库,使新老订单库同时保有全量订单数据。

图片

四、踩过的坑

4.1 过度设计

4.1.1 过早的服务器隔离

在各业务形态流量规模还没有到影响稳定性的情况下,过早的以业务线维度部署服务器达到所谓的服务器隔离的级别,导致了测试机器、生产机器的成本浪费,同时也增加了测试的复杂度和成本。

  • 解决方案:隔离级别从服务器隔离 降为 线程池隔离

4.2 依赖组件

4.2.1 核心链路依赖开源组件

订单状态的流转,我们采用了消息驱动的方案;为了与业务代码解耦,消息是用阿里开源的数据库同步系统otter监听数据库订单表的binlog来发出的。支付成功后的履约分发等核心链路也依赖这种消息驱动;某次网络问题导致otter消息阻塞,造成近30分钟的订单不能履约的故障。开源组件本身的设计不足、或者对开源组件没有完全掌握,遇到问题不能快速定位、快速解决,都是稳定性不可控的因素。

  • 解决方案:痛定思痛,我们从核心链路彻底去掉开源组件otter,自研一致性组件miss-tfc,详细参考【3.3.5.1 最终一致性事务框架miss-tfc】;针对自研组件,我们有更大的热情、更强的自主性进行长期地维护、推广和完善。

五、未来展望

经过了2年多的持续建设,目前交易平台能够高效、稳定地支撑公司业务的快速发展,但还是存在一些功能需要持续完善。

5.1 业务参数动态配置化

目前业务参数配置较为混乱,在系统配置文件(yml文件)、分布式配置中心(missconf)都有,静态、动态的区分混乱,导致管理成本高。后续规划建设交易平台配置中心,统一、动态管理业务配置。

5.2 流程编排可视化

搭建业务流程管理中心,能力地图、流程编排可视化展示,流程编排可动态生效。

5.3 订单全链路监控

建立基于订单生命周期全链路的数据流,实时跟踪订单各个环节的处理状态,并将作业数据可视化;有效防止订单流转失败、漏单、超卖等其他系统异常和业务异常情况;掌握各系统作业效率,帮助内部效率提升。

六、总结

在每日优鲜交易平台建设过程中,我们借鉴行业最佳实践,也踩过大大小小的坑,探索并形成了自己的平台化架构建设的方法论,希望对大家有一些启发;同时也欢迎大家来件一起探讨。

作者简介

  • 陈国民,2017年加入每日优鲜,目前主要从事交易平台建设的相关工作。邮箱:chengm@missfresh.cn
  • 感谢苏磊(公 众号:再见伐木机)、张祖瑞对本文的指正。
  • 感谢晋冰、陈思彤对本文的指导。
文章目录
  1. 1. 一、前言
    1. 1.1. 1.1 每日优鲜简介
    2. 1.2. 1.2 交易平台简介
  2. 2. 二、背景
    1. 2.1. 2.1 没有交易平台时面临的困难是什么?
      1. 2.1.1. 2.1.1 成本高企
      2. 2.1.2. 2.1.2 数据割裂
      3. 2.1.3. 2.1.3 业务迭代需求响应慢
      4. 2.1.4. 2.1.4 创新业务试错成本高
    2. 2.2. 2.2 交易平台要解决的核心问题是什么?
      1. 2.2.1. 2.2.1 什么是交易平台?
      2. 2.2.2. 2.2.2 交易平台要解决的核心问题
        1. 2.2.2.1. 2.2.2.1 效率问题
        2. 2.2.2.2. 2.2.2.2 稳定问题
        3. 2.2.2.3. 2.2.2.3 性能问题
    3. 2.3. 2.3 交易平台的价值
      1. 2.3.1. 2.3.1 降低成本
      2. 2.3.2. 2.3.2 提高效率
      3. 2.3.3. 2.3.3 业务赋能
      4. 2.3.4. 2.3.4 标杆系统
  3. 3. 三、技术实现
    1. 3.1. 3.1 业务架构
    2. 3.2. 3.2 系统架构
    3. 3.3. 3.3 技术保障
      1. 3.3.1. 3.3.1 差异管理方案
        1. 3.3.1.1. 3.3.1.1 业务身份定义
      2. 3.3.2. 3.3.2 扩展性方案
        1. 3.3.2.1. 3.3.2.1 数据模型扩展
        2. 3.3.2.2. 3.3.2.2 业务功能扩展
        3. 3.3.2.3. 3.3.2.3 业务流程扩展
      3. 3.3.3. 3.3.3 稳定性方案
        1. 3.3.3.1. 3.3.3.1 服务隔离
        2. 3.3.3.2. 3.3.3.2 分业务监控
        3. 3.3.3.3. 3.3.3.3 业务保障系统
      4. 3.3.4. 3.3.4 高性能方案
        1. 3.3.4.1. 3.3.4.1 存储高性能
        2. 3.3.4.2. 3.3.4.2 流程异步化
      5. 3.3.5. 3.3.5 一致性方案
        1. 3.3.5.1. 3.3.5.1 最终一致性事务框架miss-tfc
      6. 3.3.6. 3.3.6 灰度方案
        1. 3.3.6.1. 3.3.6.1 增量数据双向同步
  4. 4. 四、踩过的坑
    1. 4.1. 4.1 过度设计
      1. 4.1.1. 4.1.1 过早的服务器隔离
    2. 4.2. 4.2 依赖组件
      1. 4.2.1. 4.2.1 核心链路依赖开源组件
  5. 5. 五、未来展望
    1. 5.1. 5.1 业务参数动态配置化
    2. 5.2. 5.2 流程编排可视化
    3. 5.3. 5.3 订单全链路监控
  6. 6. 六、总结