mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-11-17 06:33:48 +08:00
mod
This commit is contained in:
149
极客时间专栏/架构实战案例解析/业务架构篇/02 | 业务架构:作为开发,你真的了解业务吗?.md
Normal file
149
极客时间专栏/架构实战案例解析/业务架构篇/02 | 业务架构:作为开发,你真的了解业务吗?.md
Normal file
@@ -0,0 +1,149 @@
|
||||
<audio id="audio" title="02 | 业务架构:作为开发,你真的了解业务吗?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ab/22/aba8fe4a1b83d403a92944ec51085322.mp3"></audio>
|
||||
|
||||
你好,我是王庆友,今天我们一起聊聊业务架构。
|
||||
|
||||
作为开发人员,我们平常讨论比较多的是技术层面的东西,比如Spring框架、Redis缓存、MySQL数据库等等,我们喜欢讨论这些,是因为纯技术的东西比较通用,和业务相关性不大,沟通起来比较方便。
|
||||
|
||||
但你要知道,一个项目能否成功落地,首先需要的是把业务分析做到位,至于选用什么技术来实现,这是我们第二位才去考虑的因素。**从架构角度看,业务架构是源头,然后才是技术架构**。所以,作为专栏的第二讲,今天我们就从业务架构开始说起。
|
||||
|
||||
在软件开发的过程中,你肯定知道需求分析是怎么回事,但不一定知道业务架构设计是怎么回事;你也肯定清楚需要产品经理这个角色,但不一定清楚有时还需要业务架构师这个角色。关于需求分析和业务架构设计,相信你经常会有以下几个疑问:
|
||||
|
||||
1. **业务架构师和产品经理有什么区别?**
|
||||
1. **需求分析和业务架构设计有什么区别,业务架构到底有什么用?**
|
||||
|
||||
我们知道,项目的开发都是从收集业务需求开始的,原始的需求一般来自于最终用户。但是,每个用户其实只清楚自己所负责的那部分,因此这些原始需求往往是零散和碎片化的,特别是当一个业务流程跨多个部门的时候,更没有一个人能够说清楚这个业务的全貌。
|
||||
|
||||
所以说,仅仅基于这些原始的需求来指导开发是远远不够的,这时,就需要产品经理和架构师介入进来,填补这段空白。
|
||||
|
||||
接下来,我们就一起看下,产品经理和架构师在这个过程中都会做些什么,他们是如何帮助业务落地的。
|
||||
|
||||
## 产品经理的职责
|
||||
|
||||
简单来说,产品经理的职责就是:**告诉用户,系统长什么样子;告诉开发,他要实现什么功能。**
|
||||
|
||||
产品经理首先会收集用户的原始需求,然后,将它们梳理成一个个业务流程,每个业务流程由多个业务步骤组成。一个业务步骤包含三部分的内容:输入、输出和业务功能。
|
||||
|
||||
比方说,一个典型的交易流程,它包含商品浏览、商品加购物车、下单、支付等步骤。其中,下单步骤的输入,就是订单的各种信息,下单的功能,就是整合这些信息,创建一个具体的订单,而下单的输出结果,就是新创建的订单。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9e/e9/9e0b775a1b358ec4deef9a1bd066cce9.jpg" alt="">
|
||||
|
||||
需求梳理好后,产品经理会把每个步骤具体化为页面原型。在原型中,会以直观的方式给出各个步骤的输入或输出,以及用户的操作过程,最后再把这些页面串起来,形成一个业务流程。
|
||||
|
||||
你可以看到,经过产品经理的工作,大量零散的原始需求经过梳理和关联,变成一系列有序的业务流程,以及流程里面的业务步骤(业务步骤也称之为业务节点),然后产品经理把这一系列的业务流程和业务节点以用户界面的方式定义出来,总的来说,产品经理定义了系统的外表。
|
||||
|
||||
这些产出对于用户了解系统长什么样子,应该如何使用这个系统,以及系统是否满足他们的需求来说,是足够的,但对于开发者来说还远远不够,因为他们需要能进一步看到系统的内部结构。
|
||||
|
||||
而这一步,就是业务架构师要做的事情。
|
||||
|
||||
## 业务架构师的职责
|
||||
|
||||
在这之前,我们不妨先思考下,如果是按照产品的输出,直接以业务流程的角度来构建系统,会是什么样子呢?
|
||||
|
||||
如果按照这个思路,我们将为每个业务流程搭建一个对应的系统模块,然后业务流程中的每个业务步骤,将对应系统模块中的一个接口,包括它的功能、输入和输出。
|
||||
|
||||
就拿前面的购物流程来说,我们设计一个购物流程模块,里面包含商品查询、添加购物车、下单和支付接口,来分别对应流程里的4个业务步骤。
|
||||
|
||||
以这样的方式构建系统,表面上看起来,业务和系统的映射好像非常简单,但在实际中,落地的难度非常很大。因为只是这样一个小小的购物流程模块,就要同时涉及商品、购物车、下单和支付四个业务,模块的开发者要同时非常清楚这四部分的数据模型和业务逻辑。
|
||||
|
||||
同样的道理,系统里的其他模块也是包含多个业务领域的内容,如果一个业务领域的需求发生了变化,比如说,订单要增加一个新的状态,那么所有涉及该订单的模块都要知道这个变化,并要做出相应的调整。这就要求,每个开发者都是全知全能的,对所有业务都了如指掌,我们知道,这是不可能的。
|
||||
|
||||
每个业务都有其本身的专业性,比如订单业务、商品业务、支付业务,它们的数据模型和业务逻辑都相当复杂,构成了一个个相对独立的业务领域。如果我们是按照业务流程来划分系统模块,结果是把不同业务混在了一个模块里,所以,这种模块划分的方式并没有降低总的业务复杂度。
|
||||
|
||||
我们可以换一种做法,先把所有的业务流程拆散,这样得到了一堆业务节点;然后把业务节点进行归类,相关的业务节点放在同一个系统模块里。判断节点是否相关,主要看它们是否属于同一个业务领域,比如一个订单查询的节点,和订单创建的节点,它们都属于订单域,那么这些节点都可以归属到同一个订单模块里。
|
||||
|
||||
下图就清楚地表示出了系统模块按业务流程拆分,和按业务域拆分的不同。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/6e/74/6e4fcfb3784531aa4365730b90fa7374.jpg" alt="">
|
||||
|
||||
- 如果按照业务流程来拆分系统模块,那么,有多少业务流程,就有多少个系统模块,这个对应关系比较直接,但实现起来很困难。
|
||||
- 如果按照业务域来拆分,有多少业务领域,就有多个系统模块,流程中的业务节点按照业务域的不同,可以划分到不同的系统模块。
|
||||
|
||||
在实际业务场景中,一个业务节点可能会涉及不同业务领域的功能。比如说,一个下单节点,会涉及到获取商品信息、获取用户信息、扣库存、下订单等多个业务功能,那么你就可以进一步分解这个节点的功能,把不同的功能分到对应的业务域和系统模块。
|
||||
|
||||
基于业务域,构建了系统模块后,我们就可以按照这样的方式还原整个业务流程,比如上面的购物流程例子,我们就可以这样还原它:
|
||||
|
||||
>
|
||||
**购物流程=商品模块.商品搜索+购物车模块.添加商品+订单模块.创建订单+支付模块.支付**
|
||||
|
||||
|
||||
如果你把这个定义画成序列图,就很直观和容易理解,也比较符合开发人员思维,系统实现起来非常容易。通过这种系统模块之间的不同功能组合,我们很容易给出各个业务流程的定义。
|
||||
|
||||
所以,**对业务架构师来说,TA的工作,就是把业务流程和节点打散,按照业务域的维度来划分系统模块,并定义这些模块之间的关系,最终形成一个高度结构化的模块体系**。这样,开发人员既容易理解业务,又方便落地系统。
|
||||
|
||||
现在,我们就可以回答文章开头的问题了,**产品经理和业务架构师的工作既有区别又有联系,简单地说,产品经理定义了系统的外观,满足了用户;业务架构师在此基础上,进一步定义了系统的内部模块结构,满足了开发人员。**
|
||||
|
||||
当然,满足现有的业务需求,保证系统顺利落地,这只是业务架构的最基本目标,业务架构的意义远不止于此,它有一系列更高的目标,下面,我就逐一为你展开介绍。
|
||||
|
||||
## 架构目标之一:业务的可扩展
|
||||
|
||||
第一个目标是业务的可扩展,我们都知道,业务需求是不断变化的,不断创新是业务的内在要求。而对于系统来说,它的要求却是相对稳定,尽量避免大的调整。
|
||||
|
||||
**那么,我们如何才能实现业务的快速变化和系统的相对稳定呢?**
|
||||
|
||||
这也是业务架构要重点解决的问题,具体地讲,业务架构设计要能支持打造一个柔性系统,通过提供良好的业务扩展性,允许业务不断调整和快速生长。
|
||||
|
||||
可以看到下图中,左边部分就比较形象地展示了业务和系统的不同特点:**业务的主题是变化和创新,系统的主题是稳定和可靠。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/67/23/677b2ee621f753e67730b156eeed2023.jpg" alt="">
|
||||
|
||||
在右边图中,我们通过巧妙的业务架构设计,很好地解决了业务和系统之间的矛盾。
|
||||
|
||||
这里,我们把业务平台和业务线剥离开,让业务平台封装基础通用的功能,这样,它就变得相当地稳定;让各个业务线包含自己的个性化需求,业务线只依赖业务平台,业务线彼此之间互相独立,可以自由变化。这样的业务架构设计,就同时保证了系统的相对稳定和业务的快速创新。
|
||||
|
||||
为了帮助你更好地理解业务架构的扩展性,这里,我给出了支付宝的业务架构变化过程。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/78/b9/78f16439bc590b8e2e5d6e105cd89bb9.jpg" alt="">
|
||||
|
||||
在支付宝一代的业务架构中,前台的业务和后台的业务直接耦合,形成了多对多的网状结构,如果修改一个后台业务线,就会影响到很多前台业务线;如果增加一条新的前台业务线,需要同时和很多后台业务线对接,这样的架构无疑是对业务的扩展非常不利的。
|
||||
|
||||
而在支付宝二代业务架构中,你会发现,他们在前后台业务线之间,构建了独立的支付清算平台,从而实现了前台业务和后台业务的解耦。
|
||||
|
||||
在这里,不管前台业务,还是后台业务,都只需要对接中间的支付清算平台,把系统的变化收敛到一个点,而业务线之间相互不影响,这样的方式,自然可以很好地支持业务扩展。
|
||||
|
||||
好了,这里我们说完了业务架构的可扩展目标,接着再说说业务架构的另一个目标:可复用。
|
||||
|
||||
## 架构目标之二:业务的可复用
|
||||
|
||||
你肯定会有这样的体验:一个项目过来,你和伙伴们一起加班加点、紧赶慢赶,总算把它成功落地了。结果这时候又有另一个类似的项目过来,你们又要按照同样的方式,重新吃一遍苦,结果就是开发不满意,项目经理不满意,老板也不满意。
|
||||
|
||||
**对于类似的业务需求,如果原来做的工作可以大量复用的话,这是非常理想的结果,无论对于开发效率和开发质量的提升都很有帮助。**
|
||||
|
||||
当然,能不能复用,能在多大程度上复用,这和业务架构设计很有关系,也是业务架构设计的重要目标之一。
|
||||
|
||||
**那么,业务架构设计如何实现业务的可复用呢?**
|
||||
|
||||
你可以试想一下,在业务架构设计中,如果只是简单地基于业务流程来定义系统模块,这个系统模块就要和业务流程严格对应。我们知道,业务流程对应业务场景,而业务场景是经常变化或是定制的,这就导致系统模块也是经常变化和定制的,那么,这样的系统模块就很难在不同业务场景中复用。
|
||||
|
||||
如果我们按照业务域来划分业务,把业务流程中的节点拆分到各个业务域,按照业务域构造系统模块,这样的复用效果会如何呢?
|
||||
|
||||
我们都知道,业务域是相对固定的,它有明确的数据模型和业务规则,这样一来,系统模块也就比较固定和通用,也就具备比较好的复用基础。
|
||||
|
||||
但要想实现高复用,业务架构对系统模块的定义,还有更多的要求。
|
||||
|
||||
**首先,模块的职责定位要非常清晰**。对于模块来说,在定位范围内的职责要全部涵盖到,而不在这个范围的职责全部不要。
|
||||
|
||||
**其次,模块的数据模型和接口设计要保证通用**。架构师需要归纳业务场景,通过抽象提炼,形成通用化的设计,以此来满足多个类似场景的需求。
|
||||
|
||||
>
|
||||
小提示:清晰的模块定位和通用化设计,是模块能够复用的内在要求。
|
||||
|
||||
|
||||
**最后,实现模块的高复用,还需要做好业务的层次划分**。我们知道,越是底层的业务,它就相对更固定。举个例子,同样是订单业务域,对于底层订单的增删改查功能,不同类型的订单都是一样的,但对于上层的订单生命周期管理,外卖订单和堂食订单可能就不一样。
|
||||
|
||||
所以,在做高复用设计时,我们可以尝试把一个业务域按照层次拆分得更细,比如,把订单模块拆分为多个上层订单模块和一个基础订单模块,这样,基础订单模块对于所有类型的订单,都能够提供复用。
|
||||
|
||||
就拿当前非常流行的微服务架构来说,很多公司在微服务的基础上,通过服务分层,进一步落地了共享服务体系和中台架构,这些都是业务架构复用能力的体现。
|
||||
|
||||
下面是一个三方支付平台的业务架构图,你可以看下,在一个实际的业务架构中,模块是怎么划分的,架构的可扩展和高复用是如何体现的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/aa/cd/aa9d4457111224ced6c4c26c4ea03acd.jpg" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我带你了解了产品经理和业务架构师的不同职责,产品经理是站在用户的角度进行需求分析,而业务架构师是站在开发者的角度定义系统内部结构。通过今天的讲解,你应该对业务架构也有了更清楚的认识。
|
||||
|
||||
除了满足当前的业务需求外,业务架构师还需要面向未来,实现业务的可扩展和高复用两大目标,我也大致介绍了架构师实现这些目标的思路。在接下来的文章里,我还会针对这两大目标,结合实际案例,具体讲解如何实现它们,让你能更加深入地理解业务架构设计,并可以在工作中学会去运用这些手段。
|
||||
|
||||
**最后,给你留一道思考题**:产品经理和业务架构师都是分析业务,产品经理为什么不能兼业务架构师的角色?他们的能力模型有什么区别?
|
||||
|
||||
欢迎在留言区和我互动,我会第一时间给你反馈。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。
|
||||
169
极客时间专栏/架构实战案例解析/业务架构篇/03 | 可扩展架构:如何打造一个善变的柔性系统?.md
Normal file
169
极客时间专栏/架构实战案例解析/业务架构篇/03 | 可扩展架构:如何打造一个善变的柔性系统?.md
Normal file
@@ -0,0 +1,169 @@
|
||||
<audio id="audio" title="03 | 可扩展架构:如何打造一个善变的柔性系统?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/09/99/097098ed411c5246d3a825389a071399.mp3"></audio>
|
||||
|
||||
你好,我是王庆友,今天我和你聊一聊如何打造可扩展的架构。
|
||||
|
||||
在实际工作中,业务需求总在不断变化,因此,你经常会面临以下这些问题:
|
||||
|
||||
- 如何快速地上线新业务?老板很可能明天就想看到效果。
|
||||
- 对某个功能进行修改,如何不影响到系统其它的功能?
|
||||
|
||||
对于新的需求变化,我们一方面要快快搞定,另一方面要稳稳接住。但问题是软件虽然姓“软”,但也不是想变就能变,如果事先没有经过良好的设计,调整起来,往往牵一发动全身,导致系统到处出问题。
|
||||
|
||||
那如何设计一个具有良好扩展性的系统,能够快速支持业务变化落地呢?
|
||||
|
||||
接下来,我们围绕系统的可扩展,先来了解下什么是系统,什么样的系统才能具备良好的扩展能力。然后通过一个实际的例子,说明如何通过架构手段打造一个可扩展的系统。
|
||||
|
||||
## 系统的构成:模块+关系
|
||||
|
||||
我们天天和系统打交道,但你有没想过系统到底是什么?在我看来,系统内部是有明确结构的,它可以简化表达为:**系统 = 模块+关系。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/9e/8d/9ebf45d4bbaa1cf35873c2def64b618d.jpg" alt="">
|
||||
|
||||
在这里,模块是系统的基本组成部分,它泛指子系统、应用、服务或功能模块。关系指模块之间的依赖关系,简单地讲,就是模块之间有调用,我们知道,调用区分发起方和服务方,因此,依赖关系是有方向性的。
|
||||
|
||||
这个模型虽然简单,但它给我们提供了一个深入分析系统的工具。接下来,我们就从业务扩展性出发,讨论什么样的模块是容易修改的,什么样的依赖关系是容易调整的。
|
||||
|
||||
### 模块
|
||||
|
||||
我们先看模块,模块定义系统都有哪些基本的“玩家”,分别承担什么职责。从业务的角度看,每个模块都代表了某个业务概念,或者说业务领域。
|
||||
|
||||
模块内部由数据和业务逻辑组成,其中数据是核心,业务逻辑围绕着数据,对数据做进一步加工,方便外部使用。
|
||||
|
||||
从扩展性的角度出发,首先,我们对模块的要求是:**定位明确,概念完整。**
|
||||
|
||||
每个模块要有明确的定位,模块有了定位,说明我们已经想清楚了它的核心职责是什么,这样,每个人对它的期望和理解就会一致。在实践中,我们经常会争论一个功能应该放到A模块还是B模块,表面上看,各有各的道理,谁也说不服谁,但如果对照模块的定位,回到模块设计的初心,我们往往很快就能有答案。
|
||||
|
||||
定位比较抽象,在具体划分模块职责的时候,要保证模块业务概念的完整性。数据上,模块需要覆盖对应业务领域的全部数据,比如一个订单模块,它要覆盖所有渠道的订单,包括三方平台的订单、自有商城的订单、线下门店的订单等,这些不同类型订单的数据模型和实际数据,都由订单模块负责。
|
||||
|
||||
功能上,模块要包含业务领域的全部功能,比如订单模块包含所有订单相关的功能,包括订单数据的增删改查、订单业务规则校验、订单的状态和生命周期管理等。
|
||||
|
||||
其次,模块还要:**自成体系,粒度适中。**
|
||||
|
||||
模块的业务逻辑**尽量围绕自身内部数据进行处理**,对外部依赖越小,模块的封装性越好,稳定性也越强,不会随着外部模块的调整而调整。
|
||||
|
||||
模块的粒度要保持适中,不能为了追求定位清晰,把粒度划分得很小,导致系统的碎片化。比如系统早期的时候,一般我们把积分功能放到用户模块里面,不单独构建积分模块,如果后续积分的概念越来越突出,承载的业务越来越复杂,到时候可以把积分功能分离出来,单独成模块。
|
||||
|
||||
这里,为帮助你更好的理解,我举一个模块划分的反面例子。在实际工作中,很多老系统都有体量很大的模块,我们称之为“肿瘤”,它的特点就是定位模糊,职责泛滥,功能无所不包,这样,模块的可维护性很差,没人敢轻易对它动刀子。
|
||||
|
||||
好了,说完了模块,我们再继续看下模块的依赖关系。
|
||||
|
||||
### 依赖关系
|
||||
|
||||
依赖关系定义了模块如何协作,一起完成业务流程,依赖关系实质上体现的是模块的组织结构。
|
||||
|
||||
如果不对模块的依赖关系做针对性设计的话,依赖关系就是一个多对多的网状结构,一个有N个模块的系统,理论上有N×N个依赖关系,如果考虑依赖具有方向性,这个数字还要加倍。
|
||||
|
||||
所以,要简化模块的依赖关系,我们就要同时简化依赖的方向和减少依赖的数量。
|
||||
|
||||
首先,我们希望模块之间的依赖是单向的,尽量避免相互调用,为什么单向更好呢?我们知道业务流程是有顺序的,如果模块依赖关系越直观地体现业务流程的顺序,越能帮助人理解,否则,我们会被双向的依赖箭头绕的晕头转向,很难通过模块之间的依赖关系还原实际业务的处理过程。
|
||||
|
||||
接下来,我们看下模块的组织结构。我们知道,网状结构是一种松散的结构,节点之间的依赖关系比较复杂,一般用于表示非正式的关系,比如人群的社交关系;而层次结构是一种更有序的结构,一般用于表示正式的关系,比如公司内部的人员关系。
|
||||
|
||||
在模块的组织结构设计上也是如此,我们要尽量把网状结构转化为层次结构,模块结构层次化是简化模块依赖关系的有力手段。
|
||||
|
||||
具体做法就是,我们按照模块定位的不同,把模块划分为不同层次,比如划分为上面的应用层和下面的资源层。这样,一个层通过把多个模块组织在一起,就形成了概念上更大粒度的模块。有了层以后,我们理解业务时,因为模块定位相同,往往关注这个更大粒度的层就可以,依赖关系只要指向这个层,而不是层里面的各个模块。这样,从人理解业务的角度,依赖的数量大幅度地减少了。
|
||||
|
||||
另外,我们知道,层与层之间的依赖关系都是层与层之间自上而下的依赖,相对于多对多的网状依赖,层次依赖的方向更清晰,特别符合人的理解习惯。
|
||||
|
||||
举个具体例子,作为开发,我们都比较了解MVC架构,系统模块按照定位,分为表示层、应用层、聚合服务层、基础服务层。
|
||||
|
||||
- **表示层**,对应前端的模块,如App、小程序、公众号等,属于View层。
|
||||
- **应用层**,对应和前端表示层直接关联的服务端,属于Control层。
|
||||
- **聚合服务层**,如果系统业务比较复杂,经常需要单独的聚合服务层负责业务流程的编排组合,这个属于Model层的加强。
|
||||
- **基础服务层**,代表最基础的业务模块管理,如订单、商品、用户等,属于实际的Model层。
|
||||
|
||||
我在这里贴了一张MVC分层结构图,你可以看到,模块总体上是非常清晰的层次结构。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7d/b0/7d0eb4e3c00a289568a1fca3deaf83b0.jpg" alt="">
|
||||
|
||||
现在,我们清楚了一个可扩展系统对模块和依赖关系的要求,接下来,我们再回到系统扩展性目标,做个深入总结。
|
||||
|
||||
## 扩展性的本质
|
||||
|
||||
在文章开头,我们说因为业务总在变化,所以需要架构设计给系统提供良好的扩展性。
|
||||
|
||||
这只是表象,深层的原因是,一个新的需求进来,系统不只是为它增加一个新功能这么简单,系统的调整会引起一系列的连锁反应,从而大面积地影响系统的现有功能。架构设计时,如果模块划分的不好,一个N个模块的系统,它的复杂度就是N×N(这个在上一讲介绍的支付宝一代架构中,体现得很明显)。如果再加一个新的模块,复杂度就变成(N+1)×(N+1),系统的复杂度随着功能的数量指数级地上升,这样一来,当系统的规模到一定程度,复杂度就会失控,导致系统彻底无序。
|
||||
|
||||
所以,要支持系统的扩展,架构设计上必须能够控制系统的复杂度,面对新需求,要让系统复杂度做加法而不是乘法,从而保证系统的调整是局部化和最小化的,所以,业务架构扩展性的本质是:**通过构建合理的模块体系,有效地控制系统复杂度,最小化业务变化引起的系统调整。**
|
||||
|
||||
那如何打造一个合理的模块体系呢?具体的架构手段就是按照业务对系统进行拆分和整合:**通过拆分,实现模块划分;通过整合,优化模块依赖关系。**
|
||||
|
||||
接下来,我们以一个在线出行公司为例,它有出租车、快车和顺风车3条业务线,来具体看下如何为它打造合理的模块体系。
|
||||
|
||||
## 打造可扩展的模块体系:模块拆分
|
||||
|
||||
我们先对系统进行模块化拆分,拆分有两种方式:**水平拆分和垂直拆分。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/c2/82/c2bad813a8dbaca121a701abebf82682.jpg" alt="">
|
||||
|
||||
### 水平方向拆分
|
||||
|
||||
水平拆分是指从上到下把系统分为多层,按照系统处理的先后顺序,把业务拆分为几个步骤。
|
||||
|
||||
比如,整个叫车过程,我们可以分为UI展现、地图搜索、运力调度和订单支付等几个环节,这是根据系统的处理过程进行划分的。
|
||||
|
||||
这样一来,我们就把一个复杂流程,分解为几个相对独立的环节,分别进行处理,这么做带来了很多好处。
|
||||
|
||||
首先,UI展现部分独立成为一个模块,实现了前后端的分离。我们知道,前端的用户体验和界面样式会经常变化,而后端的数据和业务逻辑相对稳定,通过水平拆分,我们实现了稳定部分和不稳定部分的分开,避免相互影响。
|
||||
|
||||
这里的后端包含三个模块,其中地图搜索负责路径规划,运力调度负责人车匹配,订单支付负责交易管理。
|
||||
|
||||
可以看到,通过水平拆分,可以使每一块职责都比较明确,功能内聚,每个模块管理自己内部的复杂性。同时,模块之间相互松耦合,一个模块的修改不影响另一个模块,比如地图搜索模块中改变了优先路径的推荐,不会影响运力调度模块中的人车匹配算法。
|
||||
|
||||
水平分层可以很好地满足现有业务做深度扩展,当业务有变化时,系统在特定层做调整,对其他层影响有限,这样把变化局限在一个小范围。
|
||||
|
||||
### 垂直方向拆分
|
||||
|
||||
垂直拆分指的是按照不同的业务线拆分,比如,将整个出行业务分为出租车业务、快车业务和顺风车业务,按照不同的业务场景,自上而下进行竖切,让每个业务都自成体系,形成自己的业务闭环。
|
||||
|
||||
通过垂直拆分,一个复杂的出行场景就拆分为几个具体的场景,我们可以根据各个业务线的特点去设计系统,从而降低了整个系统的复杂性。
|
||||
|
||||
垂直拆分可以很好地满足业务广度上的扩展,比如说增加一条新的业务线,可以按照这个思路落地系统。
|
||||
|
||||
**一般做业务架构时,我们先考虑垂直拆分,从大方向上,把不同业务给区分清楚,然后再针对具体业务,按照业务处理流程进行水平拆分。**
|
||||
|
||||
如果同时进行垂直拆分和水平拆分,一个大系统被拆分为了一个二维的模块矩阵,每个模块既属于某个业务线,也属于业务流程的某个环节。这样一来,每个模块的职责都很清晰,当业务变化了,我们可以清楚地知道,这个变化涉及哪些模块,然后,对这些模块进行相应的调整就可以。
|
||||
|
||||
为了帮你更好地理解这两种拆分方式的好处,我这里举个搭积木的例子。经过拆分,每个业务模块都成为一个积木,然后,我们以搭积木的方式来构造系统。当业务发生变化,我们就调整对应的积木,如果系统拆分得合理,拆分后的模块就具有良好的封装性,也就意味着我们主要是调整积木的内部,而它的外观基本不变。这样一来,相邻的积木不会受到影响,系统整体也不需要大的调整。结果是,系统的变化是局部和可控的,保证了灵活的应对变化能力。
|
||||
|
||||
## 打造可扩展的模块体系:模块整合
|
||||
|
||||
系统拆完后,接下来就是模块整合的工作,整合也有两种好的手段:通用化和平台化。
|
||||
|
||||
### 通用化整合
|
||||
|
||||
通用化指的是通过抽象设计,让一个模块具备通用的能力,能够替代多个类似功能的模块。
|
||||
|
||||
回到刚才的出行平台,我们发现3条业务线都有地图搜索、运力调度、订单支付这些模块,不同的业务线之间,这些同名的模块逻辑高度类似,只是细节方面有差别。
|
||||
|
||||
那么,我们能不能对这些类似的模块进行抽象化处理,整合成一个通用的模块呢?答案是可以的,我们可以在模块接口中,通过输入参数标识调用来自哪个业务,是出租车、快车还是顺风车,然后在模块内部,针对不同业务线的差异化部分做针对性处理。结果可能是这个通用模块增加5%的逻辑,但避免了95%的重复逻辑,这样,经过通用化整合,新的模块以很低的代价,就为多个业务线提供了复用。而且,当新的业务线进来,很可能这个通用化的模块,就已经提供了现成的支持。
|
||||
|
||||
**通过模块通用化,模块的数量减少了,模块的定位更清晰,概念更完整,职责更聚焦。在实践中,当不同业务线对某个功能需求比较类似时,我们经常会使用这个手段。**
|
||||
|
||||
### 平台化整合
|
||||
|
||||
平台化是把定位相同的模块组织在一起,以组团的方式对外提供服务。对于外部系统来说,我们可以把这些模块看成是一个整体,一起对业务场景提供全面的支撑。
|
||||
|
||||
如下图所示,我们可以看到,地图搜索、运力调度、订单支付,都是各个业务线都需要的基础和通用的业务能力,当我们增加新的业务线时,还是离不开这些基础能力。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/76/3e/76faef600320f2b8974b4860cd57de3e.jpg" alt="">
|
||||
|
||||
所以,我们可以把这些基础模块放在同一层,构成一个基础业务平台。之前,它们是一个个离散的服务,独立地输出能力,现在变成一个大的业务平台,可以提供整体的能力输出。
|
||||
|
||||
通过打造业务平台,一方面,我们对多个业务模块进行包装,形成更大粒度的抽象,相当于减少了模块的数量;另一方面,作为平台,它的定位更明确,系统依赖关系也更清晰;而且,如果新的业务线进来,它可以基于业务平台快速落地。
|
||||
|
||||
**业务平台化是模块依赖关系层次化的一个特例,只是它偏向于基础能力,在实践中,当业务线很多,业务规则很复杂时,我们经常把底层业务能力抽取出来,进行平台化处理。**
|
||||
|
||||
## 总结
|
||||
|
||||
好了,下面我来总结一下今天所讲的内容。
|
||||
|
||||
首先,我们对系统进行建模,系统=模块+关系,这样会简化你对系统的认识。基于这个模型,我们对模块划分和关系定义提出具体的要求,你可以在实际设计时参考这些要求。
|
||||
|
||||
另外,我们深入地分析了扩展性的本质。系统的扩展能力来自于内部模块体系的有序,这样才能低成本地应对业务变化,认识到了这一点,有助于你从根本上理解和重视架构的扩展性设计。
|
||||
|
||||
然后,我提供了一个出行平台的例子,来帮助你理解,如何通过模块拆分和整合的手段,具体地设计一个可扩展的架构,希望你能在工作中灵活运用。
|
||||
|
||||
**最后,给你留一道思考题**:你所在公司里有没有类似的肿瘤系统,它包含了太多职责,导致系统内部结构混乱,大家都不敢对它进行调整?
|
||||
|
||||
欢迎在留言区和我互动,我会第一时间给你反馈。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。
|
||||
152
极客时间专栏/架构实战案例解析/业务架构篇/04 | 可扩展架构案例(一):电商平台架构是如何演变的?.md
Normal file
152
极客时间专栏/架构实战案例解析/业务架构篇/04 | 可扩展架构案例(一):电商平台架构是如何演变的?.md
Normal file
@@ -0,0 +1,152 @@
|
||||
<audio id="audio" title="04 | 可扩展架构案例(一):电商平台架构是如何演变的?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/79/74/79c4b48ea8478c86b886fb53b996b574.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。
|
||||
|
||||
上一讲中,我们介绍了如何打造一个可扩展的架构。今天,我就针对最近十几年电商平台的架构变化过程,来具体说明下,为了支持业务的快速发展,架构是如何一步步演进的。
|
||||
|
||||
从2003年淘宝上线开始,国内电商平台经历了高速的发展,在这个过程中,系统遇到了很多的挑战,比如说:
|
||||
|
||||
- 如何针对当前的业务现状,选择合适的架构呢?
|
||||
- 如何在业务发展过程中,升级改造架构,并保证系统的平滑过渡呢?
|
||||
|
||||
接下来,我会结合自己的工作实践,和你一起探讨架构的演变历程,你可以从中了解到各种架构的优劣点和适用性,然后在实际工作中选择合适的架构。
|
||||
|
||||
这里,我总结了国内电商平台架构发展的大致过程,你可以结合图片参考下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/18/f1/1819615457ee2aaef36242287c8a16f1.jpg" alt="">
|
||||
|
||||
我们可以看到,从最初的单体架构到最新的中台架构,架构的可扩展性越来越强,这些都是系统不断适应业务复杂化的结果。下面,我就结合电商业务的变化,按照顺序和你介绍下各个架构。因为篇幅的原因,对于中台架构,我会放在后面的文章里重点介绍。
|
||||
|
||||
## 单体架构
|
||||
|
||||
在单体架构中,只有一个应用,所有代码跑在一个进程,所有的表放在一个DB里。第一代电商平台都是单体架构,比如说淘宝,在最初的3年,它的系统就是一个巨大的单体应用。
|
||||
|
||||
单体应用内部一般采用分层结构,从上到下,一般分为表示层、业务层、数据访问层、DB层。表示层负责用户体验,业务层负责业务逻辑,数据访问层负责DB的数据存取。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a1/34/a12ac37db91a57a5d3314f1e35fa6c34.jpg" alt="">
|
||||
|
||||
我们可以看到,各个层的职责,正好对应业务处理的不同阶段,所以,单体架构在水平方向上,通过层次化的划分,降低了业务的深度复杂性(所谓的业务深度,指的是业务流程从开始到结束的长度)。
|
||||
|
||||
不过在垂直方向上,单体应用缺乏清晰的边界,上下层模块之间是多对多的网状依赖关系,比如业务层的某个模块(上图中BO1),可能调用数据访问层的所有模块(DAO1~3), 同样的道理,数据访问层的某个模块,也可能被业务层的所有业务模块给调用。
|
||||
|
||||
所以,单体架构中的模块只是在逻辑上独立,并没有在物理上严格分开,导致系统在落地时,模块的职责和边界划分比较随意,相应地,模块之间的依赖关系也比较模糊。所以,**在单体架构中,模块结构是否合理,很大程度上依赖于开发者的个人水平。**
|
||||
|
||||
在电商发展的初期,业务并不复杂,比如前台的首页、搜索页、详情页、结算页等,页面的功能都比较简单,可以放在一个应用里处理,这样,使用单体架构就可以快速落地系统。但当业务开始变得复杂时,每个页面都发展为一个独立的业务体系,比如说首页,它原先展示相对固定的内容,现在发展为一个动态的千人千面系统。
|
||||
|
||||
这样一来,业务的复杂度急剧上升,模块的数量也大幅度增加了,我们就很难在单体架构里,通过构建一个清晰的模块体系来支持系统的扩展。而且,所有代码放在一个代码库里管理,如果多团队并行开发的话,很容易发生代码冲突,这样也难以满足系统的快速扩展。
|
||||
|
||||
举个例子,07年的时候,eBay网站总体上也是一个单体应用,它的核心工程有数百万行代码,由于代码合并和编译非常复杂,他们甚至有专门的团队负责代码合并,有专门的团队负责编译脚本开发,另外还有一套复杂的火车模型,来协调不同团队之间的并行开发和上线。
|
||||
|
||||
所以,当业务系统的体量变大时,单体架构的弊端就充分暴露出来了,我们就需要对系统进行有效的拆分,比如把首页、搜索页、详情页、结算页拆成一个个独立的应用,分别进行管理。于是,分布式架构就应运而生了。
|
||||
|
||||
## 分布式架构
|
||||
|
||||
分布式架构,简单来说就是系统由多个独立的应用组成,它们互相协作,成为一个整体。
|
||||
|
||||
分布式架构包括了多个应用,每个应用分别负责不同的业务线,当一个应用需要另一个应用的功能时,会通过API接口进行调用。在分布式架构中,API接口属于应用的一部分,它和表示层共享底层的业务逻辑,你可以认为,API相当于应用在实现本身业务的基础上,开了个小窗口,给外部使用。
|
||||
|
||||
关于分布式的具体架构,你可以参考下图:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/31/8b/31232dae641f735182171b1290345f8b.jpg" alt="">
|
||||
|
||||
你可以看到,分布式架构在单体应用的基础上,进一步对系统按照业务线,进行了业务广度上的切分(所谓业务广度,指的是不同业务线的数量),这样就把一个大系统的业务复杂度,分割成多个小业务的复杂度,从而降低了整体的复杂度。通过拆分后,各个应用之间的耦合度低,就可以很好地支持多团队的并行开发。
|
||||
|
||||
但分布式架构也有局限性,作为应用的开发者,除了要满足自身业务的需求之外,同时还需要考虑外部业务的需求,这两部分经常会打架。比如,由于自身业务的需求,引起底层的业务逻辑修改,这时会同时影响API接口功能,导致其他业务受影响;同样的道理,外部业务需求过来,需要API接口做调整,即使不影响底层业务逻辑,也会导致整个应用重新部署,影响自身业务的稳定性。
|
||||
|
||||
另外,在分布式架构下,每个应用都是从头到尾,自搭一套完整的体系,导致业务之间重复造轮子,造成资源浪费。举个例子,在2008年,淘宝还没有开始服务化改造之前,不同业务线的用户、商品、订单逻辑非常类似,导致了整个系统有超过1/3的核心代码重复。
|
||||
|
||||
所以,你可以发现,**分布式架构适用于业务相关性低、耦合少的业务系统**。举个例子,企业内部的管理系统,分别服务于不同的职能部门,比如财务系统和HR系统,就比较适合按照分布式架构去落地。
|
||||
|
||||
但在电商场景下,业务都是围绕交易展开的,各个页面(应用)都需要和商品、用户、订单、库存打交道,对于这样业务相互依赖、应用之间需要紧密协作的场景,在系统架构方面,是否有更好的手段,可以更高效地集成这些应用呢?
|
||||
|
||||
答案是有的,SOA架构就可以有效地解决这个问题。 接下来,我们就具体了解下。
|
||||
|
||||
## SOA架构
|
||||
|
||||
SOA架构(Service Oriented Architecture)是一种面向服务的架构,它的发展经历了两个阶段:传统的SOA架构,它解决的是企业内部大量异构系统集成的问题;新的SOA架构,它解决的是系统重复建设的问题。下面我就来和你详细介绍一下。
|
||||
|
||||
从2000年开始,很多传统企业进入了信息化建设高潮,先后采购了很多系统,比如ERP、OA、CRM等等。这些系统都是由不同的供应商提供的,落地后,就形成了很多的信息孤岛。随着业务的发展,企业需要打通这些不同的系统,那么问题来了,这些系统使用不同的技术,事先也没有提供开放接口给外部使用,那我们如何才能有效地集成这些系统呢?
|
||||
|
||||
解决的办法是,每个系统首先把外部需要的能力,封装为一个个粗粒度的接口,打包成一个独立的服务;然后,外部系统通过这个服务访问系统内部,解决不同系统相互集成的问题。经过这样的改造,系统最后就变成了一个面向服务的SOA架构。
|
||||
|
||||
这就是一个传统的SOA架构,如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8a/71/8ab0eb6f1e4a96928f2b7cdc34da8371.jpg" alt="">
|
||||
|
||||
你可以看到,在SOA架构中,每个服务都对应一个现有的系统,所有这些服务都部署在一个中心化的平台上,我们称之为企业服务总线ESB(Enterprise Service Bus),ESB负责管理所有调用过程的技术复杂性,包括服务的注册和路由、各种通信协议的支持等等。
|
||||
|
||||
比如说,09年的时候,eBay就基于Axis 2,开发了自己的SOA框架,让各个系统通过提供标准的服务,来满足外部调用需求。 比如后台搜索系统,本身是C++开发的,但是它通过提供Java服务,封装常见的搜索功能,就方便了其他系统(大多是Java)和搜索系统进行集成。
|
||||
|
||||
以上讲的是传统SOA架构,它主要用于解决遗留系统的集成问题。而新的SOA架构,它利用服务共享的思想,解决系统的重复开发问题。
|
||||
|
||||
举个淘宝的例子,淘宝的系统基本是自建的,系统相互打通的问题不大。但经过一段时间的自然生长,系统重复建设的问题很突出,前面也提到,有超过1/3的核心代码重复。针对这种情况,我们就可以通过服务化手段,把通用的逻辑和数据从各个业务系统里抽取出来,封装成独立的服务,提供给所有业务进行共享。
|
||||
|
||||
基于这个思路,淘宝花了2~3年时间,先后落地了用户、商品、订单、库存、店铺、营销等服务,搭建了共享服务体系。通过共享,淘宝不仅提升了开发效率和质量,也加强了系统的扩展能力。
|
||||
|
||||
新的SOA架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/fc/5f/fcb0c3b41c2578927332e3241663535f.jpg" alt="">
|
||||
|
||||
所以我们可以看到,相对于分布式架构,SOA架构给系统的扩展带来了一系列的好处:
|
||||
|
||||
- 首先,它通过服务化思想,提供更好的业务封装性,并通过标准技术,能更友好地对外输出业务能力;
|
||||
- 其次,SOA服务不依附于某个具体应用,它可以独立地部署和扩展,这样避免了直接影响现有的系统;
|
||||
- 最后,服务通过封装通用的业务逻辑,可以供所有应用共享,解决了重复造轮子的问题。
|
||||
|
||||
不过,虽然SOA服务化的思想很好,但在系统实现上比较重,落地比较困难。**那有没有更轻量级的架构,使得系统各个部分更容易构建和相互协作呢?**
|
||||
|
||||
这时候,微服务架构便悄悄地登场了。
|
||||
|
||||
## 微服务架构
|
||||
|
||||
关于微服务,大家都不陌生,但究竟什么是微服务,每个人的理解可能都不一样。接下来,我就基于自己的服务化实践,和你分享我的看法。
|
||||
|
||||
微服务概念的提出,一开始是用来和单体架构做区分的。我们知道,单体架构和分布式架构,实际上都是围绕一个大的业务线来构建应用,当业务变得复杂时,就无法做到模块边界和依赖关系的清晰划分,模块局部的调整往往会导致系统整体的调整,使得系统很难扩展。
|
||||
|
||||
**而微服务围绕更小的业务单元构建独立的应用。**
|
||||
|
||||
比如说,一个飞机航班预订系统,我们可以把它划分为预订航班、时间表查询、计算票价、分配座位等几个小应用(微服务)来落地。那么经过划分后,每个小应用都比较简单,只关注于一个业务功能即可。
|
||||
|
||||
这里要注意的是,每个微服务,都是负责端到端的业务,包括前端的UI展现部分和后端业务逻辑。微服务的团队成员可能包括产品、开发、测试、运维等人员,由这个小团队负责应用的整个生命周期管理。
|
||||
|
||||
因此,从一定程度上说,微服务叫做微应用,或者说微产品,更合适一点,你也可以认为微服务架构是拆分得更细的分布式架构。
|
||||
|
||||
另外,微服务强调围绕业务,进行清晰的业务和数据边界划分,并通过良好定义的接口输出业务能力,这和SOA架构里的服务有点类似。**但两者不同的地方在于,微服务是去中心化的,不需要SOA架构中ESB的集中管理方式。**
|
||||
|
||||
一方面,**微服务强调所谓的哑管道**,即客户端可以通过HTTP等简单的技术手段,访问微服务,避免重的通信协议和数据编码支持。另一方面,**微服务强调智能终端**,所有的业务逻辑包含在微服务内部,不需要额外的中间层提供业务规则处理。
|
||||
|
||||
这样子,微服务提供方可以自由地选择语言和工具,来落地微服务,服务的部署和维护上也更灵活,从这个意义上来说,你也可以认为微服务是轻量级的SOA服务。
|
||||
|
||||
所以说,微服务兼有应用和服务的特征,你可以把微服务理解为:
|
||||
|
||||
>
|
||||
**微服务=小应用+小服务。**
|
||||
|
||||
|
||||
以上就是微服务架构设计的初衷,但在实践中,我们更多地把微服务当做一个小服务,而不是一个端到端的小应用,那么为什么会这样呢?这里有几个原因。
|
||||
|
||||
首先,我们很难把一个大系统,按照端到端业务的方式,拆分为一个个应用;而拆分为服务是比较灵活的,我们可以把系统核心的业务逻辑和数据封装成服务,其它部分还是以应用的方式落地。另一方面,微服务要求团队人员跨多个职能,构建独立的小团队,来负责服务完整的生命周期,这就需要把现有的职能团队打散后重组,这种人员组织的调整实际上也很难落地。
|
||||
|
||||
我们可以看到,微服务强调围绕端到端的小业务功能,通过组建跨职能的团队,来进行落地,这只是一种理想化的做法。所以,**在实践中,我们往往弱化微服务的小应用定位,然后扩大化微服务小服务的定位,我们不再强调端到端的业务封装,而是可以有各种类型的微服务。**
|
||||
|
||||
比如说,封装底层基础业务的是共享微服务,封装流程的是聚合微服务,封装具体业务场景的服务端是应用微服务,封装基础中间件(如Redis缓存、消息推送)的是系统微服务。当然,这些服务在具体落地时,我们还是采取去中心化的机制,使用轻量级的通讯框架,最后把它们打造成一个个技术上轻量级的、功能职责上细分的微服务。
|
||||
|
||||
所以,基于这样的思路,微服务就很容易构建,同时,也像水电煤一样,容易被我们使用。然后,我们在这个基础上组装微服务,像搭积木一样搭建系统,这样的系统更具弹性,更容易扩展。
|
||||
|
||||
**值得注意的是,我们需要对服务依赖关系进行有效的管理,打造一个有序的微服务体系**。否则的话,东一个服务,西一个服务,这样会让系统变得碎片化,难以维护和扩展。
|
||||
|
||||
所以我这里也放了一张图,来帮助你理解,一个有序的层次化微服务体系大致是什么样子的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/4f/05/4f361c17295a651ccc1b49bae6219005.jpg" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我与你分享了电商平台架构的发展过程,从单体架构到分布式架构,再到SOA架构和微服务架构,每种架构都针对前一种架构的缺点做了改进,架构的扩展性也变得越来越好,可以满足更高的业务复杂性要求。
|
||||
|
||||
但值得注意的是,每种架构都有两面性,既有优点,又有缺点,在实际系统中,这些架构也都是并存的。**架构没有最好,只有最合适的**。我们做架构设计时,一定要根据当前业务的特点,选择合适的架构。
|
||||
|
||||
通过今天的分享,相信你对架构的扩展性有了更深入的理解,也能够根据公司的业务现状,进行更合理的架构选型了。
|
||||
|
||||
**最后,给你留个思考题**:现在人人都在落地微服务,你在这方面有什么经验和教训吗?
|
||||
|
||||
欢迎你在留言区与大家分享你的答案,如果你在学习和实践的过程中,有什么问题或者思考,也欢迎给我留言,我们一起讨论。感谢阅读,我们下期再见。
|
||||
199
极客时间专栏/架构实战案例解析/业务架构篇/05 | 可扩展架构案例(二):App服务端架构是如何升级的?.md
Normal file
199
极客时间专栏/架构实战案例解析/业务架构篇/05 | 可扩展架构案例(二):App服务端架构是如何升级的?.md
Normal file
@@ -0,0 +1,199 @@
|
||||
<audio id="audio" title="05 | 可扩展架构案例(二):App服务端架构是如何升级的?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/91/8f/91da4ad31d9050258af8ac557edb148f.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。
|
||||
|
||||
上一讲,我与你介绍了电商平台从单体架构到微服务架构的演变过程。那么今天,我会通过一个**1号店App服务端架构改造**的例子,来具体说明架构的演变过程,让你能更深入地理解架构演变背后的原因。
|
||||
|
||||
好,先让时间拨回到2012年,当时随着智能设备的普及和移动互联网的发展,移动端逐渐成为用户的新入口,各个电商平台都开始聚焦移动端App。这个时候,1号店也开始试水移动端购物,从那时起,1号店App的服务端架构一共经历了三个版本的变化。
|
||||
|
||||
接下来,我就为你具体介绍App服务端架构变化的过程以及原因。
|
||||
|
||||
## V1.0架构
|
||||
|
||||
我先说说最开始的1.0版本。当时的情况是,App前端的iOS和Android开发团队是外包出去的,而App的服务端是由1号店内部一个小型的移动团队负责的,这个团队主要负责提供App前端需要的各个接口,接口使用的通信协议是HTTP+JSON。
|
||||
|
||||
具体的架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/1c/45/1c2cc4298788d157851d08b5a49e9b45.jpg" alt="">
|
||||
|
||||
这个架构比较简单,App的服务端整体上就一个应用,由移动团队来维护所有对外接口,服务端内部有很多Jar包,比如商品搜索、商品详情、购物车等等,这些Jar包包含了各个业务线的业务逻辑及数据库访问,它们由各个业务线的开发者负责提供。
|
||||
|
||||
你可以看到,这个1.0版本的服务端,实际上就是一个单体应用,只是对外的接口和内部Jar包分别由不同的团队来提供,这个架构的优点和缺点同样都非常明显。
|
||||
|
||||
**它的优点是简单方便**。App前端的外包团队只需要对接后端的一个移动团队就可以了,然后移动团队通过现成的Jar包,封装各个业务线的功能。至于这些Jar包,业务线团队也无需额外去开发。
|
||||
|
||||
为什么呢?我们知道,早期的电商平台都是先有PC端应用,再推App,App最开始的功能,大多是从已有的PC端平移过来的。因此,这些Jar包直接从PC端应用里拿过来就可以了,如果Jar包版本有更新,由业务线团队直接同步给移动团队即可。
|
||||
|
||||
那这个架构设计是不是很完美啊?当然不是,不知道你发现了没有,其实这里也存在了很多问题。
|
||||
|
||||
### 第一个问题:移动服务端对Jar包的紧密依赖
|
||||
|
||||
移动团队负责对外接口,但他们非常依赖业务团队提供的Jar包来实现业务逻辑,这是一种物理上的紧耦合依赖关系。
|
||||
|
||||
如果业务团队根据PC端的需求,修改了应用代码后,Jar包也会随之修改。那么在实践中,经常会出现这样的情况:业务团队很多时候,要么忘了同步新的Jar包给移动团队,要么是新的Jar包调整了类的接口,导致了App服务端的功能有问题,或者直接不可用。
|
||||
|
||||
### 第二个问题:移动团队的职责过分复杂
|
||||
|
||||
服务端为App提供的是粗粒度接口,而业务团队的Jar包提供的是细粒度的接口。
|
||||
|
||||
因此,移动团队在Jar包的基础上,还需要做很多的业务逻辑聚合,很多时候,这些逻辑还跨多个业务线,导致移动团队对所有业务逻辑都要深入了解。相信你也知道,这是很难做到的。
|
||||
|
||||
### 第三个问题:团队并行开发困难
|
||||
|
||||
由于移动团队和业务团队是通过物理Jar包进行集成的,移动团队直接受业务团队的代码影响,就导致了团队之间并行开发困难,一次大的App升级经常需要2~3个月的时间。
|
||||
|
||||
而当时的1号店,需要能尽快地推出App端,我们所有的做法都是围绕这个目的来的,包括把前端团队外包出去,后端采用单体架构,移动端功能从PC端直接移植过来。所以,从当时的情况来说,这种简单的服务端架构和团队合作模式是非常合适的。
|
||||
|
||||
而过了一段时间,当移动端的功能已经初步具备,我们就需要针对移动自身的特点去组织功能,并能够快速上线这些新功能。那么,这种单体架构加物理Jar包耦合的方式,就成为App进一步发展的瓶颈。
|
||||
|
||||
接下来,我们就看下系统是如何通过架构升级,来解决这个问题的。
|
||||
|
||||
## V2.0架构
|
||||
|
||||
到了2013年,1号店App服务端架构升级到了V2.0。在这个时候,1号店自己接手了App前端的开发工作,同时,服务端接口也由各个业务线团队直接负责,这样,App前端直接对接多个后端应用提供的HTTP接口。
|
||||
|
||||
整体架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/2e/a5/2e91cb605d041f27523d7183aae053a5.jpg" alt="">
|
||||
|
||||
对于各个业务团队来说,他们现在走向了前台,每个团队负责各个业务线的App接口。他们一般采取这样的做法,一方面,他们以Web应用的方式,为PC端浏览器提供访问;另一方面,针对移动端的访问需求,他们在Web应用里面,增加了一些REST接口,直接供App访问。在这里,移动接口和Web应用在同一个工程里开发,作为同一个应用进行部署和运行。
|
||||
|
||||
这里你可以看到,这实际上就是一种**分布式的系统架构**,每块业务由不同的团队负责,可以很好地支持团队之间的并行开发;同时,移动接口和PC端共享底层业务逻辑,有助于快速把PC端的功能完整地复制到App端。
|
||||
|
||||
这样,**通过V2.0架构的升级,业务线团队的生产力就被完全释放了,App的功能也就快速丰富起来了。**
|
||||
|
||||
但这种方式也带来了一系列的问题,我们具体说下。
|
||||
|
||||
**首先是移动端和PC端互相干扰的问题。**
|
||||
|
||||
你可以看到,在同一个业务线内部,移动接口和Web应用,物理上是绑定在一起的。很多时候,PC端的代码修改会影响到移动接口,而Web应用的发布,也会导致移动接口被动地被发布,如果PC端出现功能问题,也会影响到移动接口的可用性。反过来也是一样的,移动接口的需求变化,会影响到PC端的功能。
|
||||
|
||||
我们知道,当移动端发展到了一定程度,它需要和PC端有不同的功能和用户体验,但这种紧耦合的方式,导致了相互之间产生很多不必要的干扰,对系统的功能和稳定性都带来了负面影响。
|
||||
|
||||
**其次是重复开发的问题。**
|
||||
|
||||
移动接口除了要给App端提供业务数据,还需要考虑一系列系统级的功能,比如说,安全验证、日志记录、性能监控等等,每个移动接口都需要这些通用功能。
|
||||
|
||||
那现在,由于App前端是和后端直连的,这就意味着,每个后端系统都需要独自去支持这些系统级的功能,导致了各个后端系统重复开发。一旦这些通用需求发生了变化,比如说,我们要对传输数据进行压缩,那么,所有的后端系统都需要同步调整,这样不但工作量很大,而且也给项目管理也带来了很大的挑战。
|
||||
|
||||
**最后是稳定性的问题。**
|
||||
|
||||
在这里,基于这种直连方式,只要一个后端系统出问题,就会直接影响到App的可用性,使得App整体上非常的脆弱。
|
||||
|
||||
之所以会出现以上这些问题,它的根本原因在于,我们在App端,直接照搬了PC端的做法,没有针对移动端自身的特点,去做架构设计。
|
||||
|
||||
我们知道,当App发展到一个成熟阶段时,无论是业务功能,还是非业务性功能,和PC端都是不同的。所以,在架构设计上,我们必须能够支持它们各自不同的特点,根据这个思路,我们的App服务端架构也演变到了V3.0版本。
|
||||
|
||||
## V3.0架构
|
||||
|
||||
在V3.0版本中,服务端架构包含了两个大的升级。
|
||||
|
||||
**首先,我们对每个业务线的服务端进行拆分,让App接口和PC端接口各自在物理上独立,但它们共享核心的业务逻辑。**
|
||||
|
||||
拆分后的架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b3/bd/b3ad4bbf52ed59e676d488642ffab4bd.jpg" alt="">
|
||||
|
||||
这样拆分的结果是,原来大的服务端变成了3个应用,包括一个App端接口应用,一个PC端Web应用,还有一个核心业务逻辑服务,3个部分都是独立维护和部署的。
|
||||
|
||||
**除此之外,架构改造还考虑了移动端自身的特点。**
|
||||
|
||||
一方面,每个移动端接口需要调用对应的后台服务,进行业务逻辑处理,这个是个性化的,每个接口的处理逻辑都不一样;另一方面,每个移动端接口都需要进行系统级的功能处理,比如前面所说的安全验证、接口监控等,这个是共性的,每个接口的处理方式都是一样的。
|
||||
|
||||
那么,在架构上,我们就需要把共性的系统级功能进行集中处理,把个性化的业务功能进行分散处理。
|
||||
|
||||
**最后,我们结合服务端的应用拆分,以及对移动接口本身的改造,落地了服务端V3.0架构。**
|
||||
|
||||
如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d0/ec/d0e1b34d0f908391e1253fd1f93689ec.jpg" alt="">
|
||||
|
||||
在这里,App前端会通过**移动网关**来访问服务端接口。这里的网关主要就是负责处理通用的系统级功能,包括通信协议适配、安全、监控、日志等等;网关处理完之后,会通过接口路由模块,转发请求到内部的各个业务服务,比如搜索服务、详情页服务、购物车服务等等。
|
||||
|
||||
对于PC端浏览器来说,它直接访问对应的Web应用,如搜索应用、详情页应用等,然后这些应用也是访问同样的内部服务。
|
||||
|
||||
>
|
||||
这里说明下,当时还没有流行前后端分离,所以PC端有对应的Web应用,同时负责业务逻辑和UI展现。
|
||||
|
||||
|
||||
现在,你已经了解了V3.0版本的整体架构设计,接下来,我们就深入移动网关,去具体了解下它的内部实现机制。
|
||||
|
||||
### 移动网关的内部实现
|
||||
|
||||
在图中,你可以看到,整个移动网关分为三层,自上而下分别是通用层、接口路由层、适配层,接下来我们逐一分析。
|
||||
|
||||
**通用层**
|
||||
|
||||
首先是通用层,它负责所有系统级功能的处理,比如通讯协议适配、安全、监控、日志等等,这些功能统一由网关的通用层进行预处理,避免了各个业务线的重复开发。
|
||||
|
||||
在具体实现时,每个通用功能的处理逻辑都会封装成一个拦截器,这些拦截器遵循统一的接口定义,并且拦截器都是可配置的。当有外部请求过来,网关会依次调用这些拦截器,完成各个系统级功能的处理。
|
||||
|
||||
这个拦截器接口的定义如下:
|
||||
|
||||
```
|
||||
Object filter(Object input)throws Exception
|
||||
|
||||
```
|
||||
|
||||
**接口路由层**
|
||||
|
||||
接下来是接口路由层。移动端请求经过通用层的预处理之后,将会进一步分发给后端的业务适配器进行处理。
|
||||
|
||||
我们在配置文件里,对接口请求的URL和业务适配器进行映射,接口路由层的分发逻辑就是根据请求中的URL,在配置文件里找到对应的适配器,然后把请求交给适配器进行后续的处理。
|
||||
|
||||
配置文件的具体内容如下所示:
|
||||
|
||||
```
|
||||
www.website.com/search SearchAdapter
|
||||
www.website.com/detail DetailAdapter
|
||||
|
||||
```
|
||||
|
||||
**服务适配层**
|
||||
|
||||
最后是服务适配层。我们知道,外部接口的请求格式,往往和内部服务接口的格式是不一样的。具体到1号店当时的情况,外部接口是HTTP+JSON格式,内部服务是Hessian+二进制格式。
|
||||
|
||||
适配器首先用来解决内外部接口的适配,除此之外,适配器还可以根据需要,对多个内部服务做业务聚合,这样可以对App前端提供粗粒度的接口服务,减少远程网络的调用次数。
|
||||
|
||||
这些适配器遵循统一的接口定义:
|
||||
|
||||
```
|
||||
Object adapter(Object input)throws Exception
|
||||
|
||||
```
|
||||
|
||||
这些适配器物理上是Jar包的形式,由各个业务线研发团队提供,所有的适配器会集中部署在网关,而网关本身可以支持多实例的部署,通过水平扩展的方式提升服务端的处理能力。
|
||||
|
||||
现在,你已经很清楚了V3.0架构的实现细节,接下来,我们就深入看下,这次架构升级达到了什么样的实际效果。
|
||||
|
||||
### 架构的实际效果
|
||||
|
||||
**首先,App端和PC端彻底独立了**。在上面的图中,我们可以看到,App前端和PC端浏览器是完全对等的,PC端浏览器有自己的服务端,App前端也有自己的服务端,在这里,移动网关就充当App服务端的角色。
|
||||
|
||||
在这个架构下,两个服务端都可以针对自身的特点,独立开发,独立部署,无论在逻辑层面还是物理层面都实现了彻底解耦。我们知道,一开始,App是依附于PC端,而现在,它终于可以独立地发展了。
|
||||
|
||||
**其次,通过架构改造,实现了核心业务的复用**。这里,我们把核心的业务逻辑从Web应用中剥离出来,变成了共享的服务。在服务设计时,我们不再区分PC端还是移动端,而是从业务本身出发,提供一套通用的接口,同时供PC端和移动端调用,从而实现了底层业务逻辑的复用。
|
||||
|
||||
**还有,这个架构强化了系统级功能**。原来通用的系统级功能,由各个团队各自去提供,很多团队要么不提供,要么实现的方式不一样;现在的系统级功能,是由集中式的移动网关统一来提供,我们就可以很方便地强化这些系统级功能。
|
||||
|
||||
举个例子,我们可以把通信协议由HTTP升级为更安全的HTTPS,当后端服务有问题时,也可以通过网关进行事先的数据缓存,直接返回给App前端。比如说商品的详情数据,就很适合这样的处理。
|
||||
|
||||
所以,有了移动网关,整个App的可用性、稳定性和安全性都得到了大幅度的提升。
|
||||
|
||||
**最后,团队分工也更明确了**。在这里,移动团队主要负责移动网关,包括网关本身和各种过滤器的维护,他们可以针对移动端的特点,做各种系统级功能的优化;而业务团队,主要负责各自的业务逻辑,包括适配器和底层服务。移动团队和业务团队通过明确的适配接口进行协作,相互不影响。
|
||||
|
||||
我们可以看到,V3.0在V2.0分布式架构的基础上,通过服务化改造,实现了基础业务的复用;同时,通过移动网关落地系统级功能,实现了系统的平台化改造。
|
||||
|
||||
**总的改造结果就是,解放了业务线,提升了系统的稳定性,使得移动端可以做大做强。**
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我与你分享了1号店App服务端架构改造的实际例子。在这个例子中,架构经历了单体架构到分布式架构,再到SOA架构的变化过程,并且通过移动网关的方式,一定程度上实现了平台化。
|
||||
|
||||
在这里,你可以清晰地看到,公司每个阶段的业务,都有它不同的特点,我们选择的架构必须能够适配它,**过度设计和设计不足,同样都是有害的。**
|
||||
|
||||
通过今天的分享,相信你对各种架构的优缺点,以及业务上的适用性有了更进一步的了解。他山之石,可以攻玉。架构的策略和原则是通用的,希望你能够通过实战不断去领会和运用。
|
||||
|
||||
**最后,给你留一道思考题**:你都做过哪些系统改造,改造前是什么架构,改造后又是什么架构,过程中有哪些挑战呢?
|
||||
|
||||
欢迎在留言区和我互动,我会第一时间给你反馈。如果觉得有收获,也欢迎你把这篇文章分享给你的朋友。感谢阅读,我们下期再见。
|
||||
166
极客时间专栏/架构实战案例解析/业务架构篇/06 | 可扩展架构案例(三):你真的需要一个中台吗?.md
Normal file
166
极客时间专栏/架构实战案例解析/业务架构篇/06 | 可扩展架构案例(三):你真的需要一个中台吗?.md
Normal file
@@ -0,0 +1,166 @@
|
||||
<audio id="audio" title="06 | 可扩展架构案例(三):你真的需要一个中台吗?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/34/ba/34a6dbb60a9c4e4feb98931a4d2e58ba.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。前面的课程,我们从单体架构开始,讲到了微服务,今天我们就接着讲最新的中台架构。
|
||||
|
||||
关于中台,最近比较火,你可能也听到过不少关于它的讨论,但中台究竟是什么?它能解决什么问题?相信你不一定非常清楚。今天,我就为你解决这些困惑。
|
||||
|
||||
讲中台之前,我们先来理解下前台和后台,这样,你才能更清楚中台的定位。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7e/b2/7e8f2b10eba1efbe492ca6b10d080ab2.jpg" alt="">
|
||||
|
||||
**前台**比较好理解,指的是**面向C端的应用**,比如像微信、淘宝这样的应用。不过,你要注意,前台不仅仅是指前端,它还包含和前端配套的服务端。
|
||||
|
||||
**后台**指的是**企业内部系统**,比如ERP、CRM、仓库管理系统等等,主要是面向企业内部人员使用。对于传统企业来说,之前只有线下场景,通过内部的后台就能完成所有业务流程;而对于互联网企业,或者逐步开展线上业务的传统企业来说,同时需要前台和后台,一起协作,完成业务的闭环。
|
||||
|
||||
但问题是,前台和后台的特性是不一样的。**前台对外**,我们知道,消费者的需求快速多变,所以前台需要能快速响应,做到低成本试错;而**后台对内**,企业内部的业务流程不能经常变,所以后台需要稳定,不能随意调整,一旦改动,影响面广,成本很高。
|
||||
|
||||
简单地说,前台要快,后台要稳,因此在业务扩展时,我们经常会遇到以下两类挑战:
|
||||
|
||||
- 这个营销思路很棒,老板希望能马上验证,前台好改,但后台调整起来需要好几个月;
|
||||
- 后台系统技术旧,性能差,接口不开放,前台对接起来很麻烦,而且一有促销活动,后台立马就挂。
|
||||
|
||||
第一类挑战,在互联网企业比较普遍,前台经常玩各种花样,要求快;第二类挑战,在传统企业很典型,大量的后台都是早期采购的商业套件,新的线上应用很难直接对接内部老系统。
|
||||
|
||||
你可以发现,前台和后台是企业IT系统的一体两面,它们需要紧密协作,共同服务于企业的业务战略。但两者对业务稳定性的要求不一样,在技术上也普遍存在脱节现象。
|
||||
|
||||
**所以,如何实现前后台的平滑对接,这是一个巨大的挑战,中台架构因此而生。**
|
||||
|
||||
接下来,我会结合自己在中台方面的实践,和你深入聊下中台的定位,以及具体的中台架构,让你可以轻松应对这个挑战。
|
||||
|
||||
## 中台的定位
|
||||
|
||||
讲中台前,我先举一个你比较熟悉的Windows系统的例子:<br>
|
||||
<img src="https://static001.geekbang.org/resource/image/cc/ab/cc4f9f8a5807e03e84ee74c1680087ab.jpg" alt="">
|
||||
|
||||
在Windows系统里,最上面是各种桌面应用,比如Office套件等,这些是用户能够直接看到的部分;最底下是各种硬件设备,比如磁盘、内存、CPU等;中间是操作系统,它处于软硬件之间。
|
||||
|
||||
我们知道,理论上,桌面应用可以直接操作底层硬件,完成所需要的功能。比如,我们用低级的汇编语言去开发应用,就可以通过端口来直接操作硬件。但很显然,这种开发方式的效率很低,代码的可读性和可维护性也很差。
|
||||
|
||||
但是,如果我们在中间加上一层操作系统,通过操作系统向下管理硬件,屏蔽各种硬件的差异和复杂性,向上提供简洁的API接口,我们就可以使用各种高级语言,通过调用API,很方便地操作硬件了。
|
||||
|
||||
在这个里面,操作系统在底层硬件和上层应用之间,起到了很好的衔接作用。
|
||||
|
||||
我们就对照Windows操作系统的例子,来看下传统企业的IT系统。比如说麦当劳,它经过多年的信息化建设,购买了大量的商业套件,如总部使用的ERP、门店使用的收银系统等等,这些系统都属于后台的范畴,面向企业内部管理,针对的是传统的线下业务。
|
||||
|
||||
现在,随着麦当劳的业务发展,要往新零售转型,比如说,他们要提供线上小程序点餐服务,为消费者创造更好的用户体验。
|
||||
|
||||
但是,这个小程序点餐服务不是孤立的,它离不开内部系统的支撑。比如,小程序展示的菜品来自于后台ERP;小程序下的订单,会进入门店的收银系统和厨房作业系统。
|
||||
|
||||
那么问题来了,**这些C端应用,与内部后台系统要如何打通呢?**
|
||||
|
||||
理论上,C端的应用也是可以直接调用后台老系统来实现打通的,比如在麦当劳的例子中,小程序服务端可以直接调用ERP获取菜品信息,提供给小程序前端进行展示。但这个和Windows系统里的桌面应用直接控制硬件设备类似,这里前后台的直接对接是非常低效的。
|
||||
|
||||
我们知道,小程序服务于C端,ERP服务于B端,ERP建设在前,小程序建设在后。ERP系统在实施的时候,完全没有考虑小程序点餐场景,两者在业务流程、数据模型、技术栈、性能要求等方面,差异都很大,导致直接的对接非常困难。
|
||||
|
||||
而且,如果有新的C端场景进来,又要从头到尾对接一遍,重新吃一遍苦。这是一种硬着陆的方式,如果新业务上线采取这种方式,那至少需要好几个月时间,根本无法满足业务快速创新的要求。
|
||||
|
||||
这时,如果有个中间层来负责C端应用与内部后台系统的平滑衔接,帮助新的C端应用软着陆,这样就会非常高效。这里我对比了操作系统和新零售中台,如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f2/b9/f2da313161166386baa79f054c1d68b9.jpg" alt="">
|
||||
|
||||
以麦当劳为例,如果我们对内部老系统进行包装,对外提供标准的API,这样就能把旧的IT基础设施,转换成面向互联网的业务平台。然后,新的C端应用可以快速基于这个业务平台来构建,而不用关心底层老系统的实现细节。**这个中间层就是中台。**
|
||||
|
||||
你可以看到,中台相当于企业的商业操作系统,通过对后台的包装,为前台提供全方位的支持。这里,需要注意的是,中台不仅仅是前后台之间简单的适配器,中台本身也会落业务数据,有完整的业务规则,就像Windows操作系统一样,它在适配硬件的基础上,进一步提供内存管理、进程调度等功能,为上层应用提供体系化的支持。
|
||||
|
||||
对于互联网企业来说,前后台虽然是同时建设的,它们在功能上能够衔接起来,但前台求快,后台求稳。所以在这里,中台可以先承接前台的业务和数据,和前台构成C端业务的小闭环,支持业务的快速创新,等业务模式验证后,中台和后台再进一步彻底打通,构成业务的大闭环。
|
||||
|
||||
现在你已经了解了中台的定位,可能会想,**企业处于什么样的发展阶段,需要落地中台呢?**
|
||||
|
||||
接下来,我就结合一个出行平台的发展过程,来说明中台的适用性,让你能够在合适的时机选择落地中台。
|
||||
|
||||
## 中台的适用性
|
||||
|
||||
一个出行平台,当公司发展从0到1的阶段时,往往只有一条业务线,比如说出租车业务,我们直接根据它的需求落地系统即可。随着公司发展到从1到n的阶段时,业务线会逐渐增加,比如增加了快车、顺风车等业务。
|
||||
|
||||
这时,从系统落地的角度,我们有两种做法。
|
||||
|
||||
**第一种是独立地建设新业务线,这样,各个业务线并列,系统整体上是一个“川”字型的结构。**
|
||||
|
||||
如下图左边部分所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/51/45/511406013d3ae63a2a15acfaa1ba3545.jpg" alt="">
|
||||
|
||||
但是,如果各个业务线的业务逻辑非常类似,子系统之间会有大量的代码复制,这就会导致重复建设以及多头维护的问题。显然,这是非常低效的,本来我们想能尽快上线新的业务线,但结果是欲速而不达。
|
||||
|
||||
**第二种做法是,把各业务线中相同的核心逻辑抽取出来,通过抽象设计,实现通用化,共同服务于所有业务线的需求,系统结构整体上是一个“山”字型。**
|
||||
|
||||
“山”字型的上面三竖,代表各个业务线定制的应用;最底下一横,代表通用层,它把各个业务线有机粘合在一起,实现了业务逻辑和业务规则的统一,如上图中的右边所示。
|
||||
|
||||
**这样,我们就能一处建设,多处复用,一处修改,多处变化,从而实现最大程度的复用。**
|
||||
|
||||
那我们什么时候,需要从“川”字型转为“山”字形呢?
|
||||
|
||||
- 一方面,这和公司业务线的**数量**有关,业务线越多,意味着重复建设的成本会更大,当我们开始上第3条业务线时,就应该要考虑转到“山”字形了。
|
||||
- 另一方面,也和各个业务线的**相似度**有关,相似度越高,意味着业务线之间有更多类似的逻辑,更适合“山”字形。比如,出行平台的各个出行方式相似度很高,适合“山”字形;但同一个公司的出行业务和互联网金融业务,差异比较大,就可以考虑“川”字形,而没必要把它们强行扭在一起。
|
||||
|
||||
所以说,中台实现了通用基础业务的平台化。从**变化速度**来看,企业基础的业务是相对固定的,而具体上层业务场景是相对多变的;从**数量**来看,基础业务数量是有限的,而具体业务场景是无限的。因此,有了完善的中台,我们就可以通过有限而比较固定的基础业务,来满足无限而快速变化的上层业务场景了。
|
||||
|
||||
此外,从**业务角度**来看,中台收敛了业务场景,统一了业务规则;从**系统角度**看,中台相当于操作系统,对外提供标准接口,屏蔽了底层系统的复杂性;从**数据角度**看,中台收敛了数据,比如使用同一套订单数据模型,让所有渠道的订单使用相同的订单模型,所有订单数据落到同一个订单库。
|
||||
|
||||
那么用一句话总结就是,**中台通过实现基础业务的平台化,实现了企业级业务能力的快速复用。**
|
||||
|
||||
好,接下来,我们就一起深入中台,具体了解下中台架构设计的细节。
|
||||
|
||||
## 如何落地一个中台架构?
|
||||
|
||||
通过[课程之前的分享](https://time.geekbang.org/column/article/205832),你应该对微服务架构比较熟悉了,我也提到了中台架构紧跟着微服务架构,那么中台和微服务架构到底有什么区别和联系呢?
|
||||
|
||||
简单地说,我认为**中台是微服务的升级。**
|
||||
|
||||
在微服务架构下,我们搭建的是一个个离散的服务,如商品服务、订单服务等等。而在中台里,这些微服务升级为了商品中心、订单中心,每个中心更强调体系化,包括更好的业务通用能力,更好的系统运营能力(如监控、稳定性、性能的强化),更好的业务运营能力(比如商品中心自带配套的商品管理后台)。
|
||||
|
||||
每个服务中心都围绕核心业务,自成体系,成为一个微内核,这些微内核形成一个有机整体,共同构成了基础业务平台,也就是中台。**松散的微服务->共享服务体系->中台**,这是微服务架构向中台架构的演进过程。
|
||||
|
||||
现在大家谈论比较多的是业务中台,那我们就来具体看下一个典型的业务中台的结构。它一般包含三层,从上到下分别是**通用聚合服务层**、**通用基础业务平台**和**通用中间件平台**。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/11/87/115a36d4713c02698c215d6af6fdb187.jpg" alt="">
|
||||
|
||||
对于中台来说,基础业务能力由通用基础业务平台来实现;另外,通用聚合服务对基础业务进行组合,进一步提升了业务能力的易用性;而通用中间件平台,通过技术手段保证了业务中台的稳定性,三者一起实现了企业整体业务能力的复用。
|
||||
|
||||
那么关于具体如何落地中台,互联网企业和传统企业的侧重点则有所不同。
|
||||
|
||||
- 对于大的**互联网企业**来说,系统已经是类似于“山”字型的结构,进化到中台,更多的是各个基础服务点上的强化和面上的整合。
|
||||
- 对于**传统企业**来说,系统基本上是“川”字型的结构,大量独立的商业套件组成遗留系统,落地中台是一个革命性的动作。
|
||||
|
||||
所以接下来,我就主要分析下传统企业如何落地中台,这样更能体现出中台的价值和落地的挑战。
|
||||
|
||||
首先,如下图所示,我们看下典型的传统企业中台架构设计是什么样的。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e9/b1/e90d7eb2cc9118bf50e3d2d600245bb1.jpg" alt="">
|
||||
|
||||
**你可以看到,整个中台架构从上到下分为四个层次:**
|
||||
|
||||
### 渠道&应用
|
||||
|
||||
渠道&应用层,这是整个系统的**对外部分**,包括了各个应用的前端,如App、小程序、公众号等等,这些是需要定制的部分。同时,在对外部分,我们还会提供Open API,供上下游企业调用。
|
||||
|
||||
### 应用平台
|
||||
|
||||
应用平台是各个具体应用的**母体**,它包含了各个应用的服务端,比如小程序服务端、App服务端等等,这些服务端会针对具体场景,做流程编排和信息的聚合。
|
||||
|
||||
服务端和前端之间还有一个**网关**,网关实现前后端隔离,具体负责外部访问的安全验证和监控,以及内外部请求的路由和消息格式转换。
|
||||
|
||||
### 业务中台
|
||||
|
||||
业务中台是中台架构的**核心**,它包括一系列的通用基础服务,以及它上面的通用聚合服务和下面的技术平台,这个在前面已经详细介绍过了,我就不赘述了。
|
||||
|
||||
### 后台
|
||||
|
||||
后台包括两部分,第一部分是**适配插件**,用于连接商户内部系统和中台基础服务,比如,在中台的商品服务和后台ERP之间同步商品数据,在中台的会员服务和后台CRM之间同步会员信息。一般针对每个内部系统,都有一个适配插件,它起到了类似硬件驱动程序的作用,这个一般是定制化的。第二部分是**企业内部系统**,这个是企业的IT基础设施,业务最终会在这里落地。
|
||||
|
||||
OK,通过以上的介绍,你可以清晰地看到,**中台代表了企业核心的业务能力,它自成体系,能够为C端的互联网场景提供通用的能力,并通过各种插件和后台打通**。这样,经过中台的通用化和后台的插件适配后,我们最终就把企业的后台老系统,包装成一个面向互联网的平台,可以快速地给C端赋能。
|
||||
|
||||
## 总结
|
||||
|
||||
中台是从企业的业务战略高度,来考虑企业IT系统的建设,它的目标是实现企业整体业务能力的复用。从落地的角度看:
|
||||
|
||||
- **对于互联网企业来说**,有大量微服务做基础,往中台转是改良,目的是更好地衔接前台和后台,实现业务的快速创新;
|
||||
- **对于传统企业来说**,内部有大量的遗留系统,落地中台是革命,目的是盘活老系统,全面实现企业的数字化转型。
|
||||
|
||||
互联网发展到现在,从最初的电商,到O2O,再到现在的产业互联网,已经进入了深水区,很多传统企业都面临着数字化转型的挑战。架构上往中台转型,落好中台,真正发挥中台的价值,这将是一个长期的过程,也是企业业务复杂化的必然结果。
|
||||
|
||||
通过今天的分享,相信你对中台有了更深入的理解,对是否要往中台转型,你也能够做出更好的判断了。
|
||||
|
||||
**最后,给你留一道思考题**:现在中台很热,我们经常听到很多中台名词,它们分别是什么定位呢?
|
||||
|
||||
欢迎你在留言区与大家分享你的答案,如果你在学习和实践的过程中,有什么问题或者思考,也欢迎给我留言,我们一起讨论。感谢阅读,我们下期再见。
|
||||
118
极客时间专栏/架构实战案例解析/业务架构篇/07 | 可复用架构:如何实现高层次的复用?.md
Normal file
118
极客时间专栏/架构实战案例解析/业务架构篇/07 | 可复用架构:如何实现高层次的复用?.md
Normal file
@@ -0,0 +1,118 @@
|
||||
<audio id="audio" title="07 | 可复用架构:如何实现高层次的复用?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/cc/8a/cc17aecb9cc046adab6e4945aa8b1e8a.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。在前面几讲中,我们讨论了如何打造一个可扩展的架构,相信你对架构的可扩展有了一定的了解,而架构还有一个非常重要的目标,那就是可复用。所以从今天开始,我就来和你聊一聊,如何打造可复用的架构。
|
||||
|
||||
作为开发人员,你对复用这个概念一定不陌生。在开发过程中,我们把系统中通用的代码逻辑抽取出来,变成公共方法或公共类,然后在多个地方调用,这就是最简单的技术上的复用。
|
||||
|
||||
但一开始,我们不会过多地考虑复用,当一个新项目过来,我们会选择最直接的方式来实现,结果往往是欲速而不达,比如说:
|
||||
|
||||
- 好不容易搞定了一个项目,接着又有新的类似项目过来,我们又要从头再来;
|
||||
- 项目的代码是定制的,项目结束后,系统维护的噩梦刚刚开始。
|
||||
|
||||
如果项目缺乏沉淀,每个项目都是全新的开始,出现这些情况,一点都不意外。而要想解决这个问题,我们一开始就要考虑系统的复用性。
|
||||
|
||||
**复用,它可以让我们站在巨人的肩膀上,基于现有的成果,快速落地一个新系统。**
|
||||
|
||||
那么,我们在做架构设计时,如何实现系统的高可复用呢?
|
||||
|
||||
今天,我就针对复用这个话题,首先和你介绍一下,复用具体都有哪些形式;然后,我会针对最有价值的业务复用,带你了解如何划分服务的边界,让你能够在工作中,设计一个可以高度复用的系统。
|
||||
|
||||
## 复用的分类
|
||||
|
||||
复用有多种形式,它可以分为技术复用和业务复用两大类。**技术复用**包括代码复用和技术组件复用;**业务复用**包括业务实体复用、业务流程复用和产品复用。
|
||||
|
||||
从复用的程度来看,从高到低,我们可以依次划分为产品复用>业务流程复用>业务实体复用>组件复用>代码复用。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7f/4c/7f1f6ba6fda64d49f29f5ebac518b24c.jpg" alt="">
|
||||
|
||||
接下来,我就按照复用度从低到高,对这些复用方式进行一一分析,帮助你更好地理解架构的可复用性。
|
||||
|
||||
### 技术复用
|
||||
|
||||
首先是**代码级复用**,这部分应该是你最熟悉的了。这里包括你自己打包的类库,第三方提供的SDK,还有各种算法封装等。我们的代码可以直接调用它们,物理上也和我们的应用打包在一起,运行在同一个进程里。代码级复用是最低层次的复用,你可以把它当作你自己源代码的一部分。
|
||||
|
||||
再往上,是**技术组件复用**。这些组件有我们自己封装的,更多的是大量开源的中间件,比如Redis、MQ、Dubbo等;组件也包括各种开发框架,比如Spring Cloud。这些基础组件技术复杂度很高,它们的存在,极大地简化了我们的开发工作。
|
||||
|
||||
**值得注意的是,代码级复用和技术组件复用都属于工具层面,它们的好处是在很多地方都可以用,但和业务场景隔得有点远,不直接对应业务功能,因此复用的价值相对比较低。**
|
||||
|
||||
### 业务复用
|
||||
|
||||
我们知道,系统最终是为业务而服务的,如果能够实现直接的业务复用,那系统开发的效率就更高。在前面的课程中,我们讨论架构的演进过程时,很多地方谈到了业务能力的复用,比如说,[微服务](https://time.geekbang.org/column/article/205832)强调单个业务实体的封装和复用,而[中台](https://time.geekbang.org/column/article/207121)进一步实现了企业级业务能力的复用。
|
||||
|
||||
所以接下来,我们就从比较简单的业务实体复用开始说起。
|
||||
|
||||
**业务实体复用针对细分的业务领域**,比如订单、商品、用户等领域。它对各个业务领域的数据和业务规则进行封装,将它变成上层应用系统可以直接使用的业务组件。
|
||||
|
||||
**业务流程的复用针对的是业务场景**,它可以把多个业务实体串起来,完成一个端到端的任务。比如说,下单流程需要访问会员、商品、订单、库存等多个业务,如果我们把这些调用逻辑封装为一个下单流程服务,那下单页面就可以调用这个流程服务来完成下单,而不需要去深入了解下单的具体过程。相比单个的业务实体复用,业务流程的复用程度更高,业务价值也更大。
|
||||
|
||||
**最高层次的复用是对整个系统的复用**,比如说一个SaaS系统(Software-as-a-Service),它在内部做了各种通用化设计,允许我们通过各种参数配置,得到我们想要的功能;或者说一个PaaS(Platform-as-a-Service)平台,它会提供可编程的插件化支持,允许我们“嵌入”外部代码,实现想要的功能。
|
||||
|
||||
这种产品级的复用,它的复用程度无疑是最高的。这样的系统,在落地的时候,它无需核心的开发团队进行开发,只由外围的实施团队负责就可以了,这样,一个项目的上线就能简化为一次快速的实施,不但上线周期短,系统也更稳定。
|
||||
|
||||
当然,实现这样的复用,难度也是很大的,你既要对所在行业的业务有很全面的理解,又要有很强的抽象设计能力。这类系统中,比较典型的有Salesforce的CRM系统和SAP的ERP系统。
|
||||
|
||||
现在,我们先对复用做个总结。**从技术复用到业务复用,越往上,复用程度越高,复用产生的价值也越大,但实现起来也越复杂,它能复用的场景就越有限。**在实际工作中,技术层面上的复用相对比较简单,我们对这部分的认知也最多,而且由于开源的普及,现在有丰富的中间件让我们选择,我们可以基于它们,逐步构建适合自己的技术体系。
|
||||
|
||||
**但如果我们能进一步打造业务中间件,并在这个基础上,形成业务平台,这样,我们就能实现更高的业务级复用,可以更高效地支持系统的快速落地。**
|
||||
|
||||
而在实现业务组件化和平台化的过程中,首要的问题就是基础服务边界的划分。边界划分决定了服务的粒度和职责,在实际工作中,也是非常困扰我们和有争议的地方。
|
||||
|
||||
接下来,我就针对基础服务边界的划分,和你分享我自己在项目开发的过程中,总结的一些实用的原则和做法。
|
||||
|
||||
## 基础服务边界划分
|
||||
|
||||
服务边界划分要解决“我是谁”的问题,它实现了服务和周边环境的清晰切割。
|
||||
|
||||
我们都知道,服务包含了业务数据和业务规则,并提供接口给外部访问,其中,接口是服务的对外视图,它封装了服务的业务数据和规则。
|
||||
|
||||
所以从边界划分的角度来看,我们就是要确定哪些数据属于这个服务,哪些接口功能由这个服务提供。这里,我总结了3个基础服务边界划分的原则,供你设计时做参考。
|
||||
|
||||
### 首先,是服务的完整性原则
|
||||
|
||||
你在划分服务的边界时,需要确保服务内部数据的完整性。
|
||||
|
||||
举个例子,一个商品服务的数据模型,不仅要有商品基本信息,比如商品名称、价格、分类、图片、描述等;还需要包含商品的扩展信息,如商品的各种属性、商品标签等;最后还要包含各种复杂商品类型的定义,比如组合商品、套餐商品、多规格商品等。
|
||||
|
||||
另外,你还要保证服务功能的完整性。对于服务使用者来说,他们是以业务的角度看服务,而不是纯粹的数据角度。比如一个套餐商品,在服务内部,它是多个单品的复杂组合,但从服务调用者的角度来看,它就是一个商品。
|
||||
|
||||
那现在问题来了,对于套餐的价格,商品服务是给出一个最终价格呢?还是给出各个单品的价格,然后让调用方自己算最终价格呢?我们知道,套餐的价格不是各个单品价格累加的结果,它包含了一定的优惠,如果它的价格由服务调用方来算,这会导致商品的部分业务规则游离于服务外面,破坏了商品服务的功能完整性。
|
||||
|
||||
在实践中,有些服务只是存储基础数据,然后提供简单的增删改查功能,这样一来,服务只是一个简单的DAO,变成了数据访问通道。这样的服务,它的价值就很有限,也容易被服务调用方质疑。因此,我们要尽可能在服务内部封装完整的业务规则,对外提供完整的业务语义,最大程度地简化服务的使用。
|
||||
|
||||
**所以,当你在划分服务边界时,要保证服务数据完整、功能全面,这样才能支撑一个完整的业务领域。**
|
||||
|
||||
### 其次,是服务的一致性原则
|
||||
|
||||
也就是说,服务的数据和职责要一致,谁拥有信息,谁就负责提供相应的功能。
|
||||
|
||||
服务内部的业务逻辑要尽量依赖内部数据,而不是接口输入的数据,否则会造成数据和业务规则的脱节(一个在外面,一个在里面),如果服务对外部的依赖性很强,就无法提供稳定的能力了。
|
||||
|
||||
很多时候,我们对一个功能到底划分到哪个服务,有很大的争议。这时,我们可以结合这个功能所依赖的数据来判断,如果功能所需要的大部分数据都存储在A服务里,那当然由A服务来提供接口比较合适,这样接口输入的数据比较少,不但简化了服务对外部的依赖,同时也降低了接口调用的成本。
|
||||
|
||||
给你举个例子,在订单小票上,我们经常能看到一些优惠信息,比如说商品原价是多少,其中因为满减优惠了多少,因为商品特价减免了多少。这个优惠计算的结果是订单的一部分,毫无疑问,它需要保存在订单服务里。
|
||||
|
||||
但这个订单的优惠计算过程,却不是由订单服务来负责,而是由独立的促销服务负责的。因为优惠计算所需要的优惠规则是在促销服务里定义的,促销服务可以在内部拿到所有的优惠规则,然后完成整个优惠计算。
|
||||
|
||||
否则,如果是由订单服务负责优惠计算,订单服务的调用者就需要在接口中提供完整的促销规则,不但调用成本高,而且外部促销规则的改变会影响订单服务的内部实现。
|
||||
|
||||
所以在这里,促销服务负责促销规则的维护,以及对应的优惠计算功能;订单服务负责优惠结果数据落地,以及后续的查询功能。这样,每个服务存储的数据和对外提供的功能是一致的。
|
||||
|
||||
### 最后一个,是正交原则
|
||||
|
||||
既然是基础服务,它们就处于调用链的底层,服务之间不会有任何的调用关系,也就是说基础服务相互之间是正交的。比如说会员服务和商品服务,它们代表不同维度的基础业务域,彼此之间不会有调用关系。
|
||||
|
||||
正交还有另外一种情况:服务之间有数据的依赖关系,但没有接口的调用关系。
|
||||
|
||||
比如说,订单明细里包含商品ID信息,但订单服务内部不会调用商品服务来获取商品详情。如果页面需要展示订单的商品详情,针对这个具体的业务场景,我们可以在上层的聚合服务里,通过聚合订单服务和商品服务来实现。
|
||||
|
||||
## 总结
|
||||
|
||||
可复用是架构设计的一个重要目标,今天我们对复用进行了梳理,包括复用有哪些形式,以及它们有哪些价值,相信你现在对复用已经有了一个整体的认识。**业务上的复用比纯粹的技术复用有更高的价值,我们要尽量往这个方向上靠。**
|
||||
|
||||
在实践中,落地基础服务是实现业务复用的有效方式,而基础服务边界的划分,它有科学的成分,但更多的是一种艺术,这里我提供了几个实用的划分原则,你可以在工作中结合实际情况,灵活地运用它们。
|
||||
|
||||
在专栏的下一讲,我会通过一个具体的订单服务例子,来帮助你更好地落地基础服务。
|
||||
|
||||
**最后,给你留一道思考题:我们在落地服务时,有时会冗余存储其它服务的数据,你对这个有什么看法呢?**
|
||||
|
||||
欢迎你在留言区与大家分享你的答案,如果你在学习和实践的过程中,有什么问题或者思考,也欢迎给我留言,我们一起讨论。感谢阅读,我们下期再见。
|
||||
167
极客时间专栏/架构实战案例解析/业务架构篇/08 | 可复用架构案例(一):如何设计一个基础服务?.md
Normal file
167
极客时间专栏/架构实战案例解析/业务架构篇/08 | 可复用架构案例(一):如何设计一个基础服务?.md
Normal file
@@ -0,0 +1,167 @@
|
||||
<audio id="audio" title="08 | 可复用架构案例(一):如何设计一个基础服务?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9b/ef/9bd028030105cbe0b2c92985b8f254ef.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。
|
||||
|
||||
在上一讲中,我提到过,在架构设计中,要实现业务上的复用,一个比较可行的做法是,把各个基础业务封装成共享服务,供上层所有应用调用。所以今天,我就来和你聊一聊,如何从头开始,落地这样一个典型的共享服务。
|
||||
|
||||
我们知道,落地一个微服务其实并不困难,但要实现一个能够高度复用的共享服务并不容易,在落地过程中,经常会有一系列的问题困扰着我们。
|
||||
|
||||
- 我们事先对服务的边界没有进行很好的划分,结果在落地的过程中,大家反复争论具体功能的归属。
|
||||
- 由于对业务的了解不够深入,我们要么设计不足,导致同一个服务有很多版本;要么服务过度设计,实现了一堆永远用不上的功能。
|
||||
|
||||
**对于落地一个共享服务来说,服务边界的划分和功能的抽象设计是核心。**服务边界确定了这个服务应该“做什么”,抽象设计确定了这个服务应该“怎么做”。
|
||||
|
||||
接下来,我就以一个**实际的订单服务例子**,为你详细讲解一下要如何重点解决这两个问题。这样你可以通过具体的案例,去深入地理解如何落地共享服务,实现业务能力的复用。
|
||||
|
||||
## 订单业务架构
|
||||
|
||||
不同企业的订单业务是不一样的,所以这里我先介绍下这个订单的业务场景。
|
||||
|
||||
这是个O2O(Online To Offline,线上到线下)的交易业务,订单的来源有两个,一个是自有小程序或App过来的订单,还有一个是外卖平台过来的订单,然后这些线上的订单会同步到门店的收银系统进行接单和进一步处理。这里我放了一张订单的业务架构图,你可以到文稿中看下:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/f6/f5/f60dec4aff0201e54f8a509046e0aef5.jpg" alt="">
|
||||
|
||||
在这里,订单服务是和4个应用直接打交道的:
|
||||
|
||||
- **小程序服务端**调用订单服务落地自有线上订单;
|
||||
- **外卖同步程序**接收三方外卖平台的订单,然后调用订单服务落地订单;
|
||||
- ** POS同步程序**通过订单服务拉取订单,并推送给商户内部的收银系统;
|
||||
- 最后还有一个**订单管理后台**,通过订单服务查询和修改订单。
|
||||
|
||||
OK,接下来,我们就具体看下,如何从头开始落地这个订单服务。
|
||||
|
||||
## 订单服务边界划分
|
||||
|
||||
首先,我们要确定这个服务的边界,这是进行服务内部设计的前提。划分边界时,你需要对相关的业务场景有充分了解,并且在一定程度上,能够预测潜在的需求。在[上一讲](https://time.geekbang.org/column/article/207876),我也和你分享了划分边界一些比较实用的原则和做法,你可以对照学习一下。
|
||||
|
||||
根据业务场景的分析,这个订单服务需要负责三个方面的功能。
|
||||
|
||||
### 基本信息管理
|
||||
|
||||
首先是订单基本信息管理,主要提供订单基础信息的增删改查功能,包括下单用户、下单商品、收货人、收货地址、收货时间、堂食或外卖、订单状态、取餐码等。
|
||||
|
||||
另外,你需要注意的是,这里有多个下单渠道,除了通用的订单信息,每个渠道还有特定的渠道相关信息,比如堂食的订单要有取餐码、外卖的订单要有收货人和收货地址等等,这个都需要在我们的数据模型里给出定义。
|
||||
|
||||
### 订单优惠管理
|
||||
|
||||
然后是订单优惠管理功能,这对应的是订单的小票信息,从最开始的商品金额,到最后需要用户实际支付的金额,中间会有一系列的折扣和减免,这些都是属于订单信息的一部分。这些信息我们需要展示给用户看,如果后续要进行订单成本的分摊,也需要用到它。
|
||||
|
||||
### 订单生命周期管理
|
||||
|
||||
最后是订单的生命周期管理功能,主要负责管理订单的状态变化。我们知道,从不同下单渠道过来的订单,它的状态变化过程是不一样的;不同行业的订单,它的状态变化过程也是不同的,所以**订单服务的状态要做到通用**,能够支持各种可能的状态定义和状态转换过程。这个也是订单服务设计的难点,我在后面会重点介绍。
|
||||
|
||||
好了,现在我们已经给出了订单服务的功能。**为了更好地定义边界,在实践中,你还需要澄清哪些功能不属于服务**,这样可以避免后续的很多争论。所以在这里,我会进一步给出订单服务不包括的功能,你在划分自己的服务边界时最好也能够明确给出。
|
||||
|
||||
**第一,作为基础服务,订单服务不主动调用其他服务。**
|
||||
|
||||
比如说,你想了解订单的用户详情、商品详情等等,这应该由上层应用通过调用相应的服务来实现,然后和订单信息组装在一起,而不是在订单服务内部直接调用其他服务,否则会导致基础服务之间相互依赖,职责模糊。
|
||||
|
||||
如果说这个信息整合的场景非常通用,我们可以创建一个在基础服务之上的聚合服务来实现,把订单信息、用户信息、商品信息整合在一起。
|
||||
|
||||
**第二,订单服务不负责和第三方系统的集成。**
|
||||
|
||||
在这里,订单需要在我们的订单服务和三方外卖平台,以及收银系统之间进行同步,这些同步功能都是针对第三方系统定制的,不具有通用性。而我们的订单服务作为基础服务,需要具备通用性,因此这些和外部系统对接的功能不会在订单服务的内部实现,而是由额外的同步程序实现。
|
||||
|
||||
>
|
||||
小提示:这些同步程序可以主动调用订单服务,然后再和第三方对接,如果想实时获取订单信息的变化,同步程序可以订阅订单服务的消息通知,第一时间了解订单变化。
|
||||
|
||||
|
||||
**第三,订单服务不提供优惠计算或成本分摊逻辑。**
|
||||
|
||||
订单服务不负责具体的优惠计算,只提供优惠结果的存储和查询,用于还原订单的费用组成。优惠的具体计算过程一般由专门的促销系统负责,成本的分摊一般由后续的财务系统负责。这个我们在上一讲中已经说过,这里就不详细解释了。
|
||||
|
||||
**最后,该服务不提供履单详情,不负责详细物流信息的存储。**
|
||||
|
||||
比如说,订单已经发送至上海、订单已经到达某某快递站等等这些信息,订单服务不负责提供这些详细信息,这些都是属于后续履单系统的职责。订单服务可以存储一些外部系统的单据号码,比如配送单号,这样能方便上层应用通过订单记录和配送系统进行关联,获取配送的详细信息。但订单服务只负责存储,不负责数据的进一步解释。
|
||||
|
||||
到这里,你可以看到,通过从正反两个方面说明订单服务的职责,我们就得到了一个边界很清晰、职责很聚焦的订单服务边界,所有人对它的职责认识是一致的,尽可能地避免了后续的争论。
|
||||
|
||||
## 订单服务内部设计
|
||||
|
||||
好,确定了这个**订单服务要做什么**之后,接下来,我们要解决的就是**服务内部怎么做**的问题了。
|
||||
|
||||
作为共享服务,我们要保证订单服务功能上的通用性,就需要同时对内部数据模型和外部接口进行良好的抽象设计。
|
||||
|
||||
### 订单状态通用化
|
||||
|
||||
对于数据模型来说,订单要存储哪些信息,已经比较明确了,具体你可以看下这个图。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a0/83/a0976cc0a2fee60922a9253809a57183.jpg" alt="">
|
||||
|
||||
但对于如何管理订单的状态,情况就比较复杂了。
|
||||
|
||||
我们知道,如果针对一个具体的项目,无论它的订单状态有多么的复杂,我们都可以事先精确地定义出来。**但不同的行业甚至不同的企业,他们对于订单状态管理都是不一样的,订单服务作为一个共享服务,它必须要满足不同项目的订单状态管理。**所以对于如何解决这个问题,这里我有两个思路供你参考。
|
||||
|
||||
**一个是开放订单状态定义。**
|
||||
|
||||
在这里,订单服务事先不限定订单有哪些状态,每个项目都可以自己定义有哪些订单状态。服务的调用方可以在接口里传递任意的状态值;订单服务只负责保存状态数据,不负责解释具体的状态,也不负责任何的规则校验,它允许订单从一个状态转换为其他任意的状态。
|
||||
|
||||
这样的设计,在理论上可以满足各种状态的定义,满足各种状态之间的变化,但这样做其实有很大的问题。在这里,订单状态是完全由外部负责管理的,上层应用的负担会很重,不但要负责定义有哪些状态,而且还要维护状态的转换规则,一不小心,订单可能从状态A非法地变成状态B,导致业务出问题。
|
||||
|
||||
**另外一个是应用和服务共同管理状态。**
|
||||
|
||||
对于订单状态管理,应用和服务各自承担一部分职责,我们看下具体如何实现。
|
||||
|
||||
我们知道,无论订单的状态变化是如何的复杂,我们总是可以定义一个订单有哪些基本的状态,包括这些基本状态之间是如何变化的。比如,订单一开始都是用户下单后待支付,支付完成后变成一个有效的订单,然后由商家进行接单,制作完成后进行发货配送等等,订单最终的状态要么是完成,要么是取消。
|
||||
|
||||
**这些订单的基本状态,我们称之为“主状态”,它们由订单服务负责定义**,包括这些主状态之间的转换规则,比如已完成的订单不能变为已取消的订单。主状态的数量是比较有限的,状态之间的变化关系也是比较明确的。
|
||||
|
||||
这个主状态,我们对大量现有的业务场景进行总结和抽象,是完全可以定义出来的。在这个订单服务例子里,我们定义了如下图所示的订单状态机,包括有哪些主状态,以及它们的转化关系。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/0d/74/0d773b0d18e318b2b5260a0430c42c74.jpg" alt="">
|
||||
|
||||
**订单除了“主状态”,还有“子状态”。**
|
||||
|
||||
比如,一个订单处于配送中,实际情况可能是“仓库已发货”,“货已到配送站”,或者是“快递员正在送货中”等等,那么在这些情况中,订单的主状态都是“配送中”,它的子状态就是细化的这几种情况。**子状态有哪些具体的取值,不同的项目是不一样的,这个就开放给各个应用来定义。**
|
||||
|
||||
所以,订单服务数据模型里有**两个字段**,其中的主状态由订单服务负责管理,包括主状态之间的变化规则;而子状态由上层应用来定义,管理子状态的变化规则,比如一个配送中的订单,它的子状态可以由“仓库已发货”,变为“快递员正在送货中”。
|
||||
|
||||
现在,我们就可以总结下这两种订单状态的设计思路。
|
||||
|
||||
**第一种方案,我们不对订单状态进行管理**,而是把订单的状态作为一个简单的属性存储,只支持订单状态简单的增删改查功能。我们知道,订单状态是订单业务规则的核心体现,这样的订单服务是没有灵魂的,也失去了大部分业务复用的价值。
|
||||
|
||||
**第二种方案,应用和服务共同管理订单的状态**,订单服务抓大放小,通过主状态管理把控住了订单的核心业务规则,同时把子状态开放给应用进行管理,为具体的业务场景提供了灵活性。通过主状态和子状态的结合,订单服务就满足了不同行业、不同企业的订单状态管理需求。
|
||||
|
||||
### 订单服务接口定义
|
||||
|
||||
说完了订单的状态管理,接下来,我们从调用方怎么使用服务的角度,来看下订单服务外部接口是如何设计的。
|
||||
|
||||
外部系统和服务的交互有**两种方式**,包括同步的服务接口调用和异步的消息通知。
|
||||
|
||||
**首先是同步的服务接口调用。**
|
||||
|
||||
为了方便外部调用方,我们在服务接口命名时,一定要规范和统一,接口名字要能够望文生义,方便调用者快速找到所需要的接口。并且,我们还要提供接口具体的请求和响应样例帮助说明。
|
||||
|
||||
具体的接口设计规范,我就不具体展开了,每个公司都要有明确的规范要求,这里我就说下常见的查询接口是如何设计的。
|
||||
|
||||
一个订单有很多字段,每次调用方要查询的信息可能都不相同,不同字段之间的组合方式有很多,我们不可能一一支持。
|
||||
|
||||
**那么,我们怎么设计查询接口,来满足各种场景需求呢?**一般来说,我们可以根据返回字段数量的不同,提供三个不同粒度的查询接口来满足多样化的需求。
|
||||
|
||||
第一个是**粗粒度接口**,只返回订单最基本的7-8个字段,比如订单编号、订单状态、订单金额、下单用户、下单时间等等;第二个是**中粒度接口**,返回订单比较常用的十几个字段;第三个是**细粒度接口**,返回订单的详细信息。
|
||||
|
||||
这样,不同的查询需求,就可以根据要返回信息的详细程度,来选择合适的接口,通过这种方式,我们兼顾了要定义的接口数量和查询的性能。
|
||||
|
||||
**其次是异步的消息通知。**
|
||||
|
||||
订单服务除了提供同步的接口调用,还针对每次订单信息的变化,提供异步的消息通知,感兴趣的外部系统可以通过接收消息,第一时间感知订单的变化。
|
||||
|
||||
**按照消息详细程度的不同,订单消息可以分为“胖消息”和“瘦消息”。**
|
||||
|
||||
顾名思义,**胖消息**包含了尽可能多的字段,但**传输效率低**;**瘦消息**只包含最基本的字段,**传输效率高**。如果外部系统需要更多的信息,它们可以通过进一步调用订单服务的接口来获取。
|
||||
|
||||
在这个订单服务的例子里,如果是订单状态的变化,我们只需提供订单号、变化前后的状态即可,因此主要以瘦消息为主;如果是新订单的创建,由于订单的字段比较多,所以使用胖消息,避免外部系统进一步调用订单服务接口。你在实践中,可以根据实际情况,在消息的数据量和消费者处理消息的复杂度之间做平衡。
|
||||
|
||||
前面我们说了,订单服务不会主动调用外部系统的接口,这里的异步消息通知,就可以很好地保证外部系统及时感知订单的任何变化,同时避免订单服务和外部系统直接耦合。
|
||||
|
||||
## 总结
|
||||
|
||||
要想打造一个可高度复用的共享服务,你需要掌握最核心的两点:**清晰的边界划分、内部的抽象设计。**
|
||||
|
||||
今天,我通过一个实际的订单服务例子,帮助你理解如何清晰地定义服务的边界,以及如何通过抽象设计保证服务的通用性。你在实践中,一定要深入分析业务场景,识别真正的挑战在哪里,避免设计的简单化或过度复杂化。
|
||||
|
||||
通过今天的讲解,相信你在前一篇理论内容的基础上,对如何打造一个共享服务有了更深入的体会,希望你在工作中能不断地去实践,真正掌握这些技能。
|
||||
|
||||
**最后,给你留一道思考题:**在落地共享服务的时候,你碰到过哪些挑战,都是怎么解决的?
|
||||
|
||||
欢迎你在留言区与大家分享你的答案,如果你在学习和实践的过程中,有什么问题或者思考,也欢迎给我留言,我们一起讨论。感谢阅读,我们下期再见。
|
||||
130
极客时间专栏/架构实战案例解析/业务架构篇/09 | 可复用架构案例(二):如何对现有系统做微服务改造?.md
Normal file
130
极客时间专栏/架构实战案例解析/业务架构篇/09 | 可复用架构案例(二):如何对现有系统做微服务改造?.md
Normal file
@@ -0,0 +1,130 @@
|
||||
<audio id="audio" title="09 | 可复用架构案例(二):如何对现有系统做微服务改造?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/4e/f7/4e6e289aa5f4eb9f7bcf146327bbddf7.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。在上一讲中,我以订单服务为例,和你一起讨论了如何从头开始,设计一个共享服务。今天我们再来聊一聊:**如何对现有系统做微服务化改造**。
|
||||
|
||||
很多早期的互联网公司都有巨大的单体应用,底层的数据表集中放在一个数据库里,这些表加起来可能有几百张。对于这样的应用系统和数据库,我们往往需要对它们进行拆分,通过微服务化改造,保证系统能够不断地扩展和复用。
|
||||
|
||||
相比从头开始落地服务,对现有系统做微服务化改造,这会面临更多的挑战。
|
||||
|
||||
首先,应用和数据表紧密耦合在一起,代码模块和表是多对多的依赖关系。一个模块会访问多张表,多个模块也会对同一张表进行访问,而且由于表都在一个数据库里,开发人员往往会随意对表做关联,有时候甚至Join 5~6张表以上。这样,代码模块和表之间的关系是剪不断,理还乱,我们很难清晰地划分代码和数据表的边界,也就很难把它们封装成独立的微服务。
|
||||
|
||||
还有,系统现在已经在运行了,我们的改造不能影响业务的稳定性。那微服务落地后,现有的系统要怎么对接微服务,数据要怎么迁移,才能保证系统的平滑过渡呢?
|
||||
|
||||
所以,要想应对这些挑战,一方面,我们要保证比较合理的服务设计,才能达到优化系统架构的目的;另一方面,我们要做到整个过程对现有系统的影响比较小,才能达到系统改造顺利落地的目的。
|
||||
|
||||
接下来,我就以1号店库存服务化改造为例,让你深入理解,我们是如何把库存相关的功能和数据表,从现有系统里剥离出来,最终构建独立的库存服务,并实现和业务系统平滑对接的。
|
||||
|
||||
## 改造背景和目标
|
||||
|
||||
我们先来看下这次架构改造的背景和目标。
|
||||
|
||||
1号店作为一个网上超市,售卖的商品种类有数十万个,包括1号店自营和第三方商家的商品。由于历史原因,所有商品相关的表都存在产品库里面,这里面有产品的表(产品、分类、品牌、组合关系、属性等)、商品SKU的表、商家和供应商的表、库存和价格的表等等,这些表加起来,数量超过了上百张。
|
||||
|
||||
我们知道,商品是电商业务的核心,几乎所有的前后台系统都需要访问这个产品库,而这些系统的开发人员,早期的时候,只关心如何实现业务功能,对这些表的访问是怎么方便怎么来,有些SQL语句会对大量的表做Join关联。所以说,虽然系统是类似分布式的,但数据库是集中式的,如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d6/27/d60dcd957d63ac2b92bf0158a3fb9c27.jpg" alt="">
|
||||
|
||||
这样的方式,就给系统的维护带来了一系列的问题。
|
||||
|
||||
- 从**应用方面**来说,各个系统功能重复建设,比如很多系统都会直接访问库存相关的表,类似的库存逻辑散布在很多地方;另外,如果修改了库存表的某个字段,这些系统同时会受影响,正所谓牵一发而动全身。
|
||||
- 从**数据库方面**来说,数据库的可用性是比较差的,如果某个系统有慢查询,它就很可能拖垮整个产品数据库,导致它不可用;还有,这么多系统同时访问产品库,数据库的连接数也经常不够用。
|
||||
|
||||
所以,我们这次架构改造的目标,首先是对这个大数据库按照业务维度进行垂直拆分,比如分成产品数据库、库存数据库、价格数据库等等;然后基于这些拆分后的库,构建微服务,以接口的方式来支持数据库表的访问;最后将各个业务系统统一接入微服务,最终完成整个商品体系的微服务化改造。
|
||||
|
||||
## 微服务改造过程
|
||||
|
||||
你可以看到,这里涉及了多个微服务,如果同时进行服务化改造的话,牵扯太大,很难落地。于是,我们选择从**库存微服务**开始。一方面,库存的业务很重要,库存的规则也比较复杂,如果我们能够对库存逻辑进行优化,这会带来明显的业务价值;另一方面,电商的库存概念相对独立,涉及的表也比较少,我们可以相对容易地把它从现有体系中剥离出来。
|
||||
|
||||
整个改造过程,从确定库存相关的表开始,到最后把库存表从产品库迁移出来,落到单独的库存数据库为止,一共分为两个阶段,每个阶段包含了3个步骤,具体如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d5/5a/d59b641233f9c9190be00119330da75a.jpg" alt="">
|
||||
|
||||
- **准备阶段**:这个阶段为微服务改造做好前期的准备工作,具体步骤包括了圈表、收集SQL和SQL拆分。
|
||||
- **实施阶段**:这个阶段实际落地微服务,具体步骤包括微服务开发、服务接入和数据库独立。
|
||||
|
||||
通过这些良好定义的步骤,我们就很好地保证了整个库存微服务改造的有序和可控。接下来,我就具体说明下改造的各个步骤,包括哪些人负责哪些事情、具体的挑战在什么地方,这样,你可以深入地理解整个改造过程。
|
||||
|
||||
### 准备阶段
|
||||
|
||||
**准备阶段的第一步,就是圈表。**产品数据库有100多张表,圈表就是用来确定库存微服务具体包含哪些表,也就是确定服务的数据模型。在确定了表以后,库存微服务就负责这些表的访问,当然,库存微服务也不会访问其它的表,而业务系统后续将通过库存微服务的接口,实现对这些表的访问。
|
||||
|
||||
圈表是微服务改造中比较有挑战性的地方,它实际上对应了服务的边界划分。只是针对老系统做服务化改造的时候,我们更多的是从数据库表的角度来考虑划分,这样更好落地。
|
||||
|
||||
针对库存微服务来说,我们要求圈定的表,一方面要满足所有的库存访问需求,这些表之间关系紧密,和其它的表关联不大;另一方面,这些表的数量不能太多,一般不超过十几张。这样,我们既容易拆分数据库,又能控制服务的粒度,保证功能聚焦。
|
||||
|
||||
在这个例子中,由于库存的概念比较独立,圈表相对比较容易,一共有15张表和库存直接相关,包括自营库存表(这里有分表,实际是12张)、商家虚拟库存表、活动库存表和库存共享表,这些库存表之间是紧密相关的,它们一起决定了前台用户能看到的可用库存数量。
|
||||
|
||||
这些库存相关的表都有商品ID字段,和商品基本信息表关联,我们知道,库存数量的计算不依赖于商品的具体信息。所以,这些库存表和其它表的关系比较弱,这样我们就可以比较清晰地实现库存表和其它表的切分,简化了库存服务的落地。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d8/41/d8d9fd5198ae90448b74a509e6915241.jpg" alt="">
|
||||
|
||||
在微服务改造中,确定哪些表属于这个服务,会直接影响后续的所有改造工作,这需要有经验的业务架构师和数据架构师参与进来,通过深入地分析现有的业务场景和表的关系,才能对库表进行合理的划分。
|
||||
|
||||
所以,你可以发现,**对现有系统的改造,服务的边界划分主要是从圈表入手的,而不是从一个服务应该有哪些功能入手的,这一点和新服务设计是有所不同的。**这有两方面原因:
|
||||
|
||||
- 一方面,如果确定了服务包含哪些表,也就大致确定了服务有哪些功能,而表是现成的,它比业务功能要直观很多,所以从表入手比较高效;
|
||||
- 另一方面,如果从表入手,构造的服务和表是对应的,服务包含的是完整的表,不会产生一个表的一部分字段属于库存服务,而另一部分字段属于别的服务的情况,避免表字段的拆分带来额外的复杂性。
|
||||
|
||||
值得注意的是,因为这是对现有系统的改造,为了避免一下子引入太多变化,我们先不对库存的表结构进行调整,表结构的优化可以放在服务的升级版里做,这样对业务系统的影响也最小。
|
||||
|
||||
**第二步是收集SQL。**在确定了哪些表属于库存服务后,我们会收集所有业务系统访问这些表的SQL语句,包括它的业务场景说明、访问频率等等。库存微服务后续就针对这些SQL进行封装,提供相应的接口给业务系统使用。
|
||||
|
||||
这里,服务开发团队负责提供SQL收集的Excel模板,各业务系统开发团队负责收集具体的SQL。
|
||||
|
||||
**第三步是拆分SQL。**对于收集过来的SQL语句,有些SQL不仅仅访问圈定的这几张库存表,还会和产品库中的其他表进行关联。
|
||||
|
||||
比如说,商品详情页需要展示商品详情,它会发起SQL查询商品基本信息表和库存表,一次性获取商品的基本信息和库存数量。针对这种情况,我们就需要把查询语句拆分为两条SQL,先查询商品表获取商品基本信息,再查询库存表获取库存数量。
|
||||
|
||||
对于这样的SQL语句,我们就要求各个业务团队先进行拆分,保证最后提供给服务开发团队的SQL,只包含访问库存的相关表。通过SQL拆分,我们切断了库存表和其他表的直接联系,等后面微服务落地后,业务系统就可以通过接入微服务,完成现有SQL的替换。
|
||||
|
||||
SQL拆分,会涉及一定的业务系统改造,这部分工作主要由各个研发团队负责,一般情况下,性能可能会受些影响,但问题不是很大。
|
||||
|
||||
### 实施阶段
|
||||
|
||||
完成了圈表、SQL收集和拆分以后,接下来,我们就进入了服务实际落地的阶段。
|
||||
|
||||
**第四步是构建库存微服务。**这里面包括了接口设计、代码开发、功能测试等步骤,服务开发团队会对业务方提供的SQL进行梳理,然后对接口做一定的通用化设计,避免为每个SQL定制一个单独的接口,以此保证服务的复用能力。
|
||||
|
||||
这部分工作由微服务开发团队负责,第一版的服务主要是做好接口设计,聚焦业务功能,以保证服务能够落地,业务系统能够顺利对接为目标。将来,服务可以持续迭代,内部做各种技术性优化,只要服务的接口保持不变,就不会影响业务系统。
|
||||
|
||||
**第五步是接入库存微服务。**库存服务经过功能和性能验证以后,会由各个业务开发团队逐步接入,替换原来的SQL语句。这部分工作主要由业务研发团队负责,难度不大,但需要耗费比较多的时间。
|
||||
|
||||
**最后一步是数据库独立。**当服务接入完成,所有的SQL语句都被替换后,业务系统已经不会直接访问这些库存的表。这时,我们就可以把库存相关的表,从原来的产品库中迁移出来,部署成为一个物理上独立的数据库。业务系统是通过服务来访问数据库的,因此,这个数据迁移对于业务系统来说是透明的,业务团队甚至都不用关心这些表的新位置。
|
||||
|
||||
通过库存表独立成库,我们可以从物理层面,切断业务团队对这些表的依赖,同时,也可以大幅度降低产品库的压力,特别是大促的时候,库存读写压力是非常大的,数据库独立也为库存服务后续的技术优化打下了基础。
|
||||
|
||||
这部分工作主要由微服务开发团队和DBA一起配合完成,主要是要避免业务系统还有遗漏的SQL语句,避免它们还在直接访问库存的表。我们可以在迁库前,通过代码扫描做好相应的检查工作。
|
||||
|
||||
改造完成后的库存微服务架构如下图所示,库存微服务一共包含了15张表,对外有30多个接口,几十个业务系统接入库存服务。平时,库存服务会部署50个实例,大促时会部署更多,我们很容易通过加机器的方式,实现库存服务的水平扩展。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ed/e4/edd3896352ae263f6ae6f7c773d8f2e4.jpg" alt="">
|
||||
|
||||
## 微服务改造小结
|
||||
|
||||
到这里,我们的库存微服务就改造完成了,整个改造大概持续了3个月,主要是对接的工作比较耗时。
|
||||
|
||||
从前面的步骤中,你可以看到,**除了做好库存服务本身的设计开发工作,相关团队之间的配合也是非常重要的。**
|
||||
|
||||
在整个改造过程中,有很多**团队之间沟通和确认**的环节。比如说,服务开发团队圈定表以后,需要和业务开发团队一起确认,保证圈表的合理性;在业务团队拆分SQL的过程中,服务开发团队需要介入进去,帮助解决拆分时带来的性能和一致性问题;在服务接口设计和接入过程中,服务的接口可能需要重新调整,也可能有新的SQL进来,双方需要及时沟通,相互配合。
|
||||
|
||||
这些都是纯技术层面的问题,值得一提的是,系统改造不会产生直接的业务价值,对于业务开发团队来说,他们往往还需要承担大量新需求的开发工作。所以,**从项目推进的角度来看,这种核心服务的改造,很多时候都是技术一把手工程**。在库存微服务改造过程中,我们也是老板高度重视,大家事先定好时间计划,每周Review进度,协调各个团队工作的优先级,确保改造的顺利落地。
|
||||
|
||||
以上就是库存微服务改造的例子。1号店的系统从08年就开始建设了,由于历史原因,形成了几个典型的大库,比如产品库、用户库等等,我们通过类似的微服务改造,逐步把这些大库拆分开,构建了一系列的基础服务,如订单服务、用户服务、产品服务、库存服务、价格服务等等。而且通过这些微服务化改造,我们同时提升了业务的复用性和系统的稳定性。
|
||||
|
||||
最后,我在这里放了一张1号店的总体系统架构图,你可以深入看下,**一个历史包袱很重的系统,它是如何经过服务化改造,最终变成一个能够高度复用和扩展的平台的。**
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/45/4a/45ee463d98340c23d9b0830acb4b074a.jpg" alt="">
|
||||
|
||||
## 总结
|
||||
|
||||
好了,下面我总结一下今天所讲的内容。
|
||||
|
||||
**基于现有系统进行改造和全新的服务设计是有所不同的,我们不能追求理想化和一步到位,而是要考虑到系统的平滑过渡,先实现微服务的顺利落地,后续再考虑各种优化。**
|
||||
|
||||
今天,我通过1号店库存微服务改造的例子,给你提供了一种可行的微服务落地套路,让你可以顺利地完成老系统的架构升级。
|
||||
|
||||
相信通过今天的分享,你对现有系统如何进行微服务化改造有了更深入的理解,希望你在实践中也能灵活运用。
|
||||
|
||||
**最后,给你留一道思考题:**你在做现有系统服务化改造的过程中,具体碰到了哪些挑战,你又是如何克服的呢?
|
||||
|
||||
我是王庆友,欢迎你在留言区与大家分享你的思考,我们一起讨论。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。
|
||||
108
极客时间专栏/架构实战案例解析/业务架构篇/10 | 可复用架构案例(三):中台是如何炼成的?.md
Normal file
108
极客时间专栏/架构实战案例解析/业务架构篇/10 | 可复用架构案例(三):中台是如何炼成的?.md
Normal file
@@ -0,0 +1,108 @@
|
||||
<audio id="audio" title="10 | 可复用架构案例(三):中台是如何炼成的?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/1d/02/1df62c48be984c8c1b5b784faff0f002.mp3"></audio>
|
||||
|
||||
你好,我是王庆友。
|
||||
|
||||
在[第8讲](https://time.geekbang.org/column/article/209138)中,我通过一个实际的订单服务案例,和你介绍了如何设计一个基础服务。今天,我就继续带你了解,如何在实际的业务场景中,通过一步步的架构升级,最后落地一个中台,实现企业级能力的复用。
|
||||
|
||||
通过前面的介绍,我们已经很清楚了共享服务和中台的价值,但在实践中,要不要对系统做这样的升级,我们还需要结合业务来判断,比如说:
|
||||
|
||||
1. **业务上有什么重大变化,导致当前系统的弊端已经很明显,不能适应业务发展了呢?**
|
||||
1. **架构改造时,如何在业务、系统、资源三者之间做好平衡,对系统进行分步式的改造呢?**
|
||||
|
||||
我们知道,架构没有最好,只有最合适的。随着业务的发展,系统需要不断地升级,这是一个螺旋式上升的过程,如何结合当前的业务发展阶段,适时地推进架构改造,并能比较接地气地落地,是我们要追求的目标。
|
||||
|
||||
接下来,我以实际的订单系统改造为例,结合订单业务的发展和系统的痛点,为你介绍,如何推进架构从单体到共享服务、再到中台的改造过程,保证系统能够不断适配业务的升级。
|
||||
|
||||
先说下项目背景。公司作为供应商,为大型餐饮连锁企业打造O2O交易平台,包括三方聚合外卖、自有小程序、App点餐,这些线上用户的订单最终会落到门店的收银系统,由门店进行履单。
|
||||
|
||||
公司的业务发展有一个变化过程,一开始只提供聚合外卖服务,后来进一步提供小程序/App下单服务。你可以发现,整个订单处理的架构也是随着业务的变化而不断演变的,下面我就为你一一介绍。
|
||||
|
||||
## 聚合外卖订单架构
|
||||
|
||||
一开始,我们提供的是聚合外卖服务,相应地,系统整体架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/d3/46/d3a670ed6f529c91a17603cd91733c46.jpg" alt="">
|
||||
|
||||
这里一共有三个系统,分别是三方外卖平台、门店收银系统以及外卖系统。其中,外卖系统是我们开发的,其他两个都是我们要对接的外部系统,接下来,我说下系统具体的交互过程。
|
||||
|
||||
首先,用户在三方外卖平台(如美团、饿了么)下单;然后,我们的外卖系统通过外卖平台的API拉取用户的订单,把订单落到本地数据库;最后,门店的收银系统访问外卖系统提供的接口获取订单,在门店内部完成履单。当然,门店履单后,收银系统会反过来同步订单状态给外卖系统,外卖系统再同步订单状态到第三方外卖平台。
|
||||
|
||||
你可以看到,这里的外卖系统是一个单体应用,内部包含外卖同步接口和POS接口两个模块。其中,**外卖同步接口**负责和第三方外卖平台对接,它主要是针对不同的外卖平台做接口适配;而 **POS接口**负责和门店的收银系统对接。这两个模块都是使用同一个外卖订单数据库。
|
||||
|
||||
从**数据模型**上看,系统的订单模型也是完全按照外卖订单的需求设计的,订单状态管理也相对比较简单,因为这些订单都是用户在第三方外卖平台已经完成支付的。所以,我们的外卖系统,主要是负责管理门店履单过程中带来的订单状态变化。
|
||||
|
||||
从**系统架构**上看,外卖系统从外卖平台接单,然后把订单推送给后面的收银系统,只需要一个应用、一个数据库、两套接口就可以支持,使用单体架构就能很好地满足外卖的接单需求。
|
||||
|
||||
## 小程序下单架构
|
||||
|
||||
接下来,随着公司业务的升级,除了提供聚合外卖服务之外,公司还提供自有小程序的下单服务。这样,消费者既可以在三方外卖平台下单,也可以在品牌自有的小程序里下单。
|
||||
|
||||
**不同于三方外卖订单,小程序下单平台是一个完整的业务**,它包括小程序用户注册、商品和菜单浏览、商品加购物车、在线支付等等。相应地,这里会有多个基础服务对应具体业务的处理。比如,商品服务提供前台的商品浏览功能,支付服务提供用户的支付功能,这些基础服务都是由独立的小程序服务端负责整合,然后提供接口供小程序前端访问。
|
||||
|
||||
当用户在小程序提交订单后,小程序前端会调用服务端的下单接口,然后服务端调用订单服务,在小程序的订单库里落地订单。现在我们已经完成了前台用户的下单,但后台的订单履行怎么处理呢?这里有两种选择:
|
||||
|
||||
1. 小程序订单和外卖订单的处理类似,收银系统除了对接外卖系统,同时也对接小程序的订单服务。但这样一来,收银系统需要同时对接两套订单接口,它需要做大的改造。由于这是第三方的系统,我们在实践中很难落地。
|
||||
1. 我们把小程序订单当作一个特殊的外卖渠道,把小程序订单推送到外卖订单库里,最终还是由外卖系统来对接收银系统,也就是相当于小程序订单直接借用了外卖订单的履单通道。
|
||||
|
||||
当时由于项目上线的时间比较紧急,同时从系统稳定性的角度出发,避免对收银系统做大的改造,我们采用了**第二种方式**,小程序的订单处理就嫁接在已有的外卖系统上,整个系统架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ef/ba/ef08a388d34ab936350efd52c1549cba.jpg" alt="">
|
||||
|
||||
你可以看到,小程序下单平台和外卖系统相对独立,同时为了更好地解耦,小程序订单服务和外卖系统之间是通过**消息系统**同步订单数据的。
|
||||
|
||||
这个方案是一个比较务实的选择,通过复用外卖订单的履单通路,我们也实现了小程序订单的闭环处理。表面上看,我们节省了重新搭建系统的成本,也快速落地了小程序交易这条新业务线。
|
||||
|
||||
但这样的架构**实际上是一种妥协**,在后续的系统运行过程中,给我们带来了很多问题:
|
||||
|
||||
1. 这里有两套订单系统,一套针对小程序订单,一套针对外卖订单。我们知道,两者的字段属性和订单状态定义都有不同的地方,我们把小程序的订单硬生生地套在了外卖订单的模型里,这样限制了小程序订单能力的扩展。
|
||||
1. 小程序订单处理链路过长,从小程序服务端->订单服务->小程序订单数据库->消息系统->外卖同步接口->外卖订单数据库-> POS接口->收银系统,一共包含了8个处理环节,系统整体的性能和可用性都存在很大问题。比如,取餐码已经从收银系统同步给了外卖系统,但由于消息队列堵塞,外卖系统不能及时同步给小程序的订单服务,这样导致了小程序用户不能及时地看到取餐码。
|
||||
1. 为了使两套订单系统解耦,我们使用了消息队列在两个库之间同步订单数据,这降低了系统整体的稳定性。实践中,也发生过多起消息队列故障导致的线上事故。
|
||||
|
||||
你可以发现,出现这些问题的根源是我们把小程序订单硬塞给外卖系统,一方面订单数据模型不匹配,另一方面由于这是两个系统的简单拼接,导致系统调用链路很长,影响了业务的扩展和系统的稳定性。
|
||||
|
||||
**那有没有更好的办法,能够把这两个系统有机地结合起来呢?**接下来,我们就来看下,如何通过一个统一的订单服务对两个系统进行深度的融合,从而灵活地支持多种订单业务。
|
||||
|
||||
## 统一订单服务架构
|
||||
|
||||
这里,我们把小程序订单服务提升为统一共享的订单服务,由它来落地所有类型的订单。对于这个统一的订单服务来说,外卖订单、小程序订单,或者是其他的新订单,都是它的下单来源,所有订单汇总在订单服务里,然后统一提供给收银系统进行履单。具体架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7c/b0/7c40a6fe7f17f17b28decf7b09cb0cb0.jpg" alt="">
|
||||
|
||||
你可以看到,系统架构经过调整,有两个大的变化:
|
||||
|
||||
1. 原来外卖和小程序各自有一个订单库,现在合并为了一个订单库,由这个订单服务统一对外提供订单数据的访问和状态管理。
|
||||
1. 原来外卖系统的两个模块“外卖同步接口”和“POS接口”,升级为了两个独立的应用。外卖同步接口变成外卖同步服务,对接外卖平台;POS接口变成POS服务,对接门店的收银系统。它们都是通过统一订单服务存取订单数据。
|
||||
|
||||
**经过升级,新的架构具备了明显的层次结构,自上而下分为三层:**首先是各个渠道端,包括三方外卖平台、小程序前端和POS收银系统;然后,每个端都有相应的服务端来对接,比如外卖同步服务对接外卖平台、小程序服务端对接小程序、POS服务对接收银系统;最后,这些服务端都统一调用底层的订单服务。
|
||||
|
||||
在这个架构里,如果我们要增加新的下单渠道,就非常方便,比如要支持App下单,我们提供App服务端即可;要新增加后台履单方式也非常方便,比如对于新的电子卡券类订单,它不需要经过收银系统,可以直接由企业的OMS系统(Order Management System,订单管理系统)处理,要实现这样的业务,我们只需新增加一个和OMS系统的适配应用就可以了。所以,**这里就不仅仅是一个外卖订单和小程序订单的处理平台,而是升级成了一个完整的全渠道交易平台。**
|
||||
|
||||
同时,订单处理的链路大大缩短,从小程序服务端->订单服务->订单数据库-> POS服务->收银系统,只有5个节点,相比之前减少了3个,系统的可用性和端到端的性能得到了大幅度的提升。
|
||||
|
||||
最后,统一订单服务实现了统一的订单属性定义、统一的订单状态管理,以及订单数据的集中存储,这对后续的BI分析和数据中台建设非常有帮助。它们处理数据时,只需要从一个订单库拉取数据,解析一个订单数据模型就可以了。
|
||||
|
||||
## 中台架构
|
||||
|
||||
上面的统一订单服务整合了外卖和小程序的订单,并且为新的下单渠道预留扩展。按照同样的思路,我们可以构建统一的商品服务,同时满足外卖和小程序上商品的管理;可以构建统一的促销服务,同时支持线上和线下的促销活动;也可以构建统一的库存服务,实现线上和线下库存的同步和共享等等。
|
||||
|
||||
**通过构建这样一系列的共享服务,我们就实现了各个渠道业务规则和业务数据的统一管理,最终我们落地了一个强大的业务中台,可以很方便地扩展各个业务,实现企业整体业务能力的复用。**
|
||||
|
||||
最后,实际项目的中台架构如下图所示:
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/33/75/3319e45e76ed17eea808a6a4dcf8e575.jpg" alt="">
|
||||
|
||||
在这个架构中,**前端**有3个业务场景,分别是小程序点单、App商城下单、外卖平台下单,每个业务场景都有相应的**服务端**负责对接。在各个服务端下面,还有一些**辅助的应用**,如购物车、秒杀、拼团等等。同时这里还有一个**订单控制服务**(Order Control Service,OCS),负责订单逻辑的编排以及前后台之间的状态同步,你可以把它看作是基础服务之上的聚合服务。
|
||||
|
||||
再底下就是核心的**业务中台**,它由9大服务中心组成,这些中心和商户内部系统进行对接。其中,商品中心和库存中心对接ERP系统,会员中心对接CRM系统,订单中心对接POS收银系统,这里的对接分别由对应的适配插件负责。
|
||||
|
||||
通过这个订单业务改造落地后的中台架构,你可以看到,中台由各个通用的基础服务构成,它是相对标准的;而插件是定制的,具体和每个企业的后台系统有关。这样,通过共享服务和中台,我们就把企业内部基础设施和线上业务场景有效地打通了,从系统架构的层面,为企业的全面数字化转型打下了良好的基础。
|
||||
|
||||
## 总结
|
||||
|
||||
今天,我从一个企业的订单业务变化出发,为你介绍了为什么要落地一个统一的订单服务,以及如何落地,并通过打造一系列类似的共享服务,逐步升级系统到中台架构。
|
||||
|
||||
相信通过这个实际案例,你进一步理解了如何通过共享服务和中台,实现业务能力的复用,并能根据公司的业务发展阶段,选择合适的时机、合适的架构,以接地气的方式对系统进行逐步改造。
|
||||
|
||||
**最后,给你留一道思考题:**目前你的公司有没有落地共享服务,它是怎么逐步演变过来的呢?
|
||||
|
||||
欢迎你在留言区与大家分享你的答案,如果你在学习和实践的过程中,有什么问题或者思考,也欢迎给我留言,我们一起讨论。感谢阅读,我们下期再见。
|
||||
Reference in New Issue
Block a user