微服务概览、误解和误用


【编者的话】 本文从对微服务的误解误用切入,探讨什么是微服务,如何切分微服务。提出了结合传统的DDD,领域驱动设计的理念来帮助定义微服务的边界,值得一读。

【3 天烧脑式基于Docker的CI/CD实战训练营 | 北京站】本次培训围绕基于Docker的CI/CD实战展开,具体内容包括:持续集成与持续交付(CI/CD)概览;持续集成系统介绍;客户端与服务端的 CI/CD 实践;开发流程中引入 CI、CD;Gitlab 和 CI、CD 工具;Gitlab CI、Drone 的使用以及实践经验分享等。

过去的三年里,我的工作是Golang,以及云原生的分布式系统架构,包括微服务的咨询师和培训师。在微服务的咨询经验里,在不同的客户那里因为大家对微服务的误解,我遇到过很多奇怪的事情。我在印度工作,至少在这里,微服务是当下IT行业里最被误解的技术。每个人都在谈微服务。很多人说他们的应用程序是基于微服务架构的。

对微服务的误解

这里有几种我听说过的微服务相关的误解:
  • 构建HTTP服务,使用Docker容器运行它,并且使用Kubernetes做集群管理,这就是微服务。
  • 使用API Gateway和服务发现以及服务registry,这就是微服务。
  • 使用Spring Boot框架构建HTTP服务,并且使用Netflix OSS,这是微服务。(这来自于Java社区)
  • 使用Azure Service Fabric构建并且运行应用程序,这是微服务。(这来自于.Net社区)
  • 构建轻量的RESTful API,这是微服务。
  • 有很多框架声称是微服务框架。使用这些框架的任意一种来构建应用程序,这就是微服务。


有关微服务的误解还有很多很多不在上述列表中。你可能会使用上述列表里的一些技术来构建微服务,但是使用了某些工具和框架就说自己是微服务,这是没有道理的。

微服务是分布式的系统架构,但是那些从来没有开发过分布式系统的开发团队,正在试图像学习新框架那样来学习它,并且基于一些误解来构建应用程序,然后声称自己的应用是基于微服务的。可以看到很多文章的标题都是使用X(Go,Node.js,Java等等)语言,Docker和Kubernetes来构建微服务,讲解构建HTTP服务,在Docker容器里运行,并且使用Kubernetes管理集群,认为这就是微服务。最近在一次我的微服务培训里,一个认为自己是很有经验的架构师的人,告诉我他是微服务大师,知道有关微服务的所有东西。在我和他的讨论中,我意识到他从来没有听说过DDD和Bounded Context, Event Sourcing, CQRS, gRPC等等,都是从我这里第一次听到这些词。一个从来没有听说过领域建模和DDD Bounded Context的人认为自己是微服务的超级大师。。。

和我探讨过的很多创业公司,都宣传他们遵守微服务架构。在和他们的讨论中,我意识到他们称其为微服务,是因为他们的产品有2-5个运行着的应用程序,对于他们来说每个应用程序就是一个微服务。面向客户的应用程序是一个微服务,管理应用是另一个微服务,后台worker是微服务,RESTful API是另一个微服务。大家基于自己的理解以及自己习惯的方式来构建自己的微服务。让人伤心的是有人甚至没有意识到微服务是一种分布式系统架构。

什么是微服务?

首先最为重要的是,微服务是构建分布式系统的架构风格。微服务解决了构建分布式系统的复杂度。在微服务架构里,软件系统由一系列互相独立可部署,小的,模块化的服务组成。每个微服务围绕一种小型业务能力而构建,由独立团队管理。微服务是某个小型业务能力的独立可部署的组件,微服务架构是构建高度可扩展可进化的软件系统的工程风格,由细粒度的微服务组成。直观上,微服务是可以一起工作的小的,自治的服务

在绝大多数情况下,一开始应用程序是个单体应用,所有业务能力都放在单个应用程序里,运行在单个进程里。这样的应用程序很容易开发,但是很难有效地扩展,因为应用程序的每个组件都紧耦合在一起。在微服务的架构里,我们通过组合一系列的自治服务来构建应用程序,每个服务运行在自己的进程里。这样,就可以很轻松地实现扩展——可以给不同的服务赋予不同的扩展能力,独立地升级并且替代每个服务,同时获得很多其他的架构优势。

很多人认为微服务是统一的架构框架,是解决所有软件工程问题的银弹。因此大家问的问题类似于,微服务的安全是什么样的。微服务并非一种统一的架构框架,并不是软件工程中所有问题的解决方案,但是它是构建分布式系统的一种方式,这里的核心思想是服务模块化。也就是说,微服务是构建分布式系统的一种思路,需要多种架构方案来实现这样的思路。微服务是已经使用很久的多种架构方案的演进。因此在真实的微服务里,你仍然会使用之前使用过的多种架构方案。

容器化是构建并且运行微服务的很好的方式。如果可以将微服务打包进容器,那么就可以无限扩展并且动态编排微服务,而不需考虑在哪里运行。打包工具,比如Docker,rkt和编排工具,比如Kubernetes,都是容器化微服务的很好的技术。

DDD Aggregate,Bounded Context分离微服务

当从单体架构向微服务架构演进时,最大的问题是怎么将微服务分离出来。如何将一个大型软件系统分解为功能性的组件?微服务的规模多大合适?答案很简单:领域建模。Sam Newman,在他的书“构建微服务”里说,所有走向微服务的路都绕不开领域建模。Eric Evans写的在2003年出版的“领域驱动设计:在软件核心里处理复杂度”是领域建模领域的经典书籍,提供了基于领域模型构建复杂软件系统的指导概要。自从这本书出版之后,Domain-Driven Design (DDD,领域驱动设计)这一术语被业界广泛接受,并且开始使用DDD作为构建软件系统的方式。DDD,介绍了多种构建块,比如Entity,Value Object,Service,Repository,Aggregate和Bounded Context。

Aggregate和Bounded Context是构建微服务的构建块,将决定单个微服务的大小。通常业务实体,比如Order,Customer,Account是Aggregate,它是一张图,包含根实体以及一个或多个其他实体和value对象,是一个最小单元。在微服务里执行事务的更好的方式是通过aggregate实现持久化。Bounded Context是DDD的核心模式,通过将大模型分解为不同的Bounded Context,从而将业务问题从逻辑上分解成多个子域。Bounded Context封装了单个领域的细节,大型领域模型的子域,就可以剥离为单个微服务。构建微服务的常见战略就是基于每个Bounded Context构建微服务。每个微服务使用自己的数据库来持久化某个领域模型的Bounded Context。单个Bounded Context可以包含很多aggregate root,或者单个Bounded Context可以包含一个aggregate root。Order的aggregate root,包含实体包括OrderLine,Customer和Product,value object包括ShippingAdress,PaymentMethod等。在Order的aggregate root图里,Customer实体还可以作为aggregate root,因为它可以作为root实体,得到某个客户的所有信息。Aggregate和Bounded Context是微服务架构里的重要概念,这是构建微服务的基础块,也将决定微服务的大小。简单来说,微服务是围绕Bounded Context的自治服务

微服务里的挑战

微服务架构的确是构建分布式系统的绝佳方案。但是要记住微服务既不是银弹也不是一种简单的方案。即使使用了微服务来降低构建分布式系统的复杂度,构建分布式系统也绝不是一件轻松的事情。

当从单体应用程序转向微服务架构时,需要解决很多实际的挑战。比如,业务事务可能会分散为几个微服务,因为基于Bounded Context将单体系统分解为多个自治服务。当需要管理数据一致性时,事务可能需要在很多微服务里执行持久化。另一个挑战是从多个数据库里查询数据。在单体应用里,可以从单个数据库轻松地执行inner join查询。因为单体数据库在分解功能性组件时改成了多个数据库,那么就无法执行inner join,而必须从多个数据库获取数据。这时没有任何中央化的数据库。

使用Event Sourcing和CQRS构建可扩展的微服务

DDD Aggregate和Bounded Context是微服务的基础构建块,但是为构建分布式系统解决实际微服务挑战而选择具体架构时,DDD Aggregate上的事件驱动的交互系统是一种比较好的方案。这里,我强烈推荐使用Event Sourcing,是事件为中心的架构,通过组合各种事件来构成应用程序的状态。Event Sourcing处理不可变事件日志的事件存储,每条日志(某个对象的一次状态变更)代表一种应用程序的状态。因为应用程序里的每次状态变更都作为不可变日志来处理,你可以轻松地排查出应用程序的故障,也可以回到特定时间特定版本的应用程序状态。事件存储像版本控制系统一样。在微服务的架构里,我们可以将aggregate持久化成事件序列。事件即事实,代表系统里发生的一些操作。这些是不可变的,不会变更或者回退。如果想要在系统里做变更,需要往事件存储里写入新的日志来表示另一个事件集。事件的示例包括OrderCreated,OrderApproved, OrderShipped, OrderDelivered等等。在Event Sourcing架构里,当你从某个微服务发布一个事件时,其他的微服务可以响应这些事件,并且发布另外的事件集。有时候,事件序列类似于Unix pipe。微服务系统里的单个事务可能会延展到多个微服务里,可以通过构建交互式的微服务来将事务作为事件序列来执行。一个aggregate的每次状态变更都是一个事件,这是系统相关的不可变的事实。为了发布事件让其他微服务知道系统里发生的事情,可以使用消息系统,比如Apcera NATS,Kafka,RabbitMQ等等。我个人喜欢的选择是Apcera NATS和Google的Cloud Pub/Sub。事件驱动,交互式的架构是构建大规模可扩展微服务的很好的架构方案。

当使用Event Sourcing将事件序列持久化时,你可能需要一种架构方案来做微服务的查询。架构模式,Command Query Responsibility Segregation(CQRS)是实现微服务查询的理想模式。顾名思义,CQRS将应用程序分为两部分:Command来执行操作变更aggregate的状态,Query为aggregate的视图提供查询模型。我们还可以使用不同的数据库来做写操作和查询操作。这也让你能够通过将非规则数据装载进读模型的数据仓库来提供高性能的查询模型。NoSQL/NewSQL数据库是很好的读模型数据的存储方案。

虽然Event Sourcing和CQRS,是使用微服务架构实现分布式系统的很好的模式,但是它并非银弹,也有其自身的限制。你可能需要为一些类型的微服务系统的构建使用不同的架构风格。但是总的来说,我认为Event Sourcing,和CQRS的组合,是实现微服务系统的很好的方案。

使用gRPC实现基于API的进程间通信

在微服务架构里,可能需要进行很多微服务间的进程间通信。在微服务间进行进程间通信的两种方案如下:
  • 使用消息系统的异步的事件驱动架构
  • 为每秒百万次的API调用构建大规模高性能的API


通过使用带消息系统的Event Sourcing架构,你可以实现一种异步的事件驱动的架构来管理aggregate的状态。你还可能需要创建API以供微服务间通信。当你用API执行微服务间的进程间通信时,性能和可扩展性是非常重要的。应该通过构建高性能的API,从而让大家感觉不到网络上正在发生很多通信。当构建大规模可扩展的系统时,基于JSON的RESTful API不是很好的选择,因为性能挑战会很大,并且缺少暴露领域特定的操作为API的能力,因为RESTful系统是基于资源的概念的。这里,gRPC,一种高性能,开源的远程过程调用(RPC)框架,可以用来为微服务间的进程间通信构建大规模可扩展的API。默认来说,gRPC使用Protocol Buffer作为接口定义语言(IDL),并且作为底层消息交换格式。gRPC是Google的内部框架,Stubby的开源版本,它用来扩展支撑每秒100亿次的API调用。gRPC是微服务架构里基于API的进程间通信的协议。

微服务的误解和误用

正如本文一开始所说,对于微服务有很多误解,来自于多个开发者社区。我曾经收到过来自不同客户的要求举办微服务workshop的请求,很多时候,客户会提出课程大纲,这非常让人吃惊,因为大纲上没有任何关于微服务的东西。很多开发者社区将微服务和一些框架和工具绑定在一起。很多Java开发人员坚信使用Spring Boot构建RESTful API并且利用Netflix OSS的一些工具就是微服务,而另一些来自.NET社区的人认为使用Azure Service Fabric运行的应用程序是微服务。也有人问我RESTful服务和微服务的区别,因为很多人认为构建一些轻量的RESTful API就是微服务。另外一些人认为使用Spring Boot构建API是微服务。我也不知道从哪里开始Spring Boot被误解为微服务的。

我感觉到大家只是听到了微服务这个词,并且将它和一些工具以及框架联系在一起,并没有理解这种变革性架构的真正精神和意图。这样的情况和架构模式,比如SOA以及工程实践,比如Agile很是类似。我知道很多公司声称自己遵照敏捷工程实践,仅仅因为他们使用了Scrum流程,而其实他们仍然以非常传统的方式构建应用程序。TDD也是一样,很多企业在编写生产代码之后才写单元测试,然后尝试得到想要达到的测试覆盖率,就认为自己在使用TDD开发应用程序。即使是一个小众社区,但是很多人在引入技术,模式和实践的时候,仅仅是为了声称使用了这些东西而已。

微服务的确是构建大规模可扩展应用程序的伟大的架构风格。微服务还适合构建互联网规模的应用程序,比如Netflix,Uber,Amazon,eBay等等。但是它不是所有人都适合使用的,也并非银弹。虽然微服务是构建大规模可扩展应用程序,包括面向大众的互联网级别的应用程序的正确的架构方案,但是对于大多数其他类型的应用程序,特别是构建企业级应用程序(有复杂的领域模型)来说,它可能是错误的选择。我个人不推荐用微服务来做复杂的业务应用,因为这些应用有非常复杂的领域模型。但是微服务是领域模型没那么复杂的,大规模可扩展的,并且可扩展是最具挑战因素,也是成功要素的应用程序的理想选择。对于一些类型的应用程序,微服务架构风格的实现可能会带来很多性能问题,并且增加了系统的复杂度,最终可能导致失败。仅仅因为和热门技术沾边就使用微服务可能会给你的系统带来很多副作用。

改进单体应用程序

在我的咨询经验里,我认为在绝大多数用例里,不需要使用微服务架构,适合大多数应用程序的更好的方案是折中方案。你可以使用混合方案来解决问题。你的企业可能并没有Google,Amazon,Netflix,Uber,eBay,Square等公司这样的技术能力。在你的公司里你的问题是独特的。架构方案必须要能够解决问题,并且使用它解决你自己的独特问题,而不是为了用而用。

你可以按照如下方式改进已有的单体应用程序:将单体应用程序分解为多个系统,而不是盲目遵守微服务的概要,选择使用混合架构方案,使用DevOps文化以及现代化的CI/CD pipeline。微服务的最大优势是模块化。当构建新的应用程序时,可以通过应用模块化系统设计原则,而不是微服务架构,来架构系统。比如,Go编程语言的打包生态系统让你能够使用更好的模块化思路来设计应用程序。如果之后你想要迁移到微服务来独立扩展模块和团队的话,该方案还让你能更容易地迁移到微服务上。最好是从使用模块化系统设计原则实现单体应用开始,然后当系统和团队发展时,可以在确实需要的时候转向微服务架构。

要记住你的公司和产品是独一无二的。不要成为技术,模式和实践的盲目追随者,使用折中方案解决自己的问题。为了使用技术而使用是没有任何意义的。

原文链接:Microservices: Overview, Misinterpretations and Misuses(翻译:崔婧雯)
===========================
译者介绍
崔婧雯,现就职于IBM,高级软件工程师,负责IBM WebSphere业务流程管理软件的系统测试工作。曾就职于VMware从事桌面虚拟化产品的质量保证工作。对虚拟化,中间件技术,业务流程管理有浓厚的兴趣。

0 个评论

要回复文章请先登录注册