在单体到微服务架构的迁移过程中,我们经常会问一个问题:在什么情况下我需要从单体中剥离一部分出来将其作为一个微服务?答案有很多,其中有一个答案就是:我发现好多单体都有相似的功能,我觉得可以把它抽出来做一个公共服务。
那么何为公共服务?公共服务就是那些专门为其他服务提供服务的服务,它的业务具有高度普适性和可复用性。比如用户服务、订单服务就是这样一类服务。
那我们怎样才能达成这一目标呢?下面举个例子说明:
我们发现单体A、B、C中的具有高度相似的功能F,经过初步研究发现可以抽取出一个公共服务P,于是马上安排人员开发,然后上线、迁移。
一切似乎会很顺利,但是等等,这里有太多风险和未经验证的东西。那我们应该怎么做?
第一步:识别不兼容
我们需要深入到单体A、B、C的功能F的细节中去考察,虽然功能F在各个单体中的看起来差不多,但是细节是魔鬼。
毕竟这三个单体是独立演进的,如果深入细节,就会发现AF、BF、CF总有一些不兼容的地方,具体表现形式可以是数据schema不一致、接口不一致,还有更糟糕的——部分业务逻辑不一致。
这些不兼容如果一开始不识别出来,那最有可能的结果是开发人员从单体A中抽取功能F开发公共服务P,结果公共服务P只能给单体A使用,单体B、C完全无法使用。
第二步:设计并导入概念
识别出了不兼容,那么我们就需要作出一套兼容单体A、B、C的功能F的设计,在设计过程中我们需要和单体A、B、C的产品经理、需求设计人员、开发人员做详尽的沟通。
做这些沟通最主要的目的就是将新的功能F的设计导入到相关人员的脑中,即所谓的概念导入。
概念导入是非常重要的一步,道理很简单,如果大家对同一件事情的认知是一样的,那么这件事情就好办了,否则在推行过程中很可能出现不可预料的阻力。
第三步:开发类库
好了,咱们设计也做了,思想也统一了,为什么不直接开始开发服务P呢?这是因为虽然我们在第二步已经统一了思想,但是在真正落地的时候很可能还会存在意想不到的困难。
因此我们要把这些困难在早期趟平,用的办法就是提供功能F的公共类库,将其替换掉原来单体A、B、C中的代码。
这一工作完成后我能能够得到的成果有:
- 设计、概念层面形成了统一
- 表、实体结构形成了统一
- 接口形成了统一
- 代码层面的复用
也就是说,我们做到了表里一致,有了这个基础,开发公共服务P才有了真正坚实的基础。
第四步:微服务架构设计
公共类库的思路毕竟还是单体应用的思路,只是做到了代码层面的复用。当你要做公共服务P的时候就需要额外考虑一些额外的设计,比如:
- Client的认证模式。不认证、OAuth 2.0,如果用OAuth 2.0那么用哪种Grant type?
- 通信协议。http、https、gRPC。
- 接口协议。RESTful、SOAP。
- 接口定义。Swagger、OpenAPI。
- 加密方式。TLS、对称加密。
- 是否涉及多租户,如果涉及,那么还需要设计多租户的业务、功能、数据schema。
- 考虑服务注册、服务发现等等。
- 弹性设计、容错设计、分布式事务等等。
- 运维相关的日志采集、监控指标等等。
当然以上这些并不是说要一次性全部做到,可以一开始只实现一部分,之后通过迭代不断地完善。
第五步:迁移
好了,我们的公共服务P已经上线了,那么如何从公共类库迁移到公共服务P呢?
其实到这一步就很简单了,在制作公共类库的时候我们应该定义了良好的接口,并且提供了一套基于单体架构的实现。
现在我们只需提供一套基于公共服务的实现然后替换上去就可以了。举个例子,公共类库有一个接口叫做UserRepository
,它有一个实现是查询本地数据库的,我们只需提供另一个实现是查询RESTful接口的就可以了。
当然,这个查询RESTful接口的实现最好也由公共服务P的开发团队提供,这样可以避免单体A、B、C开发团队的重复劳动。
第六步:没有第六步
到此为止大功告成。