微服务架构无疑是近几年最为火热的架构风格,其结合敏捷开发、DevOps,为技术团队应对飞速增长的业务需求和庞大的业务体系,提供了完备的理论支撑和技术支持。然而,通往美好的道路往往不是一帆风顺的,技术团队转型时也会面临诸多的问题,如:微服务怎么理解、服务如何划分、服务的交互形式、数据库设计要注意哪些等,本文希望提供一份行之有效的开发实践,帮助技术团队少走一些弯路,进而提高技术团队的转型幸福感。
1. 理解微服务
要理解微服务架构,理论支撑少不了,推荐这两篇文章快速入门:微服务架构那点事、微服务(Microservices)——Martin Flower
这里重点提一下Martin Flower对微服务的定义:
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
简单来说,微服务架构风格是一种将一个单一应用程序开发为一组小型服务的方法,每个服务运行在自己的进程中,服务间通信采用轻量级通信机制(通常用HTTP资源API)。这些服务围绕业务能力构建并且可通过全自动部署机制独立部署。这些服务共用一个最小型的集中式的管理,服务可用不同的语言开发,使用不同的数据存储技术。
2. 服务的划分
服务的划分关系到团队的开发效率和功能设计,服务划分比较理想的情况是:服务间耦合度低,大家通过接口或MQ交互并无什么困难,且服务数量适度,维护成本不高。服务划分比较痛苦的情况有:服务间耦合度较高,数据查询往往需要关联查询,特别是遇到分页,难以实现;服务拆分过小过多,如几个人要维护几十个甚至上百分服务,维护成本极高。
为了更好的划分服务,可参考如下几个步骤:
1)优先基于业务功能、基于数据模型划分服务,要避免拆分的太小导致工程较多不好维护,也避免耦合度较高的业务被拆分后造成交互过于复杂难以实现,所以在拆分时,要重点考虑服务耦合度,建议一个服务对应一块业务,或一个服务对应耦合度较高的一组业务。划分服务时要先从底层业务开始,再依次往上划分。如先划分出基础数据服务、用户服务、产品服务,再划分出购物车服务、订单服务,再划分出评价服务、返利服务等。通常来讲,一般规模的公司十几个或几十个微服务都是可以承受的,建议每个服务都由两、三个人参与维护(避免单点),每个人参与维护的服务数量控制在5个以内(避免维护成本过高),一个十人团队维护10~15个微服务是比较合适的。
2)基于特殊情况拆分服务,这种特殊情况包括:业务模块对性能、吞吐量、稳定性等指标有更高的要求;业务模块变更较频繁,频繁上线影响整体稳定性。对于这类特殊情况,首先我们要看设计上能不能优化,如果比较困难,且这部分业务模块与其他业务耦合度不高,则可将其拆分出来,单独形成服务。
3)基于技术功能划分服务,即将通用性的技术实现进行服务化,这类技术实现与业务功能无关,且使用内部组件的方式通常难以达到目标,对于这类技术实现,可以进行服务化,以技术平台的形式对外提供服务,常见的有:网关服务、监控服务、数据结转服务等。
4)随着业务的发展,服务的体量可能会日益增大,当触及团队的容忍底线时,需要进行服务拆分,服务拆分依旧可以参考上面三步,但需要注意的是,在服务平时的开发中,我们就要尽量做到按模块开发,多运用些设计模式,降低服务内部模块之间的耦合度,这样既提高了代码可读性,也便于后面的拆分。
3. 服务的交互
在微服务架构中,需要调用很多服务才能完成一项功能。服务之间如何互相调用就变成微服务架构中的一个关键问题。服务调用通常有两种技术手段:
1)服务接口:通常指基于HTTP的轻量级API,特点是同步交互,反馈及时;
2)MQ:通过第三方消息队列服务实现数据交互,特点是松耦合、高可用、支持流量削峰、开发简单、支持一次发送多端消费。
实际开发中可根据不同业务场景可选用不同的实现方式,具体可参考下表:
交互场景 | API | MQ |
A服务获取B服务数据 | A服务调动B端接口查询数据或运算。 | 不支持 |
A服务同步增删改B服务数据 | A服务调动B服务增删改数据接口。 优点:实时反馈,交互简单,支持分布式事务。 缺点:耦合度高。 适用业务场景:资源更新等对实效性要求较高、对一致性要求较高的场景。 | 不支持 |
A服务异步增删改B服务数据 (推送模式) | 1. A服务调用B服务消息接收接口,先数据落地(可进行简单的校验逻辑)。 2. B服务异步处理消息数据,执行业务数据增删改操作。特殊的,若涉及的业务字段较多,消息中可仅包含id、来源等基础字段,在处理消息时通过调用A服务查询接口,获取完整数据后再进一步处理。 优点:响应快,数据可达性好,支持具备补偿机制的分布式事务。 缺点:耦合度高,交互复杂。 适用业务场景:工作流任务处理(上游服务生成任务数据下发给下游服务处理)等场景。 | 1)A服务发送MQ。 2)B服务消费MQ并执行数据增删改操作。特殊的,若涉及的业务字段较多,消息中可仅包含id、来源等基础字段,在处理消息时通过调用A服务查询接口,获取完整数据后再进一步处理。 优点:松耦合、高可用、支持流量削峰、开发简单、支持一次发送多端消费。 缺点:实效性略差,分布式事务支持相对较差(消息数据正确性需由调用端保证,可实现最终一致性,分布式事务的实现推荐使用本地消息表+MQ的方式实现)。 适用业务场景:数据同步、任务处理等对实效性要求不太高的场景,以及通知、限流、数据分发等特殊场景。
|
A服务异步增删改B服务数据 (拉取模式) | 1)A服务记录同步消息。 2)B服务通过A服务的数据查询接口拉取同步信息,处理完成后调用A端同步成功接口,A服务把对应同步数据清空。 优点:松耦合,同步消息支持数据库事务可靠性高。 缺点:实效性差,不支持分布式事务。 适用业务场景:对实效性要求不高的数据同步等场景。 | 不支持 |
4. 数据库设计
微服务设计的一个关键是数据库设计,基本原则是每个服务都有自己单独的数据库,而且只有微服务本身可以访问这个数据库。它是基于下面三个原因。
1)服务解耦:服务间仅通过接口交互(API或消息),充分解耦,各自内部变动对外无感。如果微服务不能独享自己的数据库,那么数据库也变成了接口的一部分,这大大拓展了接口范围,无法做到充分解耦,数据结构的变动会导致其他系统的报错。
2)错误诊断:生产环境中的错误大部分都是和数据库有关的,要么是数据出了问题,要么是数据库的使用方式出了问题。当你不能完全控制数据库的访问时,会有各种各样的错误发生。它可能是别的程序直接连到你的数据库或者是其他部门直接用客户端访问数据库的数据,而这些都是在程序中查不到的,增加了错误排查难度。如果是程序中的问题,只要修改了代码,那么这个错误就不会再有。而上面提到的错误,你永远都没法预测它们什么时候还会再次发生。
3)性能调优:性能调优也是一样,你需要对数据库有全权控制才能保证它的性能。如果其他部门一定要访问数据库,而且只是查询的话,那么可以另外创建一份只读数据库,让他们在另一个库中查询,这样才不会影响到你的库。
理想情况下,你的数据库只有你的服务能访问,你也只调用自己数据库中的数据,而对外的交互都通过服务接口或MQ等方式来实现。然而,在实际应用中,单纯的服务调用和MQ可能不能满足某些特殊情况的需求,这类特殊需求主要分两类:
技术需求:某些复杂业务只能通过级联查询等复杂数据库操作才能实现(其他技术手段几乎行不通),而这种复杂的操作需要一次数据库交互中既要访问自己的表,也要访问其他服务的表。
性能需求:某些处理逻辑对性能要求较高,且此逻辑中数据获取的网络开销占比较大。
无论是以上哪类需求,通常可通过如下两种处理方式解决:
一是通过数据同步的方式将其他服务的数据同步到自己的库中,可使用数据库本身提供的同步方式或第三方同步软件或通过程序实现,但要保证同步过来的数据不可修改,若确需修改也建议使用接口方式调用数据源服务进行修改。
二是获取从库的查询权限,直接查其他服务的从库,这种实现方式实现很容易,但需要特别的注意,在享受开发的愉悦时,需要承担上述服务解耦、错误诊断、性能调优三方面的风险,这种使用方式需要技术骨干的评估,建议至少要满足:相关服务都隶属于同一个开发小团队,数据结构的变化也完全可控。
总结一下,可归结为两个凡是和一个特殊。
凡是涉及其他服务数据的写操作的,不可直连数据库,需使用接口或MQ等交互形式。
凡是涉及其他服务数据的读操作的,如果通过API获取数据即可实现业务逻辑且无性能等问题的,不可直连数据库。
特殊情况,对于通过API获取数据无法满足技术或性能等需求的,可考虑使用数据同步到本地库或直连其他服务从库的方式获取数据,但需要技术骨干评估。
引用资料:
微服务(Microservices)——Martin Flower