关键词:
作者:vivo 官网商城开发团队 - Cheng Kun、Liu Wei
本文介绍了交易平台的设计理念和关键技术方案,以及实践过程中的思考与挑战。
点击查阅:《vivo 全球商城》系列文章
一、背景
vivo官方商城经过了七年的迭代,从单体架构逐步演进到微服务架构,我们的开发团队沉淀了许多宝贵的技术与经验,对电商领域业务也有相当深刻的理解。
去年初,团队承接了O2O商城的建设任务,还有即将成立的礼品中台,以及官方商城的线上购买线下门店送货需求,都需要搭建底层的商品、交易和库存能力。
为节约研发与运维成本,避免重复造轮子,我们决定采用平台化的思想来搭建底层系统,以通用能力灵活支撑上层业务的个性化需求。
包括交易平台、商品平台、库存平台、营销平台在内的一整套电商平台化系统应运而生。
本文将介绍交易平台的架构设计理念与实践,以及上线后持续迭代过程中的挑战与思考。
二、整体架构
2.1 架构目标
除了高并发、高性能、高可用这三高外,还希望做到:
1.低成本
注重模型与服务的可重用性,灵活支撑各业务的个性化需求,提高开发效率,降低人力成本。
2.高扩展
系统架构简单清晰,应用系统间耦合低,容易水平扩展,业务功能增改方便快捷。
2.2 系统架构
(1)电商平台整体架构中的交易平台
(2)交易平台系统架构
2.3 数据模型
三、关键方案设计
3.1 多租户设计
(1)背景和目标
-
-
交易平台面向多个租户(业务方),需要能够存储大量订单数据,并提供高可用高性能的服务。
-
不同租户的数据量和并发量可能有很大区别,要能根据实际情况灵活分配存储资源。
-
(2)设计方案
-
-
考虑到交易系统OLTP特性和开发人员熟练程度,采用MySQL作为底层存储、ShardingSphere作为分库分表中间件,将用户标识(userId)作为分片键,保证同一个用户的订单落在同一个库中。
-
接入新租户时约定一个租户编码(tenantCode),所有接口都要带上这个参数;租户对数据量和并发量进行评估,分配至少满足未来五年需求的库表数量。
-
租户与库表的映射关系:租户编码 -> 库数量,表数量,起始库编号,起始表编号。
-
通过上面的映射关系,可以为每个租户灵活分配存储资源,数据量很小的租户还能复用已有的库表。
示例一:
新租户接入前已有4库*16表,新租户的订单量少且并发低,直接复用已有的0号库0号表,映射关系是:租户编码-> 1,1,0,0
示例二:
新租户接入前已有4库*16表,新租户的订单量多但并发低,用原有的0号库中新建8张表来存储,映射关系是:租户编码-> 1,8,0,16
示例三:
新租户接入前已有4库16表,新租户的订单量多且并发高,用新的4库8表来存储,映射关系是:租户编码-> 4,8,4,0
用户订单所属库表计算公式
- 库序号 = Hash(userId) / 表数量 % 库数量 + 起始库编号
- 表序号 = Hash(userId) % 表数量 + 起始表编号
可能有小伙伴会问:为什么计算库序号时要先除以表数量?下面的公式会有什么问题?
- 库序号 = Hash(userId) % 库数量 + 起始库编号
- 表序号 = Hash(userId) % 表数量 + 起始表编号
答案是,当库数量和表数量存在公因数时,会存在倾斜问题,先除以表数量就能剔除公因数。
以2库4表为例,对4取模等于1的数,对2取模也一定等于1,因此0号库的1号表中不会有任何数据,同理,0号库的3号表、1号库的0号表、1号库的2号表中都不会有数据。
路由过程如下图所示:
(3)局限性和应对办法
-
全局唯一ID
问题:分库分表后,数据库自增主键不再全局唯一,不能作为订单号来使用。且很多内部系统间的交互接口只有订单号,没有用户标识这个分片键。
方案:如下图所示,参考雪花算法来生成全局唯一订单号,同时将库表编号隐含在其中(两个5bit分别存储库表编号),这样就能在没有用户标识的场景下,从订单号中获取库表编号。
-
全库全表搜索
问题:管理后台需要根据各种筛选条件,分页查询所有满足条件的订单。
方案:将订单数据冗余存储一份到搜索引擎Elasticsearch中,满足各种场景下的快速灵活查询需求。
3.2 状态机设计
(1)背景
-
之前做官方商城时,由于是定制化业务开发,各类型的订单和售后单的状态流转都是写死的,比如常规订单在下单后是待付款,付款后是待发货,发货后是待收货;虚拟商品订单不需要发货,没有待发货状态。
-
现在要做的是平台系统,不可能再为每个业务方做定制化开发,否则会导致频繁改动发版,代码错综冗余。
(2)目标
-
引入订单状态机,能为每个业务方配置多套差异化的订单流程,类似于流程编排。
-
新增订单流程时,尽可能不改动代码,实现状态和操作的可复用性。
(3)方案
-
在管理后台为每个租户维护一系列订单类型,数据转化为JSON格式存储在配置中心,或存储在数据库并同步到本地缓存中。
-
每个订单类型的配置包括:初始订单状态,以及每个状态下允许的操作和操作之后的目标状态。
-
当订单在执行某个动作时,使用订单状态机来修改订单状态。
订单状态机的公式是:StateMachine(E,S —> A , S’),表示订单在事件E的触发下执行动作A,并从原状态S转化为目标状态S’
-
每个订单类型配置完成后,生成数据的结构是
/**
* 订单流程配置
**/
@Data
public class OrderFlowConfig implements Serializable
/**
* 初始订单状态编码
**/
private String initStatus;
/**
* 每个订单状态下,可执行的操作及执行操作后的目标状态
* Map<原状态编码, Map<订单操作类型编码, 目标状态编码>>
*/
private Map<String, Map<String, String>> operations;
-
订单商品行状态机、售后单状态机,也用同样的方式实现
3.3 通用操作触发器
(1)背景
业务中通常都会有这样的延时需求,我们之前往往通过定时任务来扫描处理。
- 下单后多久未支付,自动关闭订单
- 申请退款后商家多久未审核,自动同意申请
- 订单签收后多久未确认收货,自动确认收货
(2)目标
-
业务方有类似的延时需求时,能够有通用的方式轻松实现
(3)方案
设计通用操作触发器,具体步骤为:
-
配置触发器,粒度是状态机的流程类型。
-
创建订单/售后单时或订单状态变化时,如果有满足条件的触发器,发送延迟消息。
-
收到延迟消息后,再次判断执行条件,执行配置的操作。
触发器的配置包括:
-
注册时间:可选订单创建时,或订单状态变化时
-
执行时间:可使用JsonPath表达式选取订单模型中的时间,并可叠加延迟时间
-
注册条件:使用QLExpress配置,满足条件才注册
-
执行条件:使用QLExpress配置,满足条件才执行操作
-
执行的操作和参数
3.4 分布式事务
对交易平台而言,分布式事务是一个经典问题,比如:
-
创建订单时,需要同时扣减库存、占用优惠券,取消订单时则需要进行回退。
-
用户支付成功后,需要通知发货系统给用户发货。
-
用户确认收货后,需要通知积分系统给用户发放购物奖励的积分。
我们是如何保证微服务架构下数据一致性的呢?首先要区分业务场景对一致性的要求。
(1)强一致性场景
比如订单创建和取消时对库存和优惠券系统的调用,如果不能保证强一致,可能导致库存超卖或优惠券重复使用。
对于强一致性场景,我们采用Seata的AT模式来处理,下面的示意图取自seata官方文档。
(2)最终一致性场景
比如支付成功后通知发货系统发货,确认收货后通知积分系统发放积分,只要保证能够通知成功即可,不需要同时成功同时失败。
对于最终一致性场景,我们采用的是本地消息表方案:在本地事务中将要执行的异步操作记录在消息表中,如果执行失败,可以通过定时任务来补偿。
3.5 高可用与安全设计
-
熔断
使用Hystrix组件,对依赖的外部系统添加熔断保护,防止某个系统故障的影响扩大到整个分布式系统中。
-
限流
通过性能测试找出并解决性能瓶颈,掌握系统的吞吐量数据,为限流和熔断的配置提供参考。
-
并发锁
任何订单更新操作之前,会通过数据库行级锁加以限制,防止出现并发更新。
-
幂等性
所有接口均具备幂等性,上游调用我们接口如果出现超时之类的异常,可以放心重试。
-
网络隔离
只有极少数第三方接口可通过外网访问,且都有白名单、数据加密、签名验证等保护,内部系统交互使用内网域名和RPC接口。
-
监控和告警
通过配置日志平台的错误日志报警、调用链的服务分析告警,再加上公司各中间件和基础组件的监控告警功能,让我们能够能够第一时间发现系统异常。
3.6 其他考虑
-
是否用领域驱动设计
考虑到团队非敏捷型组织架构,又缺少领域专家,因此没有采用
-
高峰期性能瓶颈问题
大促和推广期间,特别是爆款抢购时的流量可能会触发限流,导致部分用户被拒之门外。因为无法准确预估流量,难以提前扩容。
可以通过主动降级方案增加并发量,比如同步入库切为异步入库、db查询转为cache查询、只能查到最近半年的订单等。
考虑到业务复杂度和数据量级还处在初期,团队规模也难以支撑,这些设计有远期计划,但暂时还没做。(架构的合适性原则,杀鸡用牛刀,你愿意也行)。
四、总结与展望
我们在设计系统时并没有一味追求前沿技术和思想,面对问题时也不是直接采用业界主流的解决方案,而是根据团队和系统的实际状况来选取最合适的办法。好的系统不是在一开始就被大牛设计出来的,而是随着业务的发展和演进逐渐被迭代出来的。
目前交易平台已上线一年多,接入了三个业务方,系统运行平稳,公司内有交易/商品/库存等需求的新业务,以及存量业务在遇到系统瓶颈需要升级时,都可以复用这块能力。
上游业务方数量的增加和版本的迭代,对平台系统的需求源源不断,平台的功能得到逐渐完善,架构也在不断演进,我们正在将履约模块从交易平台中剥离出来,进一步解耦,为业务持续发展做好储备。
咖啡商城|基于springboot+vue前后端分离咖啡商城系统(代码片段)
...捷短暂,足不出户可一缆天下,一机在手可买遍全球。所以消费者基本上已经被新的消费模式所吸引,也具备了网络消费的应用水平。而对于广大的电商平台来讲,大而全的电商有之,小而美的电商也有自己... 查看详情
咖啡商城|基于springboot+vue前后端分离咖啡商城系统(代码片段)
...捷短暂,足不出户可一缆天下,一机在手可买遍全球。所以消费者基本上已经被新的消费模式所吸引,也具备了网络消费的应用水平。而对于广大的电商平台来讲,大而全的电商有之,小而美的电商也有自己... 查看详情
vivo服务端监控架构设计与实践(代码片段)
...今时代处在信息大爆发的时代,信息借助互联网的潮流在全球自由的流动,产生了各式各样的平台系统和软件系统,越来越多的业务也会导致系统的复杂性。当核心业务出现了问题影响用户体验,开发人员没有及时发现,发现问... 查看详情
打通电商多模式支持的“任督二脉”(代码片段)
...,讲电商平台设计的功夫~背景当今的电商可不仅仅是B2C商城,接下来还会有O2O,往后可能还会有商超、奥莱、二手交易。。。且称之为业务模式~而每个业务模式下还会有预售、竞拍、拼团等不同组合的子模式。可是我商城的商... 查看详情
django计算机毕业设计基于安卓android的移动电商平台系统app-商品购物商城app
项目介绍网络的广泛应用给生活带来了十分的便利。所以把移动电商平台与现在网络相结合,利用python技术建设移动电商平台APP,实现移动电商平台的信息化。则对于进一步提高移动电商平台发展,丰富移动电商平台... 查看详情
15生鲜电商平台-售后模块的设计与架构(代码片段)
说明:任何一个的电商平台都有售后服务系统,那么对于我们这个生鲜的电商平台,售后系统需要思考以下几个维度。 1.买家的需求维度 说明:买家在平台上没找到自己... 查看详情
商城前台sku的展示(代码片段)
...t;html><head><metaname="description"content="必要C2M商城是全球性价比最高的电子商务平台,是全球第一家用户直连制造(CustomerToManufactory)的平台,通过平台建立消费者与品质制造商的 查看详情
别样肉客入驻阿里巴巴旗下天猫商城通过国内最大电商平台扩展零售版图
...消费者带来升级的购买便利中国上海,2022年3月7日—全球植物肉先锋品牌别样肉客(BeyondMeat,Inc.,纳斯达克代码:BYND)今日宣布其品牌旗舰店正式上线阿里巴巴旗下天猫商城,旨在为国内更多消费者提供更大的... 查看详情
21生鲜电商平台-通知模块设计与架构(代码片段)
说明:对于一个生鲜的B2B平台而言,通知对于我们实际的运营而言来讲分为三种方式: 1.消息推送:(采用极光推送) 2.主页弹窗通知。(比如:现在有什么新的活动,有什么新的优... 查看详情
毕业设计基于nodejs开发的电商购物商城系统.rar含源码项目(代码片段)
本框架是基于nodejsexpress框架二次开发的,本框架包含数据库操作示例和注解。适合nodejs入门学习或者nodejs开发人员开发中小型web项目。直接在命令窗口运行:npmstart,就可启动程序!访问地址:http://127.0.0.1:300... 查看详情
8生鲜电商平台-购物车模块的设计与架构(代码片段)
说明:任何一个电商无论是B2C还是B2B都有一个购物车模块,其中最重要的原因就是客户需要的东西放在一起,形成一个购物清单,确认是否有问题,然后再进行下单与付款. 1.购物车数据库设计: 说明:业务需求:&nb... 查看详情
基于springboot电商生鲜购物商城平台设计与实现(含源码+数据库文件)
主要功能:商品分类、商品详情、购物车、订单、用户管理、登录注册、个人中心等功能idea开发工具打开,导入数据库可直接运行登陆注册账号个人中心信息修改更新(个性签名)浏览查看商品信息详情添加商品... 查看详情
14生鲜电商平台-搜索模块的设计与架构(代码片段)
说明:搜索模块针对的是买家用户,在找菜品找的很费劲下的一种查询方面。目前也是快速的检索商品。 对于移动端的APP买家用户而言,要求的速度在3秒内完成。支持模糊查询,由于业务实战表面,... 查看详情
ssm商城项目(代码片段)
1. 学习计划1、电商行业的背景。2、宜立方商城介绍3、宜立方商城的系统架构a) 功能介绍b) 架构讲解4、工程搭建-后台工程a) 使用maven搭建工程b) 使用maven的tomcat插件启动工程5、SSM框架整合6、svn的使用。2. ... 查看详情
vivo商城前端架构升级-总览篇(代码片段)
【背景】一年前vivo商城还是以Java为技术核心,前后台一起,Java既要负责服务、数据库,也要负责页面的渲染。在早期这种开发模式也能够很好的运行。然而随着业务迭代的加快,前端技术的发展,这种开发模式的弊端越来越明... 查看详情
商城项目01_电商系统基本模式分布式基础概念微服务架构图微服务划分图(代码片段)
...费者销售产品和服务。如:苏宁易购、京东、天猫、小米商城你去超市买东西②.B2B2C模式企业对企业对个人我是一家公司,开发天猫、京东平台,企业可以入住平台,对外提供课程供 查看详情
商城项目01_电商系统基本模式分布式基础概念微服务架构图微服务划分图(代码片段)
...费者销售产品和服务。如:苏宁易购、京东、天猫、小米商城你去超市买东西②.B2B2C模式企业对企业对个人我是一家公司,开发天猫、京东平台,企业可以入住平台,对外提供课程供 查看详情
事件驱动架构在vivo内容平台的实践(代码片段)
...事件来实现跨多个服务的业务逻辑。事件驱动架构是一种设计应用的软件架构和模型,可以最大程度减少耦合度,很好地扩展与适配不同类型的服务组件。在这一架构里,当有重要事件发生时,比如更新业务 查看详情