首页 > 代码库 > Martin Fowler关于微服务的原文翻译(一)

Martin Fowler关于微服务的原文翻译(一)

原文如下:http://martinfowler.com/articles/microservices.html

微服务

一个新的架构术语

“微服务架构”一词是在过去几年里涌现出来的,它用于描述一种独立部署的软件应用设计方式。这种架构方式并没有非常明确的定义,但有一些共同的特点就是围绕在业务能力、自动化布署、端到端的整合以及语言和数据的分散控制上面。

“微服务”- 这是在软件架构领域这个非常拥挤的街道上,冒出的一个新名词而已。虽然我们对这个新出的名词不屑一顾,但是它所描述的软件系统的风格越来越吸引我们的注意力。在过去的几年里,我们发现越来越多的项目开始使用这个风格,并且到目前为止得到的反馈都是积极的,以至于我身边的许多同事在设计企业架构时,都把它作为默认的构建方式,然而很不幸,到底什么是微服务,我们又如何来使用它,外界并没有太多的信息可供参考。

总之,微服务这种架构风格就是把一组小服务演化成为一个单一的应用的一种方法。每个应用都运行在自己的进程中,并通过轻量级的机制保持通信,就像HTTP这样的API。这些服务要基于业务场景,并使用自动化布署工具进行独立的发布。可以有一个非常轻量级的集中式管理来协调这些服务,可以使用不同的语言来编写服务,也可以使用不同的数据存储。

在开始解释什么是微服务之前,先介绍一下单体应用还是很有用的:把一个单体应用构建成为一个部分。企业应用通过是由三个重要部分组成:客户端界面(由HTML、Javascript组成,使用浏览器进行访问)、数据库(由许多的表组件构成一个通用的、相互关联的数据管理系统)、服务端应用。服务端应用处理HTTP请求、执行领域逻辑、检索并更新数据库中的数据、选择和填充HTML视图发送给客户端。这个服务端应用是一个单块结构也就是一个整体,这是一个可执行的单一逻辑,系统中的任何修改都将导致服务端应用重新编译和布署一个新版本。

就这样一个单体应用很自然的被构建成了一个系统,虽然可以使用开发语言基本特性会把应用封装成类、函数、命名空间,但是业务中所有请求都要在单一的进程中处理完成,在某些场景中,你可以在开发人员的笔记本电脑中运行和测试,并且通过布署通道将测试通过的程序布署到生产环境中,你还可以水平扩展,利用负载均衡将实例布署到多台服务器中。

的确,单体应用也是很成功的,但是越来越多的人感觉到了不妥,特别是应用程序被发布到了云的时候,变更发布周期被绑定了 —- 原来可以划分成小的应用、小的需要的变更,需要统一的进行编译和发布。随着时间的推移,软件开发者很难保持原有好的模块架构,使得一个模块的变更很难不会影响到其它的模块,而且在扩展方面也只能进行整体的扩展,而不能根据进行部分的扩展。
技术分享

这些原因导致了微服务架构风格的出现:以服务构建应用。这些服务还可以被独立布署、独立扩展,每个服务也都提供了清晰的模块边界,甚至不同的服务都可以使用不同的编程语言来实现,也可以由不同的团队进行管理。

微服务的概念不是我们发明的,它至少起源于Unix时代的设计原则,我们认为这种风格所带来的好处,并没有引起足够多人的重视。

微服务架构特征

我们没有办法对微服务有一个正式的定义,但我们可以尝试表述适合这种架构的共同特征来给它打上特性标签,共同特性并不代表每个服务都具备这些特点,但是我们真的期望大多数微服务架构能具备其中大部分特点。虽然我们的作者已经是松散社区的核心成员,但是我们也在尝试描述我们工作中或者我们了解的组件中所理解的微服务。我们并不依赖于那些已经明确过的定义。

组件化与服务

只要我们一直在从事软件行业,我们的愿望就是,软件由很多组件组装在一起,如同物理现实世界中类似的构造方式。在过去的几十年里,我们已经看到了大部分语言平台公共库有了长足的进步。

当我们在谈论组件时,我们遇到了组件定义方面的困难,我们给定的定义是:一个组件是软件中的一个部分,可以独立的替换和升级。

微服务也会使用组件库,将一个软件组件化的主要方式就是将其分解成服务,我们定义的库是可以连接到程序并使用内存函数的的组件库,服务是进程外的组件,如Web请求服务或者远程调用来相互通信的组件。(这种定义的方式与其它面向对象程序中服务对象的概念是不一样的。)

把服务当成组件(而不是组件库)的一个原因是服务可以独立布署,如果你有一个应用是由多个库组成并且运行在一个进程中,那么任何一点的改变都会引起整个应用的重新发布,但是将这个应用拆解为多个服务,你可以期待每个服务的变更仅需要发布相应的服务就可以,当然这也不是绝对的,比如导致服务接口变更的更新就需要相应服务的变化,但是良好的架构设计是通过聚合服务边界并且按照合约实现服务演化,最大限度地减少因为改变影响其他地方。

把服务当成组件的另一个考虑是这会拥有更加清晰的接口,大多数的语言并没有一个很好的机制来定义一个明确显式的发布接口,通常只有文档和规范说明,让用户避免组件间过度紧密而导致高耦合,通过显示的远程调用机制,可以避免这种情况。

使用服务也有其自身的缺点,远程调用比进程内部调用更加消耗性能,而且远程的API往往是粗粒度的,用起来不是很友好,对组件的职责进行变更,也会影响到进程间的交互,那么操作起来也比较困难。

第一个可能性,我们看到每个服务是运行在独立的进程上的。注意,这只是第一个可能性。服务也可以由多个进程组成,它们是同时开发和部署的,如果一个应用进程和一个仅由该服务使用的数据库。

围绕业务能力进行组织

当我们把一个大的应用拆分成小的部分时,我们的注意力主要集中在技术层面,拆分成UI团队、服务端的逻辑团队和数据库团队。当使用这种标准对团队进行划分时,甚至一个非常小的更变都将导致跨团队间项目协作,从而消耗时间和预算审批。一个高效的团队会针对这种情况进行改善,关注它们所涉及的应用逻辑,并从中做出较好的选择。换句话说,逻辑无处不在。康威定律就是一个例子。

一个组织的沟通结构反映了其设计的系统的结构

-- Melvyn Conway, 1967

技术分享

微服务的划分方法有所不同,它更倾向于围绕业务功能对服务结构进行划分、拆解,这些服务可以采用不同的技术栈来实现,包括用户界面,持久层存储,或任何对外协作,因此团队应该是跨职能的,包括开发所需要的全部技术:用户体验、数据库和项目管理。

技术分享

按照这种方式组织的公司是 www.comparethemarket.com,跨职能团队负责建立和操作每个产品并且每个产品都被分成若干单独的服务通过消息进行通信。

大型的单体应用也可以按照业务功能进行模块化的,尽管这种例子不常见。当然,我们也会敦促一个大型团队在构建一个单体应用时按照业务线来进行划分,我们能看到主要问题在于,这种组件形式会导致很多的上下文依赖,如果这个系统跨越很多模块边界,对于一个单独团队是很难在短时间解决问题的。此外,我们发现模块化方式需要大量的规范去强制执行,而服务组件明确的划分,使得团队间的边界也变得清晰起来。

产品不是项目

大多数的开发工作是使用这样一种模型:其目的是完成可以交付的软件,软件开发完成就交给了维护团队,该项目组也就解散了。

微服务的支持者建议避免这种模型,认为一个团队应该负责产品的整个生命周期,一个很通用的概念就是Amazon’s的“you build, you run it”,它要求开发团队对软件产品的整个生命周期负责,这使得开发人员可以每天都关注产品的运行情况,而且也能够与用户保持紧密的联系,做一些必要的支持工作。

产品方式开发意味着与业务能力紧紧捆绑在一起,而不是将软件看成是一系列完成的功能,他们会关注如何让软件帮助其用户提升业务能力。

单体应用也可以采用上述产品的理念,但是更小粒度的服务可以更容易的创建开发者与用户之间的关系。

智能终端与弱管道

当在不同的进程之间构建各种通信结构时,我们已经看到许多产品和方法,来强调将大量的智能特性融入到通信机制本身,这种情况的一个典型例子就是“企业服务总线”(Enterprise Service Bus,ESB)。ESB产品经常包括高度智能的设施来进行消息的路由、编排、转换,并应用业务规则。

微服务社区主张采用另一种做法:智能终端和弱管道。使用微服务所构建的各个应用的目标都是尽可能实现“高内聚和低耦合”–他们拥有自己的领域逻辑,并且更多的是经典的UNIX的“过滤器”那样工作–即接收请求、处理逻辑、返回响应,这些应用通过使用简单的REST风格的协议来进行编排,而不去使用复杂的协议,比如:WS、BEPL或者集中式工具进行编排。

有两种协议最经常被使用到:包含资源API的HTTP的请求-响应和轻量级消息通信协议。最为重要的建议为:

微服务团队采用这样的原则和规范:基于互联网(广义上,包含Unix系统)构建系统。这样经常使用的资源几乎不用什么的代价就可以被开发者或者运行商缓存。

第二种做法是通过轻量级消息总线来发布消息。这种的通信协议非常的单一(单一到只负责消息路由),像RabbitMQ或者ZeroMQ这样的简单的实现甚至像可靠的异步机制都没提供,以至于需要依赖产生或者消费消息的终端或者服务来处理这类问题。

在一个单块系统中,各个组件在同一个进程中运行。它们相互之间的通信,要么通过方法调用,要么通过函数调用来进行。将一个单块系统改造为若干微服务的最大问题,在于对通信模式的改变。仅仅将内存中的方法调用转换为RPC调用这样天真的做法,会导致微服务之间产生繁琐的通信,使得系统表现变糟。取而代之的是,需要用更粗粒度的协议来替代细粒度的服务间通信。

<script type="text/javascript"> $(function () { $(‘pre.prettyprint code‘).each(function () { var lines = $(this).text().split(‘\n‘).length; var $numbering = $(‘
    ‘).addClass(‘pre-numbering‘).hide(); $(this).addClass(‘has-numbering‘).parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($(‘
  • ‘).text(i)); }; $numbering.fadeIn(1700); }); }); </script>

    Martin Fowler关于微服务的原文翻译(一)