要拆分微服务,首先我们要了解微服务拆了会有什么问题?怎么合理拆服务?
拆分服务会带来什么问题?
举个电商系统下单扣库存的例子。
对于单体应用,通讯在进程内部进行,下单方法调用扣库存方法,有问题就回滚事务,利用数据库同一个Session会话的ACID特性干活,保证数据的强一致性,即使在调用下单方法成功后应用崩溃,数据也不会提交到数据库,不会产生脏数据。
而拆分成各个微服务后,代码、数据库进行了隔离,下单扣库存逻辑变成了订单服务通过RPC调用库存服务,由于不受同一个数据库Session会话控制,就必然会存在因业务处理失败、应用崩溃、网络通讯异常等一系列问题导致的数据不一致。
这是一个典型的复杂度转移的例子——单体应用的代码复杂度转移到了微服务之间的通讯复杂度,整体的复杂度并没有降低。
回到经典的分布式CAP定理:数据一致性、可用性、分区容错性,三者取其二。
CAP是分布式系统中三个维度的“客户承诺”:
-
一致性:要么我给你返回一个错误,要么我给你返回绝对一致的最新数据,强调数据正确
-
可用性:我一定会给你返回数据,不会给你返回错误,但不保证数据最新,强调的是服务不出错
-
分区容错性:我会一直运行,不管我的内部出现何种数据同步问题,强调的是不挂掉
为了解决数据一致性问题,业界又引入了各种一致性保障机制,比如BASE理论(基本可用、软状态、最终一致)、分布式事务DTP模型(XA协议、TCC协议)、JTA模型等等,根据对数据一致性的要求又划分为强一致性、弱一致性、最终一致性的方案,在分布式系统中通过一系列措施来保证ACID。
在实际互联网项目开发中,分布式事务不宜设计得太重,通常来说异步的场景使用事务性MQ来解决,比如RabbitMQ、Kafka、RocketMQ等;同步的场景使用业务状态机来规避它们,比如订单分正向销售单、逆向售后单,单据有不同维度的状态,比如支付状态、退款状态、物流状态、开票状态等等,对于出错的环节进行客户重试、系统告警或客服干预,暂时停留在异常节点,这里的“状态”可以理解为BASE理论中的软状态。
说到底还是用BASE理论指导生产。
说那么多,我们通过拆分微服务,提高了系统的分区容错性与可用性,却牺牲了单体应用的一致性优势,所以说,不要为了拆而拆,拆服务也需要合理“动机”,那什么样的“动机”是合理的呢?
如何合理拆分微服务?
OK,了解了服务拆分带来的问题后,我们拆服务就得更加严谨了,那怎么合理拆呢?
这里提供一些思路。
一、按单一职责拆
还是以我们的电商平台举例,一开始我们最核心的OMS订单系统做了特别多事情,包括:用户、下单、商品、库存、出入库、营销……
随着公司业务快速增长,OMS代码激增,新增/修改一个功能就要影响几乎整个链路,稳定性降低,也大大增加了风险,运维变得十分困难,这时不得不把各个模块剥离出来,独立成为UC用户服务、PMS商品服务、CIS中央库存服务、WMS出入库服务、MCS营销中心等等。
我们按照单一职责的划分原则,每一个独立的服务只提供该业务领域的核心功能,继而每个独立的服务演化出更为丰富的功能,数据库也进行垂直拆分提供应用独立访问,并且每个服务提供双节点保证高可用。
二、按团队组织架构拆
这里必须提一提软件架构设计中的第一定律——康威定律。
康威定律是马尔文·康威1967年提出的:“设计系统的架构受制于产生这些设计的组织的沟通结构。”通俗地来讲:产品必然是其(人员)组织沟通结构的缩影。
康威定律可总结为以下四个定律:
第一定律:组织沟通方式会通过系统设计表达出来。
这条定律重点是讲组织架构和沟通对系统设计的影响。
组织的沟通和系统的设计之间紧密相连,特别是复杂系统,解决好人与人的沟通才能有一个更好的系统设计。 沟通的问题会带来系统设计的问题,进而影响整个系统的开发效率和最终产品结果,这也是为什么互联网公司都追求小团队的原因之一。
第二定律:时间再多一件事情也不可能做得完美,但总有时间做完一件事情。
人手永远是不够的,事情永远是做不完的,但可以一件一件来。
这不就是软件行业中“敏捷开发”模式所解决的问题吗,面对这样的状况,敏捷开发可以做到不断迭代、持续交付、快速验证和反馈,并持续改进。
再牛的开发也会写出BUG,再全面的测试覆盖率也无法测出所有的问题,解决方案不是消灭这些问题,是容忍一些问题的存在,然后通过适当的设计(冗余、监控、高可用设计),当问题发生时能够快速解决。 几个开发人员的小公司,去追求微服务、中台架构、这是追求完美吗?
不是,这是找死。
好的架构不是买来的,也不是设计出来的,而是根据业务落地生根长期演化来的。
第三定律:线型系统和线型组织架构间有潜在的异质同态特性。
这一定律是第一定律的具体应用。
想象一下如果公司的架构是这样的: 团队是分布式,每个团队都包含产品、研发、测试、运维等角色,而此时系统是单体应用,那项目沟通和协调的成本是巨大的,弄不好还会打起来。
如果将单块的系统拆分成微服务,每个团队负责自己的部分,对外提供对应的接口即可,互不干扰,系统效率将得到提升,这与软件设计中的高内聚、低耦合是相通的。
添加图片注释,不超过 140 字(可选)
直白地说就是想要什么样的系统就搭建什么样的团队,有什么样的团队就搭建什么样的系统,需要前后端分离的系统就搭建前后端分离的团队;反之,拥有前后端分离的团队就可以设计前后端分离的系统。
第四定律:大的系统组织总是比小系统更倾向于分解。
“话说天下大势,分久必合,合久必分。”系统越复杂,越需要增加人手,人手越多,沟通成本也呈指数增长,分而治之便是大多数公司选择的解决方案,分不同的层级,分不同的小团队,让团队内部完成自治理,然后统一对外沟通。
我们试着从康威定律来推导系统的架构演进方向,自然知道微服务的拆解粒度了。
SOA 也好、微服务也好,解决的根本问题是团队分工问题,这是大型软件发展的必然,不因为人的喜好而改变,当你读懂康威定律,就会发现“服务拆分粒度难以准确把握”根本不是本质问题,你有几个 2 pizza 团队,最好就拆成几个微服务。
只有一个开发人员时,尽量就做单体应用,不要没事找刺激拆成 10 个微服务,最终这个开发人员还会把他合成一个。
微服务要求纵向的 2 pizza 团队(无数个小团队,包含开发、测试、运维),如果团队还是处在横向结构的场景下(开发、运维、测试各是一个团队),比如说一些传统大型企业,去实施微服务会让他们很痛苦,尤其是运维团队。
总结一下
具体实践建议:
-
我们要用一切手段提升沟通效率,比如slack,github,wiki。能2个人讲清楚的事情,就不要拉更多人,每个人每个系统都有明确的分工,出了问题知道马上找谁,避免踢皮球。
-
通过MVP的方式来设计系统,通过不断地迭代来验证优化,系统应该是弹性设计的。
-
你想要什么样的系统设计,就架构什么样的团队,能扁平化就扁平化。最好按业务来划分团队,这样能让团队自然的自治内聚,明确的业务边界会减少和外部的沟通成本,每个小团队都对自己的模块的整个生命周期负责,没有边界不清,没有无效的扯皮,inter-operate, not integrate。
-
做小而美的团队,人多会带来沟通的成本,让效率下降。亚马逊的Bezos有个逗趣的比喻,如果2个披萨不够一个团队吃的,那么这个团队就太大了。事实上一般一个互联网公司小产品的团队差不多就是7,8人左右。
总之,只要说得清楚,运维能力又能跟上,服务拆分一般就是合理的!