微服务架构或者简称微服务,是近年来受到欢迎的异于传统软件开发的一种架构方法.事实上,即使业界对微服务是什么或者微服务应该怎么做没有太多的定义,微服务已经受到许多企业级开发者的青睐.由于微服务的可扩展性(scalability),这种架构模式对于跨平台及设备(跨web,移动端,物联网,可穿戴设备等)应用或者云服务下不可预期可支持设备应用来说,是一个相对完美的选择.
即使没有标准的微服务定义,一些突出的特点也能够帮助我们了解微服务构架风格.事实上,微服务架构是: 将整个软件开发分解为可独立部署的
,轻量级的
模块化服务(modular service)单元,这些服务单元以独立线程(unique process)的方式运行,并通过定义良好的(well-defined),轻量级的通信机制相互沟通,服务共同的业务逻辑.
服务单元(modular service)通过什么样的方式进行通信取决于应用的需求.很多开发者使用JSON格式RESTful风格或者Protobuf.专家级的开发人员可以根据应用的需要选择最适合的通信协议(communicaiton proctocol).大部分情况下,REST(Representational State Transfer)风格整合的方法由于其简单性得到普遍使用.
为了更好的理解微服务架构,我们从与之相对应的整体架构风格(monolithic architecture style)说起.与微服务不同,一个整体应用往往是作为一个单独的,自制的单元构建而成.在一个CS模式中,服务端作为一个整体的应用处理HTTP请求,执行业务逻辑,获取或者更新底层的数据库信息.整体构架风格的问题在于应用生命周期中的所有改变都彼此耦合在一起.应用中一小部分的改变可能导致新版本的重新构建和部署.如果你想扩展应用的某个功能,你只能扩展整个应用而不仅仅是想要的那个组件.这时候微服务就应运而生.
有人会说微服务架构不就是面向接口架构(Service Oriented Architecture, SOA)的另外一种叫法吗?本世纪初SOA开始流行,SOA与微服务有许多相似之处.传统的SOA是一个比较大的框架,代表着很宽泛的东西.一些微服务支持者不愿意打上SOA的标签,一些人则认为微服务是一种完美的,精致的SOA.不管怎么说,我们认为微服务具有足够的特点来代表一种新的理念(至少是下文中要讲到的一种特殊的SOA).
传统的SOA模型,往往使用独立的ESBs(Enterprise Service Bus),而微服务使用更快速的消息机制.SOA主要关注命令式编程(imperative programming),而微服务架构关注响应式角色(responsive-actor)编程风格.另外,SOA模型往往都有外置的关系型数据库,而微服务通常使用NOSQl或者micro-SQL(可以连接到传统数据库中).二者的真正区别是最初取得整合服务的架构方法的不同.
由于数字时代的一切都在发生着变化,能追赶软件进化需要的敏捷开发技术(agile development techniques)是很有价值的.微服务架构的实践多数来自于大型企业级应用的开发,它们前瞻性的预测到当今用户期望动态的,一如既往的跨设备访问.可扩展的(scalable),适应性强的(adaptable),模块化的(modular)和能快速访问的(quickly accessible)云服务产品有着很大的需求.这也导致很多开发者开始改变他们的架构风格.
Martin Fowler指出:Netflix,eBay,亚马逊等,以及将来的Twitter,PayPal等很多大规模网站和应用都从整体服务架构转向微服务架构.
Netfix已经大规模的从整体应用转向SOA.它每天从多于800种不同类型的设备接收到超过十亿次对stream-video API的调用,每次API的访问都会引起另外后台服务约5次的调用.
亚马逊已经迁移到了微服务架构.它从各种各样的产品上得到无数的访问,包括管理web service的API和website本身.这种情形对于其原来的两层构架很难处理.
支付网站eBay是另一个转到微服务的例子,核心的产品分解成立一些自治的应用,每个应用执行不同领域的业务逻辑.
就像上面所述微服务没有明确的定义一样,这种构架风格也没有统一的标准模型.但是,我们还是可以一窥微服务的关键特征.
第一,根据定义,微服务风格的应用可以分解为多种的组件服务.为什么要这样做?因为,每一个服务都可以独立部署,修改和再部署,而不会受应用完整性的约束.这样,你仅仅需要更改一个或者多个服务而不是再部署整个应用.但是,这种方式也有自己的缺点,包括开销大的远程调用(不是进程间的访问),粗糙的远程接口和组件间重新分配责任的复杂性.
第二,微服务风格往往是根据业务能力和优先级来动态组织的.在传统的整体构架风格中,不同的团队术业专攻,例如UI团队,DBA团队,开发团队或者说后端团队.而微服务架构风格利用跨职能(cross-function)团队,每个团队负责一个具体的产品,而这个产品是建立在一个或多个独立服务之上,服务间是通过消息总线方式进行交流.这就意味着,当某个服务要发生改变时,它的更新迭代就不会因为开发者等待整个项目预算通过而耽搁.传统的开发方法关注项目本身:一个代码段提供一些预定业务价值,交付给客户,然后由一个团队定期维护.然而在微服务中,这个团队拥有这个项目整个生命周期,就像亚马逊引用的那样:”You build it, you run it.”
第三,微服务就像经典的UNIX操作系统那样:它们接受请求,处理请求并且分别产生响应.这与像ESBs(Enterprise Service Buses)这样的产品工作方式是相反的,它们利用高一层封装的系统进行消息路由,编排和执行业务逻辑.你可以理解为:微服务是通过管道中信息流转来处理业务和执行逻辑的职能终端.
第四,由于微服务涉及到各种各样的技术和平台,传统的集中式的管理就不是最优的,而分散管理收到微服务社区的青睐,因为开发者致力于开发的有用的工具能够被解决同样问题的开发者使用.一个实践中的例子就是Netflix,它的服务负责解决网络中约30%的阻塞.公司鼓励开发者使用已有的类库来节省开发时间,同时给了必要时寻找替代品的自由.就像分散管理,微服务也偏爱分散的数据管理.整体风格的应用使用一个数据库服务不同的产品,而微服务风格应用,每一个服务往往管理自己的数据库.
第五,就像一个全面发展的孩子,微服务还学会了如何面对失败.由于多个独立的服务间需要交流来提供价值,不论什么原因(供应商不提供支持)某个服务很有可能会挂掉.在这种场景下,客户应该在挂掉的退出之后某个相似的服务可以替代它继续运行.显而易见,相较于整体风格架构,这种需求增加了微服务构架的复杂性.
第六,微服务架构是一个可不断进化的设计,对于那些你不能完全参与但是又不知道那天会访问你的应用的设备类型来说,这种设计是完美的.这是因为,这种风格的参与人用解耦(decomposition)这个强有力的工具来控制整个应用系统.这种场景中最好的例子是The Guardian’s website(在2014年末之前完成了重构).这个产品的核心最开始建立在整体风格基础上,但是由于一些没有预测到的需求出现,在没有重做整个APP的前提下,开发者使用微服务风格的架构通过APIs实现了与老系统的交互.
如果你让我用不同的标签来描述微服务架构,那么接下来的标签至少可以帮助我们了解微服务的理念:
我们从研究康威定律(Conway’s Law)出发,他指出:任何设计系统的组织,必然会产生以下设计结果:即其产品结构必然是组织沟通结构的一个缩影.
Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization’s communication structure.
想象一下,公司X有两个团队,服务支持团队和支付团队.本能的,我们分离出了高风险的活动.但是,对于客户退款的行为我们很难指定时谁应该负责.我们可能会得到这样的回答:”支付团队没有足够的人手来处理退款和账单问题吗?”或者服务支持团队申请账单和安慰沮丧的客户不是更好的选择吗?”.这样的问题通过X公司新的策略得到了解决:服务支持团队可以申请账单,但是支付团队必须处理退款给客户.在这个相互关联的系统中,角色和责任成功的分离开了,并且同时获得了顾客的满意和风险的规避.
同样,开发任何软件之前,公司组建一个团队并且创建一个项目.随着时间的推移,团队在壮大,同一个代码库上的不同项目都开发完成.通常情况下这会带来项目的竞争:人们发现很难在有交叉目的的共同代码去进行合作.这时候,增加再多的人力只能带来更坏的情况.就像Fred Brooks说的那样:”九个女人一个月内生不出来一个孩子”.
另外,在X公司或者任何开发团队,优先级经常发生变动,导致管理和交流上的问题.上个月的最高优先级主题可能导致团队全力去编码,但是当一个用户提出一个问题时,我们没有时间去解决因为我的最高优先级主题不是这个.这也最令人信服的接受面向接口架构(SOA)或者微服务的原因.面向接口架构重新组织变化管理(change management),行业知识(domain knowledge)和业务优先级(business priority)间的冲突,允许开发团队将之明确分开并处理.当然,它需要沟通成本,这是需要权衡的地方.同时,它能带来冲突的集中处理好搞笑.
最重要的是,合理的SOA实现或者微服务架构风格能让你的项目满足设计模式中接口隔离的原则.由于成熟项目的相互关联性,当需要考虑隔离问题时候,传统的方法是找到一条缝或者一个连接点,在二者之间画上一条虚线.如果不仔细考虑,这样会导致另个小的但是持续增长的实体,它们通过某种桥接的方式连起来.结果可能导致重要的代码在不恰当的一面有了阻碍:团队A不想去维护它,团队B需要它,最后只能导致重构.
我么提出了一些经常出现的问题,现在来看看解决方案:
要怎样部署相对独立却是一个完美组合而且不产生意外整体(spawning accidental monoliths)的系统呢?假设你有一个大型的系统,还拿X公司作为例子,正在分解代码库和团队进行扩展.你可以从应用图的边缘而不是从整个系统着手,不难发现这些应用的依赖关系.在这个例子当中,指向打印机(Printer)和仓库(Storage)的箭头表示着它们可以从整体的系统移除并且在一边抽象出来.打印一个订单或者一张发票是不相关的,一台打印机所做的就是打印数据.像这种-打印机和仓库-在外部的设备中打印和存储信息能够避免上述的整体性风格带来的问题.不止这些,它们还能够复用而且很难别破坏,用例的经验能够别大家熟知,也能够避免出现不小心删除了关键功能.
那么,怎样才能够从整体服务风格转换到微服务风格呢?一个方法可以解决:服务对象(Service Object).不需要从现有的应用中删除代码,你只需要有效的把它重新结构化,就好像它是完全外在一样.为了达到这样的目的,你必须分别出项目中的行为(action)以及这些行为的输入和输出数据(data).看下面的一段代码:
# A class to model a core transaction and execute it
class job
def intialize
@status="Queued"
end
def do_useful_work
...
@status="Finished"
end
def finished?
return @status=="Finished"
def ready?
return @status=="Queued"
end
end
将上面的代码重构为具有microservice风格:
# service to do useful work and modify a status
class JobService()
def do_useful_work(job_status)
...
job_status.finish!
return job_status
end
end
# A class to model a core transaction and execute it
class JobStatus
def intialize
@status="Queued"
end
def finished?
return @status=="Finished"
def ready?
return @status=="Queued"
end
def finish!
@status="Finished"
end
end
现在我们分离出来了两个不同的类:一个用来建模数据,另一个执行操作.重点是,我们的JobService类没有状态(state),你可以不断地调用相同的操作,改变的只是数据,并且能得到一致性(consistent)的结果.如果我们的JobService需要在网络中实现,将这些类打成一个包,用一个网络客户端来替代原来的实现,这样就实现把现有的代码转变为可以扩展的服务.
下面是一个六角架构(Hexagonal Architecture),应用的核心和协调工作在中间,外部的组件规划在核心的周围来达成目标.
现在让我们进一步了解microservice和传统SOA的不同.
也许最重要的不同就在于副作用(side effects).微服务避免了它们,想要了解,我们先看一看古老的方法:Unix管道.
LS | WC -L
上面的命令实际上是两个命令的组合:第一个要列出来当前目录中的所有文件,第二个是读取输入流的行数.如果找一个兼容的程序,可以写为这样:
LS | LESS
组合小的功能封装成不同的工具,工具间通过标准的机制进行输入输出,并且有exit code
表示程序执行结构的成功与否.这些事通过Unix管道(pipe)的概念完成的.
让我们回到公司X的订单和发票系统.每一个交易的可以共同也可以分别完成:有些订单需要发票,有些订单不需要发票,有些发票与订单没有关系.与Unix shell命令不同,这个系统拥有订单和发票应用各自有各自的独立用户.
例如说,我们想抽取出来被不短重复执行的关键操作-发送发票请求服务,改变订单的状态和发票的状态.它们被完全从持久的数据层分离出来.
这样就可以将两个分离的组件写入到两个管道中了.
用户手动开发票
用户完成了订单,开了一张发票
与传统的SOA不同,这里我们将底层的细节通过简单接口暴露出去,而不是一个需要执行整个业务功能的高层次API.对于高层次的API,事实上,很难将小的组件连接在一起,以为服务的设计者提供了一个一次性(one-shot)的接口.
微服务不是十全十美的,实现微服务架构会暴露处交流,团队协作等之前可能隐藏的问题.但是API Gateways in Microservices能帮助节省大量的时间和精力.
一个普遍存在的问题是不同服务间共享模式或者逻辑验证(sharing schema/validation logic).如果B有不同的需求,A需要的验证逻辑可能不适用于B.最好的建议是在共享库中进行版本化和分发模式(distribute schema).库的变更需要在团队的讨论中做出,并且强的版本依赖可能会导致更多的问题.最好的实践方法是规划向后兼容,并且接受从其他服务中的回归测试.
无论如何,你是否适用于微服务架构风格取决于你的需求,因为它既有优点也有缺点.下面是微服务缺点和优点的概述:
优点:
缺点
不管将来微服务建构是否会成为流行的架构风格,至少提供了一种强有力的思想致力于企业级的应用开发获益.许多开发者和组织,没有使用名字或者给它们的实践打上SOA的标签,都可以归为微服务的范畴.
我们也看到了很多现存的技术致力于微服务中隔离和交流的问题.SOAP通过一个给定endpoint来描述可用的操作,通过WSDLs可以发现这个endpoint.UUDI理论上接近一个接口能做什么并且在哪里可以找打它们.但是这些技术由于其复杂的实现,没有别新的项目采纳.REST风格webservice也面临着同样的问题.尽管你可以使用WSDLs结合REST,不过并不广泛使用.
尽管面临着很多的问题,微服务架构可能有着很好的未来.
由于能力有限,翻译和原文可能有偏差,如果感觉有问题,以原文为准.