You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

131 lines
15 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 09 | 可复用架构案例(二):如何对现有系统做微服务改造?
你好,我是王庆友。在上一讲中,我以订单服务为例,和你一起讨论了如何从头开始,设计一个共享服务。今天我们再来聊一聊:**如何对现有系统做微服务化改造**。
很多早期的互联网公司都有巨大的单体应用,底层的数据表集中放在一个数据库里,这些表加起来可能有几百张。对于这样的应用系统和数据库,我们往往需要对它们进行拆分,通过微服务化改造,保证系统能够不断地扩展和复用。
相比从头开始落地服务,对现有系统做微服务化改造,这会面临更多的挑战。
首先应用和数据表紧密耦合在一起代码模块和表是多对多的依赖关系。一个模块会访问多张表多个模块也会对同一张表进行访问而且由于表都在一个数据库里开发人员往往会随意对表做关联有时候甚至Join 5~6张表以上。这样代码模块和表之间的关系是剪不断理还乱我们很难清晰地划分代码和数据表的边界也就很难把它们封装成独立的微服务。
还有,系统现在已经在运行了,我们的改造不能影响业务的稳定性。那微服务落地后,现有的系统要怎么对接微服务,数据要怎么迁移,才能保证系统的平滑过渡呢?
所以,要想应对这些挑战,一方面,我们要保证比较合理的服务设计,才能达到优化系统架构的目的;另一方面,我们要做到整个过程对现有系统的影响比较小,才能达到系统改造顺利落地的目的。
接下来我就以1号店库存服务化改造为例让你深入理解我们是如何把库存相关的功能和数据表从现有系统里剥离出来最终构建独立的库存服务并实现和业务系统平滑对接的。
## 改造背景和目标
我们先来看下这次架构改造的背景和目标。
1号店作为一个网上超市售卖的商品种类有数十万个包括1号店自营和第三方商家的商品。由于历史原因所有商品相关的表都存在产品库里面这里面有产品的表产品、分类、品牌、组合关系、属性等、商品SKU的表、商家和供应商的表、库存和价格的表等等这些表加起来数量超过了上百张。
我们知道商品是电商业务的核心几乎所有的前后台系统都需要访问这个产品库而这些系统的开发人员早期的时候只关心如何实现业务功能对这些表的访问是怎么方便怎么来有些SQL语句会对大量的表做Join关联。所以说虽然系统是类似分布式的但数据库是集中式的如下图所示
![](https://static001.geekbang.org/resource/image/d6/27/d60dcd957d63ac2b92bf0158a3fb9c27.jpg)
这样的方式,就给系统的维护带来了一系列的问题。
* 从**应用方面**来说,各个系统功能重复建设,比如很多系统都会直接访问库存相关的表,类似的库存逻辑散布在很多地方;另外,如果修改了库存表的某个字段,这些系统同时会受影响,正所谓牵一发而动全身。
* 从**数据库方面**来说,数据库的可用性是比较差的,如果某个系统有慢查询,它就很可能拖垮整个产品数据库,导致它不可用;还有,这么多系统同时访问产品库,数据库的连接数也经常不够用。
所以,我们这次架构改造的目标,首先是对这个大数据库按照业务维度进行垂直拆分,比如分成产品数据库、库存数据库、价格数据库等等;然后基于这些拆分后的库,构建微服务,以接口的方式来支持数据库表的访问;最后将各个业务系统统一接入微服务,最终完成整个商品体系的微服务化改造。
## 微服务改造过程
你可以看到,这里涉及了多个微服务,如果同时进行服务化改造的话,牵扯太大,很难落地。于是,我们选择从**库存微服务**开始。一方面,库存的业务很重要,库存的规则也比较复杂,如果我们能够对库存逻辑进行优化,这会带来明显的业务价值;另一方面,电商的库存概念相对独立,涉及的表也比较少,我们可以相对容易地把它从现有体系中剥离出来。
整个改造过程从确定库存相关的表开始到最后把库存表从产品库迁移出来落到单独的库存数据库为止一共分为两个阶段每个阶段包含了3个步骤具体如下图所示
![](https://static001.geekbang.org/resource/image/d5/5a/d59b641233f9c9190be00119330da75a.jpg)
* **准备阶段**这个阶段为微服务改造做好前期的准备工作具体步骤包括了圈表、收集SQL和SQL拆分。
* **实施阶段**:这个阶段实际落地微服务,具体步骤包括微服务开发、服务接入和数据库独立。
通过这些良好定义的步骤,我们就很好地保证了整个库存微服务改造的有序和可控。接下来,我就具体说明下改造的各个步骤,包括哪些人负责哪些事情、具体的挑战在什么地方,这样,你可以深入地理解整个改造过程。
### 准备阶段
**准备阶段的第一步,就是圈表。**产品数据库有100多张表圈表就是用来确定库存微服务具体包含哪些表也就是确定服务的数据模型。在确定了表以后库存微服务就负责这些表的访问当然库存微服务也不会访问其它的表而业务系统后续将通过库存微服务的接口实现对这些表的访问。
圈表是微服务改造中比较有挑战性的地方,它实际上对应了服务的边界划分。只是针对老系统做服务化改造的时候,我们更多的是从数据库表的角度来考虑划分,这样更好落地。
针对库存微服务来说,我们要求圈定的表,一方面要满足所有的库存访问需求,这些表之间关系紧密,和其它的表关联不大;另一方面,这些表的数量不能太多,一般不超过十几张。这样,我们既容易拆分数据库,又能控制服务的粒度,保证功能聚焦。
在这个例子中由于库存的概念比较独立圈表相对比较容易一共有15张表和库存直接相关包括自营库存表(这里有分表实际是12张)、商家虚拟库存表、活动库存表和库存共享表,这些库存表之间是紧密相关的,它们一起决定了前台用户能看到的可用库存数量。
这些库存相关的表都有商品ID字段和商品基本信息表关联我们知道库存数量的计算不依赖于商品的具体信息。所以这些库存表和其它表的关系比较弱这样我们就可以比较清晰地实现库存表和其它表的切分简化了库存服务的落地。
![](https://static001.geekbang.org/resource/image/d8/41/d8d9fd5198ae90448b74a509e6915241.jpg)
在微服务改造中,确定哪些表属于这个服务,会直接影响后续的所有改造工作,这需要有经验的业务架构师和数据架构师参与进来,通过深入地分析现有的业务场景和表的关系,才能对库表进行合理的划分。
所以,你可以发现,**对现有系统的改造,服务的边界划分主要是从圈表入手的,而不是从一个服务应该有哪些功能入手的,这一点和新服务设计是有所不同的。**这有两方面原因:
* 一方面,如果确定了服务包含哪些表,也就大致确定了服务有哪些功能,而表是现成的,它比业务功能要直观很多,所以从表入手比较高效;
* 另一方面,如果从表入手,构造的服务和表是对应的,服务包含的是完整的表,不会产生一个表的一部分字段属于库存服务,而另一部分字段属于别的服务的情况,避免表字段的拆分带来额外的复杂性。
值得注意的是,因为这是对现有系统的改造,为了避免一下子引入太多变化,我们先不对库存的表结构进行调整,表结构的优化可以放在服务的升级版里做,这样对业务系统的影响也最小。
**第二步是收集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个实例大促时会部署更多我们很容易通过加机器的方式实现库存服务的水平扩展。
![](https://static001.geekbang.org/resource/image/ed/e4/edd3896352ae263f6ae6f7c773d8f2e4.jpg)
## 微服务改造小结
到这里我们的库存微服务就改造完成了整个改造大概持续了3个月主要是对接的工作比较耗时。
从前面的步骤中,你可以看到,**除了做好库存服务本身的设计开发工作,相关团队之间的配合也是非常重要的。**
在整个改造过程中,有很多**团队之间沟通和确认**的环节。比如说服务开发团队圈定表以后需要和业务开发团队一起确认保证圈表的合理性在业务团队拆分SQL的过程中服务开发团队需要介入进去帮助解决拆分时带来的性能和一致性问题在服务接口设计和接入过程中服务的接口可能需要重新调整也可能有新的SQL进来双方需要及时沟通相互配合。
这些都是纯技术层面的问题,值得一提的是,系统改造不会产生直接的业务价值,对于业务开发团队来说,他们往往还需要承担大量新需求的开发工作。所以,**从项目推进的角度来看,这种核心服务的改造,很多时候都是技术一把手工程**。在库存微服务改造过程中我们也是老板高度重视大家事先定好时间计划每周Review进度协调各个团队工作的优先级确保改造的顺利落地。
以上就是库存微服务改造的例子。1号店的系统从08年就开始建设了由于历史原因形成了几个典型的大库比如产品库、用户库等等我们通过类似的微服务改造逐步把这些大库拆分开构建了一系列的基础服务如订单服务、用户服务、产品服务、库存服务、价格服务等等。而且通过这些微服务化改造我们同时提升了业务的复用性和系统的稳定性。
最后我在这里放了一张1号店的总体系统架构图你可以深入看下**一个历史包袱很重的系统,它是如何经过服务化改造,最终变成一个能够高度复用和扩展的平台的。**
![](https://static001.geekbang.org/resource/image/45/4a/45ee463d98340c23d9b0830acb4b074a.jpg)
## 总结
好了,下面我总结一下今天所讲的内容。
**基于现有系统进行改造和全新的服务设计是有所不同的,我们不能追求理想化和一步到位,而是要考虑到系统的平滑过渡,先实现微服务的顺利落地,后续再考虑各种优化。**
今天我通过1号店库存微服务改造的例子给你提供了一种可行的微服务落地套路让你可以顺利地完成老系统的架构升级。
相信通过今天的分享,你对现有系统如何进行微服务化改造有了更深入的理解,希望你在实践中也能灵活运用。
**最后,给你留一道思考题:**你在做现有系统服务化改造的过程中,具体碰到了哪些挑战,你又是如何克服的呢?
我是王庆友,欢迎你在留言区与大家分享你的思考,我们一起讨论。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。