首页 [架构] 微服务演进历史回顾和服务拆分、服务接口的一些思考
文章
取消

[架构] 微服务演进历史回顾和服务拆分、服务接口的一些思考

微服务架构的特点

微服务架构风格大家基本上耳熟能详,有着显著的特点,包括并不限于:

  • 应用组成:应用中包含多个小型组件或者服务。
  • 部署特点:每个应用均可独立部署。
  • 松散耦合:服务之间松散耦合,以API、RPC或者中间件方式进行通信。
  • 开放技术栈:每个服务可以使用不同的技术栈实现,包括编程语言、库和开发工具。

微服务架构的演进历史回顾

和其他许多架构方法一样,微服务并非一夜之间出现并大火,而是有具体背景催生,并不断演化而来。

单体架构

一把梭,所有代码和业务实现均集中在单个工程内。适用场景:

  • 业务简单,单人能够理解全局信息
  • 流量少,单实例即可满足
  • 参与开发的人员少,基本不存在编辑冲突
  • 原型验证,不需要考虑直接实用化

在很多简单场景下如工业自动化软件、嵌入式软件,单体架构的应用应用仍然十分广泛。

显著问题:

  • 随着功能迭代,项目会变得臃肿,业务耦合严重。新增业务变得困难,核⼼业务与边缘业务互相影响

集群化和垂直化架构

主要思路是进行子系统分治,组合成一个更复杂、并发更高的系统。

  • 通过子系统拆分、垂直化部署和负载均衡,方便横向扩容解决并发问题。
  • 可以针对不同子系统模块进行优化。
  • 子系统之间相互独立,互不影响。

问题在于:

  • 服务间的相互调用依赖硬编码或者静态配置。
  • 服务之间调用协议和接口不统一

SOA架构

SOA(Service Oriented Architecure,面向服务的架构)经考古是二十世纪九十年代后期出现的架构(那时候我刚出生?),SOA 定义了通过服务接口复用软件组件的方法,此类接口会使用通用的通信标准,这些标准能够快速合并到新应用程序中,而不必每次都执行深度集成。SOA架构有几个显著特点:

  • 基于SOA架构思想,将重复的功能抽取为组件,以服务的形式在向各系统提供服务。
  • 使用企业服务总线(ESB)集成不同的组件和服务。
  • 系统与服务之间使用rpc、web service等方式进行通信。

什么是 ESB(企业服务总线)? ESB(企业服务总线)是一种模式,可让集中式软件组件执行后端系统集成(以及数据模型转换、深度连接、路由和请求),并将这些集成和转换作为服务接口提供,以供新应用程序复用。 通常使用专用的集成运行时和工具集来实施 ESB 模式,以确保最佳的生产力。

优点:

  • 集中式 ESB 有可能标准化和大幅简化整个企业中服务的通信及集成。
  • 服务层的存在提高了可重用性、可维护性,提高了开发效率
  • ESB使模块间的通信减少了耦合
  • ESB兼容不同协议使得集成更加灵活

缺点:

  • ESB容易成为SOA部署、变更活动中的瓶颈,更改或增强某个集成通常会干扰到其他集成
  • ESB对种类繁多协议的集成,使得ESB在后期变得臃肿难以维护。

对于互联网行业,随着分布式应用逐渐普及,SOA似乎逐渐成为了过去式,但在特定的领域如汽车制造,为了实现软件定义汽车,汽车软件正在从传统的”面向信号”开发范式,转型升级为SOA架构。汽车行业很可能复制PC和智能手机的“底层硬件、中间层操作系统、上层应用程序”的软件分工模式,工业级的SOA框架使得上层APP开发者无须关注底层硬件架构和模块交互细节,而专注于应用开发。

微服务架构

在SOA架构下虽然能解决大部分的问题,随着现在软件系统的复杂性逐渐增加,ESB本身的瓶颈变得突出,服务层的拆分粒度又比较粗放,因此微服务架构方法对SOA架构进行了拓展:

  • 将服务按照单一职责原则拆分得更细,服务更独⽴,不同的服务可以使⽤不同的开发语⾔和存储
  • 服务之间通过Restful、RPC方式进行轻量级通信。微服务架构关键在于微⼩、独⽴、轻量级通信。
  • 去除了集中式的ESB,使用更加松散的服务注册中心、配置中心、服务网关等治理组件降低服务治理的成本和难度。

优点:

  • 模块内聚:模块功能变得聚焦,易于实现,有利于产品快速迭代
  • 技术栈开放:技术栈相对开放,降低团队合作成本
  • 提高复用:复用性提高、服务组件可维护性提高
  • 便于伸缩:能够相对精准调整每个服务的资源和副本数目,便于伸缩

缺点:

  • 服务拆分粒度过细后,模块增多,服务调用过程复杂度增加,服务治理难度变大
  • 服务模块增多,对运维、测试工作形成挑战
  • 服务调用链路变长,调试跟踪变得复杂

微服务拆分粒度的控制

追一的Bot产品(文本对话机器人)2.x版本是过度拆分的鲜活例子,这个系统:

  • 模块拆分过细而且多:50~60多个服务模块
  • 业务功能可能由多个模块才能完整实现:如对话渲染业务需要smu和adaptor的业务代码配合实现。
  • 服务模块实现的技术栈繁多:就实现业务服务模块来说,有node.js、java springboot、golang、c++、python等多套技术栈。
  • 服务链路巨长:跟踪调试需要在前端node服务模块、java业务模块、bot在线模块、bot离线模块内的多个服务实例间跟踪,而分布式多实例的部署,更增加了跟踪的难度。
  • 前后端实现在一个模块:因为使用node.js同一个语言技术栈实现,所以当初就糅杂在一个模块和运行实例中。

除了上述和拆分不当相关的特点,还有诸多系统设计上的问题,使得这个版本到了后期维护成本已经变得不可接受,最终需要彻底地重构才最终解决其中的绝大部分问题。回到微服务拆分粒度控制这个话题上,我觉得相对具体衡量的标准有以下几点:

  • 技术栈分离:极可能避免使用不同的技术栈实现同一个服务模块
  • 业务和能力分离:定义业务领域(这个部分可以参考DDD的思想),将能力抽象化、平台化
  • 前后端分离:实现前后端解耦,在实际部署和运维的时候,前后端往往处于不同的网络域。
  • 服务正交依赖:避免双向依赖、环形依赖。
  • 服务无状态:尽可能实现无状态服务,便于实现灵活的伸缩。
  • 数据最小化传输:最小化数据传输、无直接数据共享。

在服务接口部分,则最好是gRPC严格定义(如使用ProtoBuffer定义,gRPC或http调用)而非宽松定义(如http+json定义),gRPC在各个平台和实现中都是一致的,消除了争论并为开发人员节省了时间;另一个重要原因实ProtoBuffer可以轻松为各个技术栈生成相对统一的接口代码,而ProtoBuffer又是便于进行版本控制管理的,因此接口定义和实现的版本统一控制成为了可能,这正是http+json松散接口定义的痛点。

在实际的工程化中,gRPC-Gateway可以基于proto文件生成反向代理(Reverse Proxy)的代码,这个反向代理运行起来后,对外提供RESTful服务,收到RESTful请求后通过gRPC调用原来的gRPC服务,实现编写一次proto定义,却可以同时支持统一的gRPC 和 HTTP/JSON API实现。因此gRPC-Gateway几乎是必用的插件,毕竟对大人来说,“我全都要”。

参考

  • https://www.ibm.com/cn-zh/cloud/learn/microservices
  • https://developer.ibm.com/articles/cl-lightweight-integration-1/
本文由作者按照 CC BY 4.0 进行授权