微服务

Martinfowler的这篇微服务文章引发了软件架构方面的热烈讨论,
“今天在软件架构方面,除了微服务这个名称没有什么新的”。现大意翻译如下:

微服务,是另外一个在软件体架构这个拥挤的街道上冒出的新名词。虽然我们很自然地用轻蔑的一瞥对待这个事情,但是我们发现它是越来越吸引人的软件系统风格。我们已经看到很多项目都在使用这种风格,在过去的几年里一直到今天,以至于我们许多同事都默认使用这种风格来构建企业级应用。但不幸的是,并没有有关“什么是微服务风格”等方面更多信息。

简短地说,微服务架构风格是一种使用一套小服务来开发单个应用的方式途径,每个服务运行在自己的进程中,通过轻量的通讯机制联系,经常是基于HTTP资源API,这些服务基于业务能力构建,能够通过自动化部署方式独立部署,这些服务自己有一些小型集中化管理,可以是使用不同的编程语言编写,正如不同的数据存储技术一样。

未了解释微服务,必须比较一下铁板一块式monolithic(整体) 风格,一个铁板一块monolithic的应用作为一个单元构建,企业应用经常基于三个部分构建:一个客户端用户界面(浏览器运行HTML和javascript),一个数据库和一个服务端应用,服务端应用处理Http请求,执行领域逻辑,加载和更新数据库数据,查询和导出Html视图到浏览器,这个服务端应用是铁板一块monolithic ,只有一个合乎逻辑的可执行文件,这个系统的任何改变都会涉及到重新构建和部署新版本。

这样一个铁板一块的服务器是构建一个系统的最朴素的方式,你所有处理请求的逻辑都运行在一个过程中,允许你使用你语言基本特点将应用切分到类 函数和名称空间等,如果你够仔细,你能够运行测试这个应用,使用部署通道确保所有改变能被测试并应用到生产环境,你能够通过水平扩展一个铁板一块式的应用,比如在一个负载平衡器后面运行多台服务器或实例。

铁板一块的应用是成功的,但是越来越多人却感受到其带来的挫折,特别是很多应用部署到云中,改变周期捆绑在一起:一个小改变导致需要整个系统重新构建重新部署,它总是能以保持模块化结构,难以让变化只影响在一个模块内,进行扩展伸缩只能整个系统扩展,而不能针对其一部分扩展其资源能力。

如下图:

这些挫折导致微服务架构,以一套服务来构建应用,这些服务能够独立被部署和扩展伸缩,每个服务提供一个模块边界,允许不同的服务以不同语言编写,由不同团队管理。

我们并不想宣传微服务是创建,它只是以前Unix设计原理的回归,但是我们真的认为不是所有人会考虑微服务,他们也没有认识到使用微服务会让他们开发工作更好。

待续。。

我们不能对微服务有一个正式定义,但是我们能够视图描述可以看见的这种架构通用共同点,通用共同点并不代表每个服务都具备这些特点,但是我们真的期望大多数微服务架构能具有大部分这些特点。

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

谈到组件时,我们碰到组件的定义,它是困难的。我们的定义是,一个组件是软件一个单元,可以独立可更换,可升级的。

微服务架构会使用库包,但是将一个软件进行组件化主要方式是将其分解成服务。我们定义的库包作为连接到一个程序的组件,调用内存中的函数,而服务是流程外的组件,是一种通过Web服务请求等机制通讯的组件,可以通过远程过程调用的机制进行通信。

使用服务作为组件而不是库的理由是:服务是独立部署。如果你有一个应用程序 ,在一个单一的过程中有很多库包,任何单个组件的改变结果导致重新部署整个应用程序的多个库。但是,如果该应用程序分解为多个服务,这些服务中一个服务的改变只要求这个服务被重新部署即可。当然这也不是绝对的,有些改变会导致导致一些其他协调性质的服务接口改变,但良好的微服务架构目的是通过聚合服务边界并且按照合约实现服务演化,最大限度地减少因为改变影响其他地方。
(banq注:聚合 >松耦合,通过聚合能巧妙解决松耦合,不能为了松耦合而松耦合,要尊重业务自身规律,聚合这些规律,正如分离水是不能通过强行切块,只能通过冷冻加强其内部凝聚达到)

使用服务作为组件的另一个后果是更明确显式的组件接口。大多数语言没有一个很好的机制来定义一个明确显式的发布接口。通常都是书上或学科化地谈论防止客户端打破了组件的封装,这就导致组件之间过于紧耦合。服务可以更容易地通过使用显式远程调用机制来避免这种情况。

使用这样的服务确实有不足之处。远程调用比进程调用更加昂贵,而远程API是需要粗粒度的,这往往是更难以使用。如果您需要跨越过程边界以外进行更改组件之间的责任分配或行为等会很难。

服务映射到运行时的进程,但毕竟是近似。一个服务可以包括多个过程,这些过程总是一起被开发和部署,例如一个应用程序过程和为该服务使用的数据库。

当我们看到将一个大应用划分为部分时,经常性的管理就聚焦在技术层面,比如UI团队,服务端逻辑团队和数据库团队,当团队按照这种线索划分时,即使最简单的改变也会导致跨团队的交流和协调,一个“聪明”的团队会根据这种情况进行优化,两害取其轻,将业务逻辑强迫放入那些被访问的应用程序中,这导致逻辑到处存储,这就是著名的Conway法则在起作用.

任何设计系统的组织会自己产生这样的组织结构:这种结构是组织联系结构的拷贝。
-- Melvyn Conway, 1967

而微服务的分工则不同,围绕业务能力不同划分为不同的服务,这些服务可以采取软件中不同技术堆栈来实现(如Java或NodeJs),包括用户界面,持久层存储,或任何外部协调,所以,团队是跨功能的,包括拥有开发所需要的全部技能: 用户体验, 数据库,和项目管理。

按照这种方式组织的公司是 www.comparethemarket.com,跨功能团队负责每个产品的建立,每个产品被分离成许多独立的服务,之间通过消息总线通讯。

大型的铁板一块monolithic应用也总是围绕业务能力模块化, 尽管这不是很普遍的情况,当然我们会督促一个大型团队构建铁板一块应用时,沿着业务线划分,我们已经看到这种情况主要问题是:他们视图围绕太多上下文进行组织,如果这个系统跨越很多模块边界,对于一个单独团队是很难在短时间解决问题的。另外我们也看到模块线划分需要大量理论的戒律强迫,如果通过服务组件使得团队之间边界更清楚,这显然是更加明确的可落地的。(banq注:我个人理解是通过引入有界上下文进行模块化的依据是更好的)

大多数应用开发是使用项目模型,其目的是提供即将需要完成的软件片段,一旦开发完成软件就被传递到维护部门,开发它的项目团队就解散了。

微服务支持者倾向于避免这种模型,愿意让一个团队有充分完整时间开发它们的产品,一个普遍的灵感是亚马逊的 "you build, you run it" ,开发团队完全负责软件的产品周期,这将把开发者带入每天关心他们的软件如何在生产环境工作,增强他们与软件用户的联系,也就是说他们必须做一些支持工作。

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

没有任何理由说同样的方法为什么不能在铁板一块monolithic应用中的采取,但服务的粒度较小可以更容易地创建服务开发者与用户之间的个人关系。

当在不同过程中建立联系的结构,我们看到很多产品将智慧应用在通讯联系本身机制上,一个好的例子是企业服务总线 (ESB), ESB 产品是用于消息路由,编排流程,转化和应用复杂业务规则的基础设施。

微服务社区另外一个目标是:智能终端和哑管道,构建于微服务的应用是尽可能聚合,也就自然会尽可能松耦合, - 它们拥有自己的领域逻辑,以类似Unix管道过滤方式运行,接受到一个请求,使用相应的逻辑,产生一个响应,这些都可以使用RESTful方式编排,而不是使用复杂的协议如WS-Choreography 或 BPEL或由一个中央工具(如ESB)指挥控制。

两种协议都是使用Http request-response,带有资源API's金额轻量消息,REST更好些是因为:

Be of the web, not behind the web
本身就是Web,而不是基于Web
-- Ian Robinson

微服务团队使用这个原理构建,经常使用的资源能够被缓存。

基于消息总线的消息是第二种方式,底层设施是典型的哑管道,类似消息路由, - RabbitMQ or ZeroMQ 是简单实现,除了提供可靠的异步之外并不做太多,智慧还体现在:终端产生消息,而服务器端消费处理这个消息。(banq注:Vertx有这种方式)

在一个整体铁板一块monolith系统中, 组件是按过程执行,它们之间的通讯使用方法调用(或RPC). 将monolith系统改为微服务架构最大挑战是这种通讯模式的改变,朴素的转变是从内存方法调用转换到RPC远程方法调用,但是这种方式其实并不好到哪里,其实,你应该使用细粒度的通讯替代粗粒度的通讯。(banq注:细粒度服务替代SOA倡导的粗粒度服务)


Martin Fowler这篇微服务文章引起争论,因为他在文中没有对微服务进行精确定义,有人说看了这篇文章,只知道从“微服务”这个名词上看出区别。

我个人观点:微服务其实等同于DDD中有界上下文,当然微服务还有其物理特点,独立部署,独立重启,这就有别于我们传统基于JavaEE服务器的服务模式了,我们编制的服务代码是部署在Tomcat服务器容器中,所以要重启一个服务只能重启整个应用程序。

传统方式是应用程序运行在容器中,那么能不能倒过来?容器服务器不要预先启动,而是在我们应用程序中启动,类似node.js那种,需要Socket IO通讯就启动一个服务,然后监听它就可以。

最近我们领导就让我们尝试用这样的方法实现,中间都是用json进行通讯的,但是感觉还是有很多问题。
比如,登陆是一个单独的服务,用户信息也是一个单独的服务,我拿到的就是两个json字符串,我要根据条件进行拼装(将用户和用户信息对应起来),这个感觉就已经比较繁琐了。如果还有条件再复杂一点的话就更加麻烦了。
虽然比较麻烦,现在也在尝试中。

希望老师可以指点一下,或者有什么案例之类的。

最近我们领导就让我们尝试用这样的方法实现,中间都是用json进行通讯的,但是感觉还是有很多问题。
比如,登陆是一个单独的服务,用户信息也是一个单独的服务,我拿到的就是两个json字符串,我要根据条件进行拼装(将用户和用户信息对应起来),这个感觉就已经比较繁琐了。如果还有条件再复杂一点的话就更加麻烦了。
虽然比较麻烦,现在也在尝试中。

希望老师可以指点一下,或者有什么案例之类的。