工程微服务化自用指南
在经历服务雪崩和单库八百张表的折磨之后,终于有人提出了重构...这篇文章是我在五月底写的,在在经历了重构和重写之后,我觉得这篇可以再改改,因为似乎发现了更好的思路(效率更高),当然,大背景是团队并没有一个真正的微服务高手,大都处于现学现卖(我们架总除外,落地的相关人员的确是在这个方面并没有过多的涉略,导致我们踩了不少坑)
前言
其实重构是一个很严肃的词汇,对于一些上了年纪的项目而言,重构大多数情况下解决的是当前系统不能满足业务方需求的问题。常见的有请求增多单服务风险变高、维护难度大,多人多分支开发混乱等。当然我们所要做的事情是“重构”,而非“重写”。即在保证业务功能不变的情况下,将服务拆分。
整体思路
首先要说明一下大背景和基础的相关内容。
- What
什么是微服务?微服务(Microservices Architecture)是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。显然里面带着我们程序设计的思路:松耦合、高内聚(软件工程hhh),优点在于每个微服务都很小,这样能聚焦一个指定的业务功能或业务需求。微服务能够被小团队单独开发(维护交接的难度降低),微服务能使用不同的语言开发(对于大公司而言不同团队可以选择适合自己团队的语言来开发,只要通信协议都支持、一致就完全没有问题)
- Why
这里的Why肯定是以我们公司的业务角度出发。那就得从现状说起:本身并不是很大的业务量,但是自建的DB逐渐已经无法满足各种变更迅速的需求和业务,于是我们先切换到了阿里云的RDS,的确暂时撑住了,但是多分支多任务的开发,一个庞大的项目不论是编译发布还是合并上线,每次都非常令人头疼,这是原因其一。单库八百张表,数十个应用都在访问同一个库,若由于某个应用引起的DB宕机都会导致其他服务都不可用,显然各种高耦合的设计是没有任何容错的(参考前面的缓存雪崩引发的服务宕机,历历在目)我们需要合理的拆分。
- When
我们已经死过一次了,难道还要再死一次?请马上亡羊补牢。雾....
其实对于任务落在业务团队的我们而言,最好的时候就是业务负担较轻,而我们的CTO似乎已经争取到了这些机会..那还在等什么?
- How
这里的拆分分为两个步骤,是拆分和微服务化,我们是按照下面的方式来行动的:
1.明确划分标准
这点关乎到服务拆分的标准,是以什么维度,粒度拆成多大。具体情况还得视业务而定。比较著名的康威定律指出:系统设计(产品结构)等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构(换句话说系统设计是要人维护的,所以得看维护的人和具体的场景,并没有非常明确的边界),当然关于康威定律也单独能说好久,这里附一篇简介吧:微服务架构的理论基础 - 康威定律,不做过多的描述(我说不清,搞的并不是很透彻)
2.确定服务间通讯方式
RPC OR REST?还是消息?
这个其实各有喜好,从同事的熟练程度来讲大家都比较熟悉DUBBO,而且支持协议又广,加上国产身份,用的人多,代表着出了问题好解决。但是作为RestFul作为SpringCloud官方推荐的服务间通信方式,并在SpringCloud的版本中集成了Feign,一切都很自然(只针对愿意上Cloud全家桶的人),而消息队列在做异步通信可以说非常适合,但是消息的可靠性和高效能往往是要注意的地方。对于我们而言,最终来看我们选择的全都要...
3.划分调用路线
这里涉及到一些服务编排的内容,我不是特别了解,只能单独开文章再说了,但是我可以画几张图来简单说下我对编排和调用的理解:
下图是我们在十月份重构时使用的编排方式,在业务场景简单且流程不复杂的情况下,这种模式是能Hold住的,但是维护成本较于我们公司人数而言,其实事实上是偏大的:
原因在于只要涉及的对外接口的修改,基本上聚合层就得更着变,所以我们将调用方式更改了下:
而服务的调用关系最终只有SkyWalking能理得清。所以在这个事情上,我们至今的方式都是有所欠缺的,我觉得这个可能会是未来维护的困难的一个隐患。
在这个环节最终也放一条关于服务编排的文章:微服务核心研究之--编排,对于这块,我还是会找机会给修补上的。
解决方案
上面基本上说了一些背景和想法,但是具体落地是需要有一个明确的路径的,而下面就是如何将一个单体的多模块项目拆分,形成微服务。
1.独立模块微服务化
首先我们要确定版本,较大版本的跨越可能会遇到一些问题,所以选择SpringBoot合适的版本尤为重要。确定版本之后接下来需要的就是解决修改版本带来的Jar包冲突(MAVEN项目你懂的)
明确了新项目的目标之后所做的第一件事就是删除无调用的接口,剥离与模块不相关的DB查询(比如用户的接口在直接查订单相关的表,需要将其拆除)。我们通过切面统计了三月内无调用的接口,并将其逐渐下线,然后将有跨业务表查询的Service层的代码逐渐改造,使用Http查询。
当完成上面两步之后进入了新的阶段:代码迁移
我们将不同模块的代码逐渐迁移进新的项目,然后修改不兼容的地方,有查询其他库表的需求都通过Feign的声明式接口来获取。而独立项目能运行之后再利用切面来比对接口的结果(这个切面单独会说下...算是微服务化中的一个亮点了..)
然后确认结果的重合度达到预期,就可以上线,将Ngnix/Gateway的流量切换到新服务。
2.分库
当上一步稳定之后,由于表之前在没有交叉查询了,可以对应的将新服务对应的新表迁移,修改资源的文件的配置,链接新库。
3.服务的注册和发现
这里注册中心第一版我们使用的SpringCloud官方提供的Eureka,虽然现在切换到了Nacos,但是第一版,毕竟尝鲜体验版。
4.数据一致性
微服务拆分后随后带来的就是数据一致性。如何保证数据一致性一直是分布式系统的一个热门话题。这点其实是一个深奥的问题,对于DB数据而言,我们选择最终一致性的分布式事务中间件RainCat(因为简单...)这个话题,不是本部分了解的核心,还是那句:单独再说吧..因为内容太多了
带来的影响
与公司而言,业务方可能没有任何感知,但是对于未来的业务方而言,拆分这件事本来就是百利而无一害(抛开维护成本),我们的确不是为了微服务而微服务,我觉得更重要的是拆分给团队积累了一些微服务和分布式系统的经验,为下半年我们直接上SpringBoot2.1.7版本、SpringCloud Greenwich.SR2版本垫下基础,而架构也在适配公司的监控和鉴权/异常/日志/链路/资源组件等方面做出了一些改动,有了公司封装的SpringBoot版本,集成了一些必要的组件,换言之,我们应该不会再遇到之前的闷棍了(线上服务暴毙我们居然不是第一时间知道)。更进一步将,微服务带来的问题也不少,比如维护人员个数少、数据一致性一直使用强一致性的RainCat...实现业务复杂度相对成本变高。优势就是划分清晰(刚刚拆开当然清晰)、模块间耦合不重维护单服务难度大大降低(这不是废话吗)..
与个人而言。学了大半年...就这样
总结
可能上文提到的一些点在后面的文章可能会放几篇上来,因为自己也是第一次将生产环境的内容做更改,的确不是特别了解很多东西,但是也在努力向上靠。这篇的文章的内容我们团队在架构的帮助下历经了大概五个月左右,才完全做到微服务化,加上在此过程中测试能够给予的帮助并不多,总体是有风险的一件事。回过头来看问题多多(其实上文提到的不少东西抓住一个点都可以挖很深,但是这篇文章我作为年终总结篇其实其本身的细节并不是很多,而是大概流程的一个描述),当然会有其他文章将遇到的问题一一说明。等我更新~