微服务:构建高可伸缩应用

当了10年码农,却尚未写过Web后端。最近可能要上Web项目,先从架构入手,赶忙先充充电,以为一篇文章写得还不错,分享一下。html

http://www.infoq.com/articles/microservices-intro前端

本文描述了当前正快速流行的微服务架构。微服务背后的主要思想是将大型的、复杂的、且须要长期演进的应用分解为一个个相互做用且不断演进的服务。顾名思义“微服务”这个词的重点在于服务必须“微”小。git

对于微服务的规模,有些社区建议一个服务应该在10到100逻辑行之间,这不是关键,由于咱们作微服务的目的在于想要解决一些实际问题,这些开发、部署相关的问题咱们会在下面讨论到。因此有些服务可能很是小型,而有些可能有点大。github

微服务并非什么很新的理念,相似的理念如分布式系统和SOA已经出现好久,甚至微服务也能够叫作轻量级SOA。事实上你能够把微服务理解为没有ESB和WS的SOA。数据库

使用微服务架构的主要动机是什么?他和传统的总体集成架构(monolithic)相比怎样?微服务架构有什么优缺点?在微服务架构中,如何解决服务间通讯和分布式数据管理等关键技术问题?尽管微架构不是什么新颖的想法,但由于它和传统的SOA不尽相同,仍是值得对其进行探讨。固然探讨的最终目的是想要解决当下不少Team面临的不少问题。apache

1.总体集成架构

早期的WEB企业应用大多将全部的服务端组件都放到一块。对于Java企业应用来讲可能就是一个WAR或EAR文件,其余语言(如Ruby或C++)开发的应用也相似。编程

设想一下咱们要作一个图1的网店应用,包括订单、库存、信用卡和物流等管理功能。后端

这个应用会包含前端和后端。前端包括用户界面和操做,后端包括商品管理、订单管理、帐户管理等服务,全部后端服务会共享商品、订单、用户等业务数据设计模式

尽管咱们能够将应用划分为多个功能,但整个应用在部署来说可能就是一个运行在Tomcat上的WAR文件。api

这种所谓的总体集成架构的优点在于:IDE友好,由于IDE擅长管理单个工程;方便启动测试,由于仅仅须要启动一个应用就能够开始测试了;方便部署,只要拷贝一个文件就够了。

这种架构对于相对较小的应用是没有问题的,但对于复杂应用就不能适用了。首先一个很大的应用不利于开发人员理解和维护源代码;另外对于很大的应用要出个一版本须要不少开发人员配合,测试周期也很长,这不利于快速迭代上线。

这种架构还有一个大问题是不利于尝试并引入新技术。好比你发现一个很好用的新框架,可是除了重写整个应用外你根本没法引入这个新框架,而重写整个应用是风险极高的,基本上不现实。因此总的来讲这种架构不适合大型的、长期演进的系统。

2.将应用分解为多个服务

幸运的是咱们有其余能更好支持伸缩的架构。“伸缩艺术”中提到了一种三维伸缩模型:图2中的伸缩立方

这个模型的X轴方向上的扩展,是运行在负载均衡器背后的多个如出一辙的应用,这是提高服务容量和可靠性的好方法。

Z轴上有点像X轴,一样运行多个如出一辙的应用,区别在于这些应用不是运行在负载均衡器背后,而是运行在路由背后。一种经常使用的路由是根据服务数据的Primary Key分区路由;另外一种经常使用的路由是根据客户的等级分体验等级路由(为VIP用户提供更快的响应速度,更大的容量等)。

Y轴也能够叫作功能分解轴。X轴和Z轴全部应用拷贝都是同样的功能,而Y轴不是,Y轴上的每一个部件的功能都不同。对应到应用,就是将总体集成架构分解为一组功能不一样的服务。每一个服务负责一组相关业务,如订单管理、客户管理等。

将一个应用分解为一组服务有点像艺术创做,不容易说清楚,但有一些经常使用的策略。好比从动词或User Case的角度分解。仍是在线商店的例子,咱们立刻会看到分解事后的状况,其中就有一个订单管理UI服务对应于订单操做的User Case的UI部分。再好比从名词或者资源的角度分解。按照这种角度分解出来的服务,会负责对应某个资源的处理。后面咱们也会看到在线商店中的商品管理服务,负责处理全部的商品资源。

理想状况下,一个服务负责一件事情,或几件小事情。Bob Martin有一个关于一个类仅负责一件事情理论(single-responsibility principle-SRP)的PDF。SRP将一个类定义为仅处理一种变化的责任主体,这个理论一样能够运用于服务的设计。另外也能够参考Unix命令,Unix系统提供了大量的命令,好比grep、cat,和find,每个命令能出色得完成单一功能,同时又能和其余命令组成Shell Script完成复杂的功能。因此也参照Unix命令的思想,按照单一功能思路来建立服务。

须要强调的一点是,分解应用的目的并非为了获得精确地“小”(好比10-100逻辑行)服务,而是要发现并解决总体集成架构的问题和不足。一些服务能够很小,而另外一些也可能比较大。

回到在线商店的例子,按照Y轴分解的话,咱们会获得图3的架构。

通过分解后的应用变成了由一些服务组成,这些服务实现了不一样的前端或后端功能。前端服务包括商品管理的UI(商品查找和浏览功能)、订单管理UI(购物车和订单提提交功能)等,相应的后端也有服务完成对应的功能。咱们已经把应用的各个模块都变成了独立的服务,让咱们看看这样会带来哪些改变。

3. 微服务架构的优劣

任何架构都是有优缺点的。

  • 优势:

 

    • 每一个微服务相对规模比较小

 

由此,开发人员更容易理解代码;IDE工具能跑得更欢,变相得提高了开发效率;启动更加快捷,这样针对具体某个模块的测试、Debug工做的效率也获得提升。

    • 每一个微服务可单独部署

当服务须要调整时,可由负责该服务的开发人员单独完成发布和部署,不须要其余人陪同参与(被动加班等);微服务架构使得持续部署成为可能(大型工程若是不拆分,又必须按明确的时间节奏提交各个阶段的版本,那么80%的可能都被20%的卡住)。

    • 每一个微服务能够单独在X轴和Z轴伸缩

能够为每一个服务单独进行负载均衡或硬件配置上的伸缩。这在总体集成架构下是作不到的,由于总体集成架构下的每一个模块对资源有着不一样的需求(若有些模块是IO密集型,有些模块是CPU密集型),而这些模块是在一个应用程序中部署的。

    • 开发过程可伸缩

团队的存在乎义就在于可以产生1+1>2的合做价值。对于开发团队而言,抛开主观因素,其存在乎义主要在于成员之间方便交流。而这种交流的根本驱动在于程序模块之间须要相互交流。当采用微服务架构后,模块之间的交流讨论需求更少(模块间接口其实不见得会变少,只是对于测试、Debug、部署、线上问题定位等,就不须要太多交流了),从而能带来更松散和人性化的团队架构。

    • 问题责任界定更快捷

好比一个服务的内存泄漏问题只会影响这个服务,其余没有内存泄漏问题的服务通常仍是能正常工做,这样就很容易界定出哪一个服务出了问题。对应的,在像总体集成架构下,一个模块的内存泄漏可能致使整个应用出问题,这经常使得服务宕机。微服务之间的界限最少应该在进程等级,除非有一些更高档的沙盒能够隔离微服务。

    • 能够更灵活的选择开发技术

理论上来说,当须要开发或重构一个微服务的时候,你能够选择任何语言和框架,固然不少组织中这种选择是有范围的。不太重点是,开发人员不用为他以为很愚蠢的技术决策买单了。另外由于微服务都比较小,因此若是一个技术尝试失败了,也不会太影响这个工程的进度。在总体集成架构下,每每一旦决定了采用的语言和框架,后面全部人都必须用这个才行。

这一点对于不少技术人员干活时的心情的影响,是很是大的。就像你本有机会娶得女神,你爹非要给你按一个如花,那在后面婚姻不如意时,更容易埋怨当初你爹的坑爹决策。而其实过日子老是会遇到不如意,关键在因而本身的自由选择就会心甘情愿承担责任。有自由才有责任嘛。

 

  • 缺点

    • 增长了开发复杂度

开发人员须要实现进程间通讯的机制,这是分布式系统的必备要素,而微服务必然使用分布式系统;IDE等开发工具不会为多个应用交互的分布式系统作不少支持,它们通常都设计为开发单个应用。这些都是放弃总体集成架构后带来的新问题。进程是非业务层面的支撑,能够理解为一种沙盒(部署时的执行镜像隔离和运行时的内存隔离),因此理论上其余沙盒若是能完成更好的隔离,也能够代替进程隔离。

    • 增长了运维复杂度

增长了不少可独立部署的服务(Y轴伸缩),而不少服务还有多个实例(X轴和Z轴伸缩),这些都须要在生产环境下管理起来。这种复杂的管理须要高度自动化。这种自动化能够本身开发实现,也能够采用PaaS技术,好比Netflix 的Asgard及相关组件搭建一个平台,或Pivotal Cloud Foundry这样现成的提供商

    • 增长了部署复杂度

多个服务之间存在相互依赖,这种依赖只有开发团队清楚,因此须要根据服务间依赖肯定服务的部署顺序图。而在总体集成构架下,全部的依赖都是应用在开发阶段搞定,无需延伸到部署环节。或者由开发人员完成部署:)

  • 架构选择

上面提到的大部分微服务架构优点,在应用开发初期都不存在,由于一开始应用老是比较简单的,不会面临那些须要微服务架构来解决的问题。而且一开始就采用微服务架构会增长了开发成本(须要不少业务无关的支撑)。因此在开发过程当中是否采用或什么时候开始切换到微服务架构就是个问题了。

项目一开始都是但愿快速实现业务需求,完成开发任务。从Y轴上去分解会使初期的迭代交付更慢。以后,当项目的主要目标慢慢从实现业务变成如何让业务作到可伸缩时,错综复杂的模块间依赖或许已经不容许你将应用分解为微服务了。

因而可知,什么时候采用或切换到微架构是须要慎重考虑的。不过对于那些显然须要具有很强伸缩能力的应用,好比2C的Web应用或SaaS应用,采用微服务架构通常是不会错的。大名鼎鼎的eBay(PDF),Amazon.com,Groupon,和Gilt已经都从总体集成架构切换到了微服务架构。

到目前咱们已经讨论了微服务架构的优劣,下面看看微服务架构设计过程当中的几个关键问题,先从通讯机制提及。

 

4.微服务架构中的通讯

在微服务架构中,客户端和应用之间的通讯机制,以及应用内部模块之间的通讯机制,都和总体集成架构中不同。咱们先看客户端和微服务之间的通讯,再看应用内部之间的通讯。

  • 客户端和应用之间的通讯——API网关模式

在总体集成架构中,浏览器(B/S)或客户端(C/S)发起HTTP请求到负载均衡路由,而后请求被路由到运行着的N个彻底相同的应用中的一个去处理。那么微服务架构下,这些HTTP请求发给谁呢?

像智能手机App这样的客户端,会发送不少分别发送RESTful HTTP请求到各个服务,如图4。

 

初看这种模式挺好的,可是显然没有考虑到不一样的客户端对于API和数据需求的粒度差别。好比,Amazon.com上要显示一个页面可能会调用到100多个服务(详见)。如此庞大的网络调用,且不说移动客户端,就是桌面客户端下也显得性能低下,用户体验可想而知。

更好的方式应该是客户端在渲染一个页面时减小服务调用次数,也许是将多个调用合并为之后,而后发送给一个独立的API网关服务器。如图5。

 

API网关处在客户端和微服务之间,他既为移动客户端提供粗粒度的API,也为桌面客户端提供细粒度的API。如上图中移动客户端仅须要一个请求便可得到桌面客户端须要3个请求才能获取的信息。API网关将粗粒度的API请求分解后,经过高性能的局域网络向微服务发起请求。好比在Netflix上,一个请求对应微服务的扇出为6左右(详情)。

API网关不只能更好地为低质量网络下的客户端服务,也对微服务提供了一层封装。这使得只要业务接口不变,实现业务的服务能很方便地灵活改变,如拆分、合并等。仅须要API网关稍微修改对粗粒度API的处理便可,而客户端彻底不受影响。

API网关是于客户端和微服务之间通讯的关键一环,下面咱们看看微服务和微服务之间如何通讯的。

  • 微服务间的通讯机制

在集成架构下,应用模块之间的通讯一般直接由编程语言的调用实现。但在微服务架构下,每一个微服务都运行在独立的进程,必须借助进程间通讯(IPC)来交互。

    • HTTP同步调用

REST和SOAP都是基于HTTP的同步调用,这是两种友好的网络技术,同时容易实现请求-响应设计模式。HTTP技术的一个局限是它不支持像发布-订阅这样的设计模式。

另外一个问题是HTTP访问须要知道服务器地址和端口号,在一些大系统这个问题会凸显出来。好比云平台上部署的服务,这些服务的运行环境可动态伸缩,并且服务自己也是有须要时才实例化,不须要时销毁,这种状况下,想要人为管理各个服务的HTTP地址也是一个复杂的事情。因此微服务架构的应用内部须要一个服务发现机制,好比创建一个服务注册机制(如Apache ZooKeeperNetflix Eureka)。一些应用里甚至规定微服务注册时必须内置负载均衡器(进而又负载均衡器接管HTTP路由?),例如在Amazon VPC中的ELB

这里的同步不是编程语言层面的同步,而是指HTTP请求的响应有一点的时限要求。

    • 异步消息

好比基于AMQP的Message Broker就是一种异步消息机制。这个机制里,将消息生产者和消费者分离开来,Message Broker会缓存消费者尚未处理的消息,生产者仅需和Message Broker交互而彻底不用关心消费者,由此也再也不须要服务发现了。同时HTTP不能实现的像发布-订阅这种双向通讯模式,该机制也能实现。不过请求-响应这种模式在消息机制下就不那么天然了(全部消息都是对等的,不像HTTP里那样有自然区分

缺点这种机制须要一个Message Broker,这无疑使得微服务下复杂的部署更加复杂。

二者各有特色,能够二者兼用。

5.去中心化数据的管理

为了让微服务之间解耦,数据库的分解是理所固然的,每一个微服务应该有本身专有的数据库(至少不能共享表结构)。甚至理论上来说每一个微服务能够根据本身的须要选用不一样的数据库实现——这种设计被称为多样持久化(polyglot-persistence)。所以,应用被分解为一个个微服务,对应的数据库也跟着被分解了。

好比,一个微服务若是须要ACID交互,那么能够用关系型数据库;而另一个微服务若是须要管理社交网络的话,反而会选择图数据库。

暂且先不讨论数据库分解如何重要,数据库分解带来了一个新的问题:如何处理那些须要多个服务和多个数据库的请求。咱们先来看对数据的读请求,再看写。

  • 处理读请求

在在线应用商店的例子中,假设每一个用户有一个信用额度(全部未付款订单交易金额总和不能超过信用额度)。那么当用户提交一个订单时,系统必须检查全部未付款订单的总额是否超过了信用额度。在微服务架构下,这个用例中涉及到用户服务和订单服务两个微服务,其中订单服务须要向用户服务查询信用额度信息。

一种方法是订单服务每次须要信用额度信息时向用户服务查询,这种实现最简单。缺点是订单服务的可用性依赖于了用户服务,另外每次都作跨进成查询比较耗时。

另外一种方法是订单服务中缓存一个信用额度信息。这样订单服务对用户服务的依赖就弱不少,并且也不会产生额外的查询耗时。缺点是必须引入另外的机制保证这个缓存的信用额度能获得及时更新。

  • 处理写请求

相似像保证订单服务和用户服务中的信用额度一致性的问题,在涉及到多服务写请求处理的时候,是很广泛的。

    • 分布式事务

一个方案是利用数据库的分布式事务,在用户服务处理信用额度更新时,除了更新本身维护的数据外,提交一个分布式事务到订单服务的数据库。这种机制能保证两边的信用额度始终相同。缺点是下降了系统可用性,由于必须处理分布式事务的服务或数据库均可用,这个更新才能完成,并且分布式事务不支持自恢复,也不被新不少技术框架支持(REST,NoSQL等)。

    • 事件驱动更新

另外一种方法是异步事件驱动回复。也就是若是一个服务关心一个数据更新,那么就对这个数据更新注册一个监听,当数据发生更新时,由负责更新的服务发出一个更新事件,以前注册的监听者就都能收到这个更新通知了。在这里,用户服务更新了信用额度后,发布一个CustomerCreditLimitUpdatedEvent,里面有用户ID和新的信用额度两个参数。而订单服务注册过这个事件因此能够收到新的信用额度。这个流程图6。

 

这个方法主要的优势是将更新数据的生产者和接受数据更新的消费者解耦,这样两个微服务相互不依赖,可独立运行。即便订单服务在信用额度更新时没有运行,也能够在运行后收到最新的信用额度。其实这是用数据一致性换取了服务可用性(事件发送和处理之间有时间差),这就致使系统必须设计为容忍必定的数据不一致,对应到程序开发时就须要增长另外的机制来检查并纠正这种数据不一致。尽管如此,这种方法是如今不少程序采用的。

 

6.摘要

总体集成架构在企业应用中很常见。这种架构下的小应用,不管是开发、测试仍是部署,都能较好完成。可是对于复杂的大型程序,总体集成架构则成为了开发和部署的绊脚石。继续发布基本已经不可能了,开发也被紧紧限制在以前选择的技术框架中。所以对于大型应用,采用微服务架构将其分解为一组服务值得一试。

微服务架构优势多多。一个微服务的源代码很容易被理解,开发部署也不须要对其余模块有什么依赖。另外在一个微服务中应用新技术框架更简单容易。

微服务架构缺点也不是没有。东西一分解之后,一个应用会变成不少零碎,你或许须要一个像PaaS那样高度自动化的平台来管理这些零碎。在开发阶段,你还要考虑如何处理数据碎片化。总的来讲,对于须要快速迭代的大型程序,特别是SaaS风格的应用来说,值得一试。

相关文章
相关标签/搜索