作者:vivo 官网商城开发团队 – Cheng Kun、Liu Wei

本文介绍了买卖渠道的规划理念和关键技术计划,以及实践进程中的考虑与挑战。

点击查阅:《vivo 全球商城》系列文章

一、布景

vivo官方商城经过了七年的迭代,从单体架构逐步演进到微服务架构,咱们的开发团队沉淀了许多宝贵的技术与经验,对电商范畴事务也有相当深刻的理解。

去年初,团队承接了O2O商城的建造使命,还有行将建立的礼品中台,以及官方商城的线上购买线下门店送货需求,都需求搭建底层的产品、买卖和库存才能。

为节约研制与运维本钱,避免重复造轮子,咱们决议选用渠道化的思想来搭建底层体系,以通用才能灵敏支撑上层事务的个性化需求。

包含买卖渠道、产品渠道、库存渠道、营销渠道在内的一整套电商渠道化体系应运而生。

vivo全球商城:电商交易平台设计

本文将介绍买卖渠道的架构规划理念与实践,以及上线后继续迭代进程中的挑战与考虑。

二、整体架构

2.1 架构方针

除了高并发、高功用、高可用这三高外,还希望做到:

1.低本钱

注重模型与服务的可重用性,灵敏支撑各事务的个性化需求,提高开发效率,下降人力本钱。

2.高扩展

体系架构简略清晰,应用体系间耦合低,简单水平扩展,事务功用增改方便快捷。

2.2 体系架构

(1)电商渠道整体架构中的买卖渠道

vivo全球商城:电商交易平台设计

(2)买卖渠道体系架构

vivo全球商城:电商交易平台设计

2.3 数据模型

vivo全球商城:电商交易平台设计

三、关键计划规划

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

vivo全球商城:电商交易平台设计

用户订单所属库表核算公式

库序号 = Hash(userId) / 表数量 % 库数量 + 开始库编号

表序号 = Hash(userId) % 表数量 + 开始表编号

可能有小伙伴会问:为什么核算库序号时要先除以表数量?下面的公式会有什么问题?

库序号 = Hash(userId) % 库数量 + 开始库编号

表序号 = Hash(userId) % 表数量 + 开始表编号

答案是,当库数量和表数量存在公因数时,会存在倾斜问题,先除以表数量就能除掉公因数。

以2库4表为例,对4取模等于1的数,对2取模也必定等于1,因此0号库的1号表中不会有任何数据,同理,0号库的3号表、1号库的0号表、1号库的2号表中都不会有数据。

路由进程如下图所示:

vivo全球商城:电商交易平台设计

(3)局限性和应对方法

  • 大局仅有ID

问题:分库分表后,数据库自增主键不再大局仅有,不能作为订单号来运用。且很多内部体系间的交互接口只要订单号,没有用户标识这个分片键。

计划:如下图所示,参考雪花算法来生成大局仅有订单号,一起将库表编号隐含在其间(两个5bit分别存储库表编号),这样就能在没有用户标识的场景下,从订单号中获取库表编号。

vivo全球商城:电商交易平台设计

  • 全库全表查找

问题:办理后台需求依据各种筛选条件,分页查询一切满意条件的订单。

计划:将订单数据冗余存储一份到查找引擎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)计划

规划通用操作触发器,具体步骤为:

  1. 装备触发器,粒度是情况机的流程类型。

  2. 创建订单/售后单时或订单情况变化时,假如有满意条件的触发器,发送推迟音讯。

  3. 收到推迟音讯后,再次判别履行条件,履行装备的操作。

触发器的装备包含:

  1. 注册时刻:可选订单创建时,或订单情况变化时

  2. 履行时刻:可运用JsonPath表达式选取订单模型中的时刻,并可叠加推迟时刻

  3. 注册条件:运用QLExpress装备,满意条件才注册

  4. 履行条件:运用QLExpress装备,满意条件才履行操作

  5. 履行的操作和参数

3.4 分布式事务

对买卖渠道而言,分布式事务是一个经典问题,比方:

  • 创建订单时,需求一起扣减库存、占用优惠券,取消订单时则需求进行回退。

  • 用户付出成功后,需求告诉发货体系给用户发货。

  • 用户承认收货后,需求告诉积分体系给用户发放购物奖赏的积分。

咱们是如何确保微服务架构下数据共同性的呢?首先要区分事务场景对共同性的要求。

(1)强共同性场景

比方订单创建和取消时对库存和优惠券体系的调用,假如不能确保强共同,可能导致库存超卖或优惠券重复运用。

关于强共同性场景,咱们选用Seata的AT模式来处理,下面的示意图取自seata官方文档。

vivo全球商城:电商交易平台设计

(2)最终共同性场景

比方付出成功后告诉发货体系发货,承认收货后告诉积分体系发放积分,只要确保能够告诉成功即可,不需求一起成功一起失利。

关于最终共同性场景,咱们选用的是本地音讯表计划:在本地事务中将要履行的异步操作记录在音讯表中,假如履行失利,能够经过守时使命来补偿。

vivo全球商城:电商交易平台设计

3.5 高可用与安全规划

  • 熔断

运用Hystrix组件,对依靠的外部体系添加熔断维护,避免某个体系故障的影响扩大到整个分布式体系中。

  • 限流

经过功用测试找出并处理功用瓶颈,掌握体系的吞吐量数据,为限流和熔断的装备供给参考。

  • 并发锁

任何订单更新操作之前,会经过数据库行级锁加以约束,避免呈现并发更新。

  • 幂等性

一切接口均具有幂等性,上游调用咱们接口假如呈现超时之类的反常,能够定心重试。

  • 网络隔离

只要极少数第三方接口可经过外网拜访,且都有白名单、数据加密、签名验证等维护,内部体系交互运用内网域名和RPC接口。

  • 监控和告警

经过装备日志渠道的错误日志报警、调用链的服务剖析告警,再加上公司各中间件和基础组件的监控告警功用,让咱们能够能够第一时刻发现体系反常。

3.6 其他考虑

  • 是否用范畴驱动规划

考虑到团队非敏捷型安排架构,又短少范畴专家,因此没有选用

  • 高峰期功用瓶颈问题

大促和推行期间,特别是爆款抢购时的流量可能会触发限流,导致部分用户被拒之门外。由于无法准确预估流量,难以提早扩容。

能够经过主动降级计划添加并发量,比方同步入库切为异步入库、db查询转为cache查询、只能查到最近半年的订单等。

考虑到事务复杂度和数据量级还处在初期,团队规划也难以支撑,这些规划有远期计划,但暂时还没做。(架构的合适性准则,杀鸡用牛刀,你乐意也行)。

四、总结与展望

咱们在规划体系时并没有一味寻求前沿技术和思想,面临问题时也不是直接选用业界干流的处理计划,而是依据团队和体系的实际情况来选取最合适的方法。好的体系不是在一开始就被大牛规划出来的,而是跟着事务的开展和演进逐渐被迭代出来的。

现在买卖渠道已上线一年多,接入了三个事务方,体系运行平稳,公司内有买卖/产品/库存等需求的新事务,以及存量事务在遇到体系瓶颈需求升级时,都能够复用这块才能。

上游事务方数量的添加和版本的迭代,对渠道体系的需求源源不断,渠道的功用得到逐渐完善,架构也在不断演进,咱们正在将履约模块从买卖渠道中剥离出来,进一步解耦,为事务继续开展做好储备。