微服务
有关这个新的技术架构术语的定义
“微服务架构”这个术语最近几年横空出世,来描述这样一种特定的软件设计方法,即以若干组可独立部署的服务的方式进行软件应用系统的设计。尽管这种架构风格尚无精确的定义,但其在下述方面还是存在一定的共性,即围绕业务功能的组织、自动化部署、端点智能、和在编程语言和数据方面进行去中心化的控制。
2014年3月25日
作者:
James Lewis是思特沃克首席咨询师,而且是该公司的技术顾问委员会成员。James对于采用相互协作的小型服务来构建应用系统的兴趣,源自于他的整合大规模企业系统的工作背景。他已经使用微服务构建了许多系统,而且几年以来已经成为正在成长的微服务社区的积极参与者。
Martin Fowler是一位作者和演讲者,并且在软件开发行业中,他通常是最能说的那一位。他长期以来一直困惑于这样的问题,即如何才能将软件系统进行组件化。声称已将软件进行组件化的声音他听到了很多,但是很少有能让他满意的。他希望微服务不要辜负其倡导者对它的最初的期望。
目录
微服务架构的九大特性
特性一:“组件化”与“多服务”
特性二:围绕“业务功能”组织团队
特性三:“做产品”而不是“做项目”
特性四:“智能端点”与“傻瓜管道”
特性五:“去中心化”地治理技术
特性六:“去中心化”地管理数据
特性七:“基础设施”自动化
特性八:“容错”设计
特性九:“演进式”设计
未来的方向是“微服务”吗?
扩展阅读
一个微服务应该有多大?
微服务与SOA
语言越多,选项越多
“实战检验”的标准与“强制执行”的标准
让“方向正确地做事”更容易
"断路器"与“可随时上线的代码”
“同步调用”有害
“微服务”——这是在软件架构这条熙熙攘攘的大街上出现的又一个新词儿。我们自然会对它投过轻蔑的一瞥,但是这个小小的术语却描述了一种引人入胜的软件系统的风格。最近几年,我们已经看到许多项目使用了这种风格,而且至今其结果都是良好的,以至于对于我们许多思特沃克的同事来说,它正在成为构建企业应用系统的缺省的风格。然而,很不幸的是,我们找不到有关它的概要信息,即什么是微服务风格,以及如何设计微服务风格的架构。
简而言之,微服务架构风格[1]这种开发方法,是以开发一组小型服务的方式来开发一个独立的应用系统的,其中每个小型服务都运行在自己的进程中,并经常采用HTTP资源API这样轻量的机制来相互通信。这些服务围绕业务功能进行构建,并能通过全自动的部署机制来进行独立部署。这些微服务可以使用不同的语言来编写,并且可以使用不同的数据存储技术。对这些微服务我们仅做最低限度的集中管理。
在我的“微服务资源指南”中(http://martinfowler.com/microservices/)能找到有关微服务最好的文章、视频、图书和播客媒体。
在开始介绍微服务风格之前,将其与单块(monolithic)风格进行对比还是很有用的:一个单块应用系统是以一个单个单元的方式来构建的。企业应用系统经常包含三个主要部分:客户端用户界面、数据库和服务端应用系统。客户端用户界面包括HTML页面和运行在用户机器的浏览器中的JavaScript。数据库中包括许多表,这些表被插入一个公共的且通常为关系型的数据库管理系统中。这个服务端的应用系统就是一个单块应用——一个单个可执行的逻辑程序[2]。对于该系统的任何改变,都会涉及构建和部署上述服务端应用系统的一个新版本。
这样的单块服务器是构建上述系统的一种自然的方式。处理用户请求的所有逻辑都运行在一个单个的进程内,从而能使用编程语言的基本特性,来把应用系统划分为类、函数和命名空间。通过精心设计,就能在开发人员的笔记本电脑上运行和测试这样的应用系统,并且使用一个部署流水线来确保变更被很好地进行了测试,并被部署到生产环境中。通过负载均衡器运行许多实例,来将这个单块应用进行横向扩展。
单块应用系统可以被成功地实现,但是渐渐地,特别是随着越来越多的应用系统正被部署到云端,人们对它们开始表现出不满。软件变更受到了很大的限制,应用系统的一个很小的部分的一处变更,也需要将整个单块应用系统进行重新构建和部署。随着时间的推移,单块应用开始变得经常难以保持一个良好的模块化结构,这使得它变得越来越难以将一个模块的变更的影响控制在该模块内。当对系统进行扩展时,不得不扩展整个应用系统,而不能仅扩展该系统中需要更多资源的那些部分。
图1: 单块应用和微服务
这些不满导致了微服务架构风格的诞生:以构建一组小型服务的方式来构建应用系统。除了这些服务能被独立地部署和扩展,每一个服务还能提供一个稳固的模块边界,甚至能允许使用不同的编程语言来编写不同的服务。这些服务也能被不同的团队来管理。
我们并不认为微服务风格是一个新颖或创新的概念,它的起源至少可以追溯到Unix的设计原则。但是我们觉得,考虑微服务架构的人还不够多,并且如果对其加以使用,许多软件的开发工作能变得更好。
[1]2011年5月在威尼斯附近的一个软件架构工作坊中,大家开始讨论“微服务”这个术语,因为这个词可以描述参会者们在架构领域进行探索时所见到的一种通用的架构风格。2012年5月,这群参会者决定将“微服务”作为描述这种架构风格的最贴切的名字。在2012年3月波兰的克拉科夫市举办的“33rd Degree”技术大会上,James在其“Microservices - Java, the Unix Way”演讲中以案例的形式谈到了这些微服务的观点(http://2012.33degree.org/talk/show/67),与此同时,Fred George也表达了同样的观点(http://www.slideshare.net/fredgeorge/micro-service-architecure)。Netflix公司的Adrian Cockcroft将这种方法描述为“细粒度的SOA”,并且作为先锋和本文下面所提到的众人已经着手在Web领域进行了实践——Joe Walnes, Dan North, Evan Botcher 和 Graham Tackley。
[2]"单块"(monolith)这个术语已经被Unix社区使用一段时间了。它出现在The Art of Unix Programming一书中,来描述那些变得庞大的系统。
微服务架构的九大特性
虽然不能说存在微服务架构风格的正式定义,但是可以尝试描述我们所见到的能够被贴上微服务标签的那些架构的共性。下面所描述的所有这些共性,并不是所有的微服务架构都完全具备,但是我们确实期望大多数微服务架构都具备这些共性中的大多数特性。尽管我们两位作者已经成为这个相当松散的社区的活跃成员,但我们的本意还是试图描述我们两人在自己和自己所了解的团队的工作中所看到的情况。特别要指出,我们不会制定大家需要遵循的微服务的定义。
特性一:“组件化”与“多服务”
自我们开始从事软件业已来,发现大家都有一个把组件插在一起来构建系统的愿望,就像在物理世界中所看到的那样。在过去几十年中,我们已经看到,在公共软件库方面已经取得了相当大的进展,这些软件库是大多数编程语言平台的组成部分。
当谈到组件时,就会碰到一个有关定义的难题,即什么是组件?我们的定义是,一个组件就是一个可以独立更换和升级的软件单元。
微服务架构也会使用软件库,但其将自身软件进行组件化的主要方法是将软件分解为诸多服务。我们将软件库(libraries)定义为这样的组件,即它能被链接到一段程序,且能通过内存中的函数来进行调用。然而,服务(services)是进程外的组件,它们通过诸如web service请求或远程过程调用这样的机制来进行通信(这不同于许多面向对象的程序中的service object概念[3])。
以使用服务(而不是以软件库)的方式来实现组件化的一个主要原因是,服务可被独立部署。如果一个应用系统[4]由在单个进程中的多个软件库所组成,那么对任一组件做一处修改,都不得不重新部署整个应用系统。但是如果该应用系统被分解为多个服务,那么对于一个服务的多处修改,仅需要重新部署这一个服务。当然这也不是绝对的,一些变更服务接口的修改会导致多个服务之间的协同修改。但是一个良好的微服务架构的目的,是通过内聚的服务边界和服务协议方面的演进机制,来将这样的修改变得最小化。
以服务的方式来实现组件化的另一个结果,是能获得更加显式的(explicit)组件接口。大多数编程语言并没有一个良好的机制来定义显式的发布接口(Published Interface,http://martinfowler.com/bliki/PublishedInterface.html)。通常情况下,这样的接口仅仅是文档声明和团队纪律,来避免客户端破坏组件的封装,从而导致组件间出现过度紧密的耦合。通过使用显式的远程调用机制,服务能更容易地避免这种情况发生。
如此这般地使用服务,也会有不足之处。比起进程内调用,远程调用更加昂贵。所以远程调用API接口必须是粗粒度的,而这往往更加难以使用。如果需要修改组件间的职责分配,那么当跨越进程边界时,这种组件行为的改动会更加难以实现。
近似地,我们可以把一个个服务映射为一个个运行时的进程,但这仅仅是一个近似。一个服务可能包括总是在一起被开发和部署的多个进程,比如一个应用系统的进程和仅被该服务使用的数据库。
[3]许多面向对象的设计者,包括我们自己,都使用领域驱动设计中service object这个术语,来描述那种执行一段未被绑定到一个entity对象上的重要的逻辑过程的对象。这不同于本文所讨论的"service"的概念。可悲的是,service这个术语同时具有这两个含义,我们必须忍受这样的多义词。
[4]我们认为一个应用系统是一个社会性的构建单元(http://martinfowler.com/bliki/ApplicationBoundary.html),来将一个代码库、功能组和资金体(body of funding)结合起来。
特性二:围绕“业务功能”组织团队
当在寻求将一个大型应用系统分解成几部分时,公司管理层往往会聚焦在技术层面上,这会导致组建用户界面团队、服务器端团队和数据库团队。当团队沿着这些技术线分开后,即使要实现软件一个简单的变更,也会导致跨团队的项目耗时和预算审批。在这种情况下,聪明的团队会进行局部优化,“两害相权取其轻”,来直接把代码逻辑塞到他们能访问到的任意应用系统中。换句话说,这种情况会导致代码逻辑散布在系统各处。这就是康威定律[5]在起作用的活生生的例子。
任何设计(广义上的)系统的组织,都会产生这样一个设计,即该设计的结构与该组织的沟通结构相一致。——梅尔文•康威(Melvyn Conway), 1967年
图2:康威定律在起作用
微服务使用不同的方法来分解系统,即根据业务功能(business capability)来将系统分解为若干服务。这些服务针对该业务领域提供多层次广泛的软件实现,包括用户界面、持久性存储以及任何对外的协作性操作。因此,团队是跨职能的,它拥有软件开发所需的全方位的技能:用户体验、数据库和项目管理。
以上述方式来组织团队的公司是www.comparethemarket.com。跨职能团队负责构建和运维每个产品,而每个产品被拆分为多个独立的服务,彼此通过一个消息总线来通信。
大型单块应用系统也可以始终根据业务功能来进行模块化设计,虽然这并不常见。当然,我们会敦促构建单块应用系统的大型团队根据业务线来将自己分解为若干小团队。在这方面,我们已经看到的主要问题是,他们往往是一个团队包含了太多的业务功能。如果这个单块跨越了许多模块的边界,那么这个团队的每一个成员都难以记忆所有这些模块的业务功能。此外,我们看到这些模块的边界需要大量的团队纪律性来强制维持。而组件化的服务所必要的更加显式的边界,能更加容易地保持团队边界的清晰性。
一个微服务应该有多大?
尽管对于这种架构风格,“微服务”已经成为一个流行的名字,但是这个名字确实会不幸地导致大家对服务规模的关注,并且产生了有关什么是“微”的争论。在与微服务实践者的交谈中,我们看到了有关服务的一系列规模。所听到的最大的一个服务的规模,是遵循了亚马逊的“两个比萨团队”(即一个团队可以被两个比萨所喂饱)的理念,这意味着这个团队不会多于12人。对于规模较小的服务,我们已经看到一个6人的团队在支持6个服务。
这引出了一个问题,即“每12人做一个服务”和“每人做一个服务”这样有关服务规模的差距,是否已经大到不能将两者都纳入微服务之下?此时,我们认为最好还是把它们归为一类,但是随着进一步探索这种架构风格,绝对有可能我们将来会改变主意。
[5]原始论文参见梅尔文•康威的网站:http://www.melconway.com/Home/Committees_Paper.html
特性三:“做产品”而不是“做项目”
我们所看的的大部分应用系统的开发工作都使用项目模型:目标是交付某一块软件,之后就认为完工了。一旦完工后,软件就被移交给维护团队,接着构建该软件的项目团队被解散。
微服务的支持者们倾向于避免使用上述模型,而宁愿采纳“一个团队在一个产品的整个生命周期中都应该保持对其拥有”这样的理念。通常认为这一点源自亚马逊的“谁构建,谁运行”(https://queue.acm.org/detail.cfm?id=1142065)的理念,即一个开发团队对一个在生产环境下运行的软件负全责。这会使开发人员每天都会关注软件是如何在生产环境下运行的,并且增进他们与用户的联系,因为他们必须承担某些支持工作。
这样的“产品”理念,是与业务功能的联动绑定在一起的。它不会将软件看作是一个待完成的功能集合,而是认为存在这样一个持续的关系,即软件如何能助其客户来持续增进业务功能。
当然,单块应用系统的开发工作也可以遵循上述“产品”理念,但是更细粒度的服务,能让服务的开发者与其用户之间的个人关系的创建变得更加容易。
特性四:“智能端点”与“傻瓜管道”
当在不同的进程之间构建各种通信结构时,我们已经看到许多产品和方法,来强调将大量的智能特性纳入通信机制本身。
http://mp.weixin.qq.com/s?__biz=MjM5MjEwNTEzOQ==&mid=401500724&idx=1&sn=4e42fa2ffcd5732ae044fe6a387a1cc3&scene=23&srcid=0216xSzS2b6tHxiRS5su0N9N#rd