一直以来表达能力都是自己的短板,不知道如何分享出自己觉得比较好的设计,所以决定好好的整理一下这几年使用的架构,以一种更加简单、清晰的思路将之前工作中的系统设计分享给大家。这是第一篇,后面会持续分享。
本博客主要从架构层面讨论交易系统的设计,不涉及细节。细节内容可以参考《交易体系-交易、支付、物流、退款退货》
一 主要内容
本博客主要从以下几个方面讲述交易系统架构:
交易系统的业务域是什么?即交易系统应该负责那些内容。
面对多样性的业务场景,交易系统如何承接他们?即交易系统的业务扩展性如何保证。
交易系统的复杂性问题,以及如何处理,特别是业务、系统解耦问题。
事件驱动流程。
简述高并发与高可用技术。
二 交易
首先我们聊聊什么是交易,以及交易系统的业务域。
1. 概念
交易是一种行为:是buyer在某一时间以某一价格购买了seller的一个或多个商品。现实世界中,这种行为是价值的交换;系统中,这种行为产生的是订单。
订单是一种契约:这种契约要求买卖双方按照订单的内容进行履约。以实物商品交易为例,buyer需要付钱契约才有效,seller需要将商品寄送给buyer。
交易至少涉及以下模型:买家、卖家、订单。
2. 交易
以实物商品交易为例,交易包括以下环节:
注意:这是一个完整的交易涉及的业务,但如果将以上所有业务都放在一个系统中却是一个非常糟糕的注意,后面会详细讨论此点。
3. 业务域分析
1) 下单
卖家必须有某个商品才能卖给买家,所以交易必然需要与商品系统打交道。为了让买家掏腰包,卖家、平台会想方设法的使用各种营销手段,吸引并促进交易的达成。常见的营销手段有:优惠(单品优惠、跨类目优惠、店铺优惠、平台优惠以及其他的组合方式);优惠券(单品优惠券、跨类目优惠券、店铺优惠券、平台优惠券以及其他组合方式);使用积分、金币等虚拟资产抵扣;多次付款(例如预付款等)。
相关业务模型:交易订单、商品、优惠、优惠券、积分、金币等。
2) 付款
为了方便用户购买,很多时候需要支持多种支付方式,例如:支付宝、微信、银联等。
相关业务模型:支付订单
3) 发货与配送
对于拥有成千上万SKU的大商家而言,他们拥有自己的仓库,这些仓库也可能分布在不同的城市,所以他们需要能够管理所有的货物,直到他们的详细信息,选择一个最为经济、快捷的仓库给买家发货。
对于仅拥有几十上百SKU的小商家而言,他们对自己有多少货物心中有数,不需要仓库,甚至直接从家里发货。
发货是一个复杂的过程,如果用户购买了多件,可能会打包一起发送给买家,也可能将一个大件物品拆成多个物流单发给买家;配送的过程也很复杂,涉及物流订单的集散以及派送过程。不过幸运的是很多情况下这些都不需要我们自己实现,直接用第三方的物流服务,我们尽关心物流信息即可。
相关业务模型:商品、商品SKU、货物
4) 收货
对于用户而言,收货仅仅是点击一个“确认收货”按钮而已,很简单,就不多说了。
相关业务模型:交易订单
5) 退款退货
常见的处理流程是:buyer提交退款/退货单,seller确认,如果一切顺利,seller同意退款/退货,buyer将货物发回并将相关物流单号等信息备注好即可。如果seller不同意产生纠纷,这时可能需要平台来做一个裁决。
相关业务模型:退款/退货单、纠纷信息
6) 收货退款
seller收到货物以后,更新退款/退货单信息,系统将钱原路退回即可(绝大多数情况下都是原路退回)。
相关业务模型:交易订单、支付订单
7) 评价
buyer为此次交易进行评价,为其他买家提供参考。
相关业务模型:评价
8) 对账结算
对于平台而言,不但为买卖双方提供交易渠道,也承担了担保人的角色,防止违约事情的发生。一般而言都会有一个结算后期,例如过了退款退货期限,才给商家结算款项;或者每个月结算一次等等。
对于自营的平台而言,这个过程可能会稍有不同。
相关业务模型:交易订单、支付订单、结算单
三 系统架构
从业务域的分析可以看到,完整的交易涉及了buyer、seller、平台、物流等多方;涉及多个正向、异常流程有;另外不同类型的商品的交易流程也会有所区别。综合这些信息来看将所有的业务放在同一个系统中,必然造成系统的臃肿,很容易造成耦合严重,功能相互影响,不方便扩展,优化困难等问题。
如果过多设计系统,系统拆分的过于细小,必然对开发速度、维护成本产生影响。
那么问题来了,我们应该如何划分系统?
1. 系统架构原则
这里仅仅讨论业务架构中几个常见的原则,为讲述讨论交易系统业务域划分做准备。
1) 没有最好的设计,只有最适合的设计。
业务发展的不确定性高,流量小或者中短期内达不到非常高的量级时,越是能以最小的成本达成业务需求的设计,越是优秀的设计。举个例子:如果开发成本只需要一两周,能满足半年到一年的设计很多时候比一个开发成本需要三四个月,能够满足5年需求的系统更加适合。且不说业务一定能发展的好,后者一定能排上用场;长的开发周期必然会拖累业务的快速发展,这是逼着业务拿刀过来砍人了(开个玩笑)。
产品应该小步快跑,系统设计与开发也应该是小而快,避免过度设计。
2) 架构要与业务水平、技术人员的水平匹配。
以淘宝、京东的架构来为一个初创公司架构系统显然是错误的。一来团队人员数量、质量跟不上这些公司;二来请求的流量、业务复杂性不如他们。如果强行使用他们的架构会严重降低开发速度,增加开发、联调、测试、发布等环节的成本,浪费服务器资源、增加维护成本等等。
3) 要能够水平扩展
上面建议不要过度设计,但是如果业务飞速发展,如何能够满足满足业务的需要,让业务不受技术的拖累?系统能够水平扩展是一个非常好的选择,通过增加服务器来为重新优化设计系统提供缓冲时间。
现在有很多开源软件提供了不少方便水平扩展的技术和中间件,例如dubbo、zookeeper、MQ、缓存等。不过在我们的系统中也应该做到以下几点,以方便简单的水平扩展:
尽可能降低db层的压力,因为增加业务服务器比增加DB实例要简单方便很多。
服务器无状态,或者自动管理状态。做到这样部署1台服务器和部署10台开发成本几乎一样。
尽可能使用分布式技术。例如:如果锁定资源,要尽可能使用分布式技术,而不要使用单机技术,否则增加服务器就可能产生不可预知的问题。
4) 要解耦、要易扩展
解耦、易扩展是为了降低维护成本,降低不同业务之间相互影响,提升系统可靠性、可用性的一种手段。面对业务发展不确定性,保证系统具有这两个品质,可以为以后流量快速增长、业务转型、多业务接入打下一个良好的技术。
2. 交易系统的责任与追求
1) 交易系统业务域
关注交易的核心流程,负责订单数据以及状态流转;兼容上层多样性的业务。
一般而言各种不同类型业务的交易流程都会有一定的差异性,此时应该仔细思考交易系统和上层业务的边界,让各个业务系统负责自己的业务领域中的个性化需求,将具有公用性的逻辑沉淀到公共的交易系统中。
简单来说就是对交易模型进行抽象,并定义一个抽象的交易流程出来,不同类型的交易在抽象的交易流程上进行扩展并添加个性化逻辑。
2) 交易系统业务目标
支持多种业务的交易流程;方便、快捷的接入新的业务。
技术是为了解决问题而存在,在公司中技术为业务服务,快速响应业务需求是对业务最大的支持;同样的强大的技术也为业务提供了更多的玩法和趣味性。
3) 支付系统技术目标
低耦合、高可用、易扩展。
高耦合对于系统维护而言是一个灾难;对业务来说会严重拖慢业务的发展,也会对业务的稳定性产生不利的影响。系统应该具有较高的鲁棒性,即便出现了部分服务故障,也应该能够继续提供服务。业务一般不会一成不变,可能快速发展,并发极具增长,可能因为陷入困境而寻求专项,所以系统要能够便于扩展。
3. 交易系统架构
站在交易系统的角度来看,交易系统的架构如下图所示:
说明:之前的一家公司同时做化妆品、旅游这些业务,所以这里我以这些业务为示例讲述。
此架构是根据交易中涉及的业务来划分的,根据实际的场景可以划分的更加细或者更加粗糙一些。
更细化的业务划分示例:如果公司的业务对运营有更多需求,那么优惠、优惠券系统可以进一步的拆分;若积分、金币的业务场景差异性较大也可以拆分成多个系统。
更粗化的业务划分示例:如果业务上不是非常关注仓储、物流,那么这一部分内容放在交易系统中也是可以接受的。
简单来总结一下就是:如果业务差别大、需求多、变化频繁,那么更加细力度的划分是非常合适的;如果需求少、基本没有变化的,为了满足业务快速发展简化业务逻辑,可以将他们合并在一个系统中(模块上最好还是分开),等未来业务上来了在拆分不迟。
4. 交易系统领域划分
5. 交易系统结构
下图是以服务层次的角度来看交易系统的结构:
从上向下多为RPC同步调用;从下往上多为MQ消息,即事件。例如:支付成功后,将交易订单改为已支付;评价后,将交易订单状态改为已评价等等。
交易表现层和交易核心层可以合为一层,是否分开,主要看业务上是否有这个必要。例如:如果交易业务的场景复杂多样,每种交易业务的差异也比较大,这时可以将他们分开,这样在核心层提供细粒度的服务,表现层通过组合多个服务来对外提供服务。
上层业务可以直接关注交易核心层的消息,即图中1处的流程,当然也可以通过交易表现层来实现,只是会复杂一些。
6. 交易系统数据模型
以上模型是希望将围绕交易的所有核心数据都记录下来,此模型支持任意对账需求。例如:交易使用总积分的对账可以通过交易订单和积分抵扣流水进行对账;总收入可以通过交易订单金额和支付订单金额进行对账。
另外,以上模型也可以进行进一步细分,具体可以参考推荐的一篇博客。
7. 一切都基于事件驱动
下面仅仅以创建订单和支付为例,描述基于事件驱动的微服务架构。更多内容请参考推荐的博客。
1) 下单
2) 下单补偿
此流程是为了解决分布式场景中rpc失败,而导致积分、优惠(券)误用,商品误减,当然如果业务上无相关要求,此流程可以忽略掉。
3) 支付
4) 交易达成
支付订单状态驱动交易订单状态修改
5) 个性化业务
交易订单状态,驱动上层业务个性化需求。
6) 总结
基于事件驱动的架构是一种非常好的解决复杂业务的手段。其核心思想是交易系统负责交易核心数据以及状态流转,将状态流转作为事件暴露出去,关心此事件的业务系统监听消息,完成个性化业务诉求。
当然另一个好处就是解耦,可以认为MQ就是为解耦而生的。
8. 设计总结
完整的交易包括非常庞大的业务领域,所以在设计的时候应该首先梳理业务,清晰业务边界,根据公司当前的状况以及对未来业务发展的评估,将不同业务划分到不同的系统中;依托现实场景,抽取交易相关的核心要素,然后抽象出实体模型;沉淀公共部分为公共的服务;以数据模型作为基础,以事件作为推动整个流程流转。
四 高可用与高并发
目前有很多成熟的技术可以实现高可用和高并发。这里先简单讨论与上面的系统设计中直接涉及的几项技术。以后专门写一篇博客,再详细讲述高并发与高可用的技术。
1. 交易系统的高可用与高并发
1) 负载均衡
同步调用使用的Dubbo本身就支持负载均衡。异步使用的RocketMQ本身也支持负载均衡。
2) 异步并发与队列
通过RocketMQ实现。
3) 倒金字塔型的过滤模型
分层逐步过滤非法请求。例如营销活动中,先在h5层拦截用户的重复请求;上层应用根据活动拦截异常请求,以及重复参加活动的请求;保证最后达到交易系统的仅仅是合法的请求,并且尽可能让过来的请求都可以成功。
4) 隔离
常见的手段有:业务隔离、数据隔离、进程隔离/系统隔离、线程隔离、读写隔离动静分离、热点/爬虫隔离、资源隔离、故障隔离
5) 解耦
架构层面:架构中划分业务域、梳理业务边界、梳理系统的依赖关系其实就是在做系统解耦。
细节层面:通过MQ消息对系统、服务进行解耦。
6) 幂等
分布式开发中,凡是通过RPC进行写操作,或者需要重试的均需保证接口幂等。常见手段是:DB唯一键;写操作前先检查;分布式锁。幂等一般和防并发操作写一起使用。
7) 数据最终一致性
分布式场景下,因为存在诸多不确定因素,导致数据不一致,特别是网络不稳定,相关的服务不可用等。保证数据一致性的手段有:最终一致性,TCC、2PC(或3PC)。
根据之前公司的业务场景以及性能要求,我们都是采用RocketMQ实现最终一致性这种方案。具体可以参考:《RocketMQ实践》
2. 高可用与高并发技术
1) 高可用技术
隔离技术、限流技术、降级技术、负载均衡、容灾与备份、读写分离、监控、系统解耦等。
2) 高并发技术
动静分离、缓存、 异步并发、 扩容、 队列等