微服务介绍

yefx 9年前

这是一篇由 Chris Richardson 撰写的客座文章。Chris 是 CloudFoundry.com 的创始人,这是一个早期的用于 Amazon EC2 的 Java PaaS (Platform-as-a-Service平台即服务)。他现在为大型组织提供咨询服务,帮助他们提升开发和部署应用的能力。他还在有规律地写关于微服务的博客,博客地址:http://microservices.io

==============

目前,微服务得到较多的关注:论文,博文,社交媒体上的讨论,还有会议报告。他们处于期望膨胀期的顶峰,快速地向着登上 Gartner 趋势报告前进。同时,在软件社区还有一群怀疑论者,他们无视微服务,认为它没什么新意。反对派们声称,这种想法就是 SOA 的马甲。但是,不管是大肆宣传还是怀疑主义,微服务架构模式具有明显的好处——尤其谈到敏捷开发和复杂企业应用交付的时候。

这篇博文是一个关于设计、构建和部署微服务的七部系列的第一部分。你将会了解到微服务的方法和它与传统的单一架构模式的对比。这个系列将会详细说明微服务架构的众多元素。你将会了解到微服务架构模式的好处和缺点,对你的项目是否有意义,和怎样应用它。

现在,我们先看看为什么你应该考虑使用微服务。

构建整体应用

假设你想要构建一个全新的打车应用与 Uber 和 Hailo 竞争。经过了一些预备会议和需求收集之后,你将会手工创建一个新的工程,或者使用 Rails、Spring Boot、Play 或 Maven 这类工具生成一个。这个新应用将会有一个模块化的六角形架构,就像下图这样:

微服务介绍该应用的核心是服务模块、域对象模块和事件模块实现的业务逻辑。围绕在核心周围的是与外界交互的适配器。这些适配器包括数据库访问组件、生产和消费消息的消息传递组件、暴露 API 或实现 UI 的 web 组件。

尽管具有合乎逻辑的模块化架构,但是该应用做为整体打包部署。具体的样式依赖应用的语言和框架。例如许多Java应用打成WAR包,部署在 Tomcat或Jetty这样的应用服务器。还有一些Java应用打成独立可执行的JAR包。类似的,Rails和Node.js应用以目录层次的形式打 包。

写成这种形式的应用极其普遍。它们非常易于开发,因为我们的IDE和其他工具专注于构建一个单独的应用。这样的应用通常也易于测试。通 过简单地运行应用就可以实现端到端的测试,使用Selenium就可以测试UI。整体应用也易于部署。你仅仅需要复制打包的应用到服务器。你也可以通过在 负载均衡器后面运行多个副本的方式扩展应用。在项目的早期阶段,这种方式运作的很好。

走向整体地狱(Monolithic Hell)

很不幸,这种简单的方式有一个巨大的限制。成功的应用都会随着时间增大,最终变得巨大。在每一个冲刺(sprint),你的开发团队实现了许多用户故事,这当然意味着添加了许多行代码。经过几年之后,原本很小、很简单的应用将会长成可怕的庞然大物。举一个极端的例子,我最近与一个开发者进行了一次谈话,他写了一个工具分析他们数百万行代码的应用中的成千上万个JAR包的依赖。我很确定这样一个怪兽是由大量的开发者经过数年的共同努力才制造出来的。

一旦你的应用变成了一个复杂的庞然大物,你的开发团队就可能生活在痛苦之中。敏捷开发的任何尝试和交付将会举步维艰。该应用的一个主要问题是过度地复杂。 它仅仅是太大了以至于任何一个开发者都不能完整地理解。因此正确地修复漏洞和实现新功能将会变得困难和耗时。更糟糕的是,这形成了一个恶性循环。如果基本 代码难于理解,就很难做出正确的改动。那么最终就会成为一个可怕的、无法理解的大泥球

应用程序的规模也会拖慢开发进度。应用程序的规模越大,启动的时间越长。例如在最近的调查中,开发者表示启动时间长达12分钟。我也道听途说过启动时间长达40分钟的应用程序。如果开发者不得不周期性重启应用服务,那么他们一天中很大一部分时间将浪费在等待上,他们的生产力将会受到影响。

巨大的、复杂的整体应用程序的另外一个问题是它是持续部署的障碍。目前,SaaS应用通常在一天之内会多次将改动推到生成环境。对于复杂的庞然大物,这极 其难处理,因为你必须重新部署整个应用来更新程序的任何一小部分。我前面提到的冗长的启动时间也将产生不利的影响。而且因为改动的影响通常不能被充分的理 解,那么你可能还必须做更广泛的手工测试。最终导致持续部署几乎是不可能的。

当不同的模块具有资源需求冲突的时候,整体应用程序也将难以扩展。例如,某个实现CPU密集型图像处理逻辑的模块非常适合部署在AWS EC2 Compute Optimized instances。另外某个内存数据库的模块最适合部署在 EC2 Memory-optimized instances。然后由于这些模块都得部署在一起,所以你不得不在硬件的选择上做出妥协。

整体应用程序的另外一个问题是可靠性。因为所有的模块都在同一进程内运行,所以任一模块的漏洞,比如内存泄露,将会影响整个进程。此外,因为应用程序的所有实例是一致的,所以这些漏洞将会影响整个应用的可用性。.

最后但并非不重要,整体应用程序使得采用新框架、新语言极其困难。举个例子来说,假设你有两百万行使用XYZ框架写的代码。那么使用新的ABC框架重写整 个应用将会是极其昂贵的(无论是时间还是花费),即使新框架相当的好。因此对于采用新技术,整体应用程序具有巨大的障碍。当你开始新的项目的时候,你将非 常纠结于选择哪种技术。

总而言之:你有一个已经长成可怕的庞然大物的,几乎没有开发者可以理解的,成功的业务关键应用。这个应用程序使用过时的、没有生产力的技术写成,这使得招聘优秀的开发者变得非常困难。这个应用程序难以扩展而且不可靠。因此,敏捷开发和应用交付是不可能的。

所以你能怎么办呢?

微服务 – 处理复杂性

许多组织,比如Amazon、eBay和Netflix,已经采用现在被称为微服务架构模式的方法解决了这个问题。其想法是将应用程序分割成更小的相互关联的服务,而不是构建单个可怕的整体应用程序。

一个服务通常实现一组独立的特性或功能,比如订单管理、客户管理等。每个微服务是一个具有六角形架构的迷你应用,其自己的六角形架构包含业务逻辑以及许多 适配器。某些微服务将会暴露供其他微服务或者客户端使用的API。另外一些微服务可能实现web UI。在运行时,每个实例通常是一个云虚拟机或者一个Docker容器。

例如,前文所述的系统的一个可能的分解如下图所示:

微服务介绍

应用的每个功能区域现在以微服务的方式实现。此外web应用程序分割成了一组更简单的web应用程序(一个面向乘客的,一个面向司机的)。这使得更容易为特定的用户、设备或特定的用例部署单独的服务。

每个后端服务暴露一套 REST API,大部分服务调用其他服务提供的 API。例如司机管理模块使用通知服务告知空闲的司机可能的订单。UI 服务调用其他服务来渲染 web 页面。服务也可能使用异步的、基于消息的通信方式。服务间通信将会在本系列后面的文章中详细讨论。

某些 REST API 是暴露给司机和乘客使用的移动 app 的。然后 app 不能直接访问后端服务,其间的通信是通过称为 API 网关的媒介传递的。API 网关负责负载均衡、缓存、访问控制、API 测量和监控,该模块可以使用 NGINX 有效的实现。本系列后面的文章将会讨论 API 网关。

微服务介绍

微服务架构模式对应扩展立方体(Scale Cube)的 Y 轴扩展,扩展立方体是《The Art of Scalability》一书中描述可扩展性的 3D 模型。此外还有两个扩展维度,X 轴扩展表示在负载均衡器后面运行多个相同的应用程序副本,Z 轴扩展(数据分割)表示使用请求中的某个属性(例如数据表主键或用户 id)来路由请求到特定服务器。

应 用通常综合使用三种扩展。Y 轴扩展分解应用程序到微服务,就像上图展示的那样。在运行时,为了吞吐量和可用性,X 轴扩展在负载均衡器后面运行多个服务的实例。某些应用也可能使用 Z 轴扩展分割服务。下图展示了如何使用 Docker 将订单管理服务部署在 AWS EC2 上。


微服务介绍

在运行时,订单管理服务包含多个服务实例。每个服务实例是一个 Docker 容器。为了高可用性,这些容器运行在多个云虚拟机上。在这些服务实例前面是一个诸如 NGINX 这样的负载均衡器在多个实例之间分发请求。负载均衡器也可能处理一些别的事情,比如缓存访问控制API 测量监控

微 服务架构模式显著地影响了应用程序与数据库之间的关系。每个服务有自己的数据库模式,而不是共享单个数据库模式。尽管这种方式与企业级数据模型的想法相 悖,也会造成某些数据的冗余。但是如果你想获得微服务的好处,那么每个服务一个数据库模式是很关键的,因为这确保了松散耦合。下图展示了应用的数据库架 构。

微服务介绍

每个服务有其自己的数据库。那么服务就可以选择使用最符合需求的数据库,这就是所谓的混合持久化架构。例如查找乘客附近司机的司机管理模块必须使用支持高效地理查询的数据库。

表 面上微服务架构模式类似于 SOA。这两种架构都包含一组服务。你可以认为微服务架构模式就是不包括商业化和 Web 服务规范(WS-)、企业服务总线(ESB)的 SOA。基于微服务的应用倾向于使用更简单轻量级的协议,比如 REST 而不是 WS-。很大程度上也避免使用 ESB,取而代之的是使用微服务自己实现类似 ESB 的功能。微服务架构模式也拒绝 SOA 的其他部分,比如规范模式的概念。

微服务的好处

微服务架构模式有许多重要的好处。第一,它解决了复杂性的问题。它将一个可怕的、庞大的整体应用分解成一组服务。在 整体的功能没有改变的同时,应用程序已经被分解成可管理的模块或服务。每个服务有以 RPC 或者消息驱动 API 形式定义清楚的界限。微服务架构模式加强了一定程度的模块化,这在整体应用程序中是很难实现的。因此单个的服务可以更快的开发,更简单的理解和维护。

第 二,这种架构使得每个服务可以由单独的团队独立开发,这些团队可以专注于某个服务。开发者可以自由地选择合理的技术,只要服务遵守 API 约定即可。当然大部分组织想要避免混乱地完全无限制的技术选项。然后这种自由意味着开发者不在受限于使用可能过时的技术开始新的项目。当开始写一个新服务 的时候,他们可以选择使用当前的技术。而且因为服务相对较小,所以使用当前的技术重写老服务是可行的。

第三,微服务架构模式使得每一个微服务能被独立部署。开发者再也不需要调整本地对其服务的更变而进行部署。各种类型的变更能在他们测试时立即部署。 UI 团队也可以这样做,举例来说,当 UI 发生改变时,能执行 A|B 测试并快速迭代。微服务架构模式让持续部署成为可能。

最后,微 服务架构模式使得每一个服务都可以被独立扩展。你可以部署大量恰好符合要求容量和有效约束条件的服务实例。此外,你可以使用最匹配服务资源要求的硬件。例 如,你可以在计算优化过的 EC2上部署一个密集CPU 镜像处理服务实例,还可以在内存优化的 EC2 上部署内存数据库服务实例。


微服务的缺点

就像 Fred Brooks 在30年前说的,没有银弹。跟别的技术一样,微服务架构也有缺点。其中的一个缺点就是名字本身。微服务这个词过分强调服务的规模。实际上有些开发者支持构 建极其细粒度10-100 LOC 的服务。尽管规模小的服务更可取,但是最好记住这只是手段而不是目的。微服务的目的是充分地分解应用程序以促进敏捷开发和部署。

微服务另外 一个主要的缺点是微服务应用做为分布式系统带来的复杂性。开发者需要选择或者实现基于消息或 RPC 的进程间通信机制。而且必须编写处理部分失败的代码,因为请求的目的地可能很慢或者不可用。虽然这都不是高深莫测的事情,但是相对于整体应用程序这明显更 复杂,因为整体应用程序中模块间的调用是通过语言层面的方法/程序调用实现的。

微服务的另外一个挑战是分割的数据库架构。更新多个业务实体的事务相当普遍。这种事务在整体应用程序中很容易实现,因为只有一个数据库。然后在基于微服务的应用中,你需要更新多个属于不同服务的数据库。分布式事务通常不是最好的选择,不仅仅因为 CAP 理论,而且目前许多高扩展性的 NoSQL 数据库和消息代理就不支持。你最终不得不使用基于最终一致性的方法,这对于开发者来说更具挑战性。

测 试微服务也很复杂。使用 Spring Boot 这样的现代框架很容易开始一个整体 web 应用程序,编写测试类测试其 REST API。于此相反,对于微服务的一个类似的测试则需要运行该服务以及依赖的服务(或者至少需要配置那些服务的存根)。这也不是高深莫测的事情,但是不要低 估做这些事情的复杂性。

微服务架构模式另外一个主要的挑战是实现跨服务的需求变更。设想你实现的用户故事需要更改服务 A,因为 A 依赖 B,B 依赖 C,所以你又得更改服务 B 和 C。在整体应用中,你可以简单地更改对应的模块,集成这些变更一起部署。相反在微服务架构模式中,你需要仔细计划和协调各个服务的更改上线。例如你需要首 先更新服务 C,接着是服务 B,最后才是服务 A。很幸运的是大部分改动通常只影响一个服务,需要协调的跨服务更改相对较少。

部署一个基于微服务的应用也很复杂。整体应用程序简单地部署在一组相同的服务器上,在这些服务器前面是一个传统的负载均衡器。每个应用程序实例配置好基础设施服务的位置(主机和端口),例如数据库和消息代理。相反微服务应用通常包含大量服务。例如 Hailo 有160个不同的服务Adrian Cockcroft 声 称 Netflix 有超过600个服务。每个服务有多个运行实例。有太多运行的实例需要配置、部署、扩展和监控。另外你也需要实现一个服务发现机制(这将会在后面的文章讨 论)以确保某服务可以找到需要通信的其他服务的位置(主机和端口)。传统的麻烦的基于票据的手工配置方式无法处理这个层次的复杂性。因此成功的部署微服务 应用需要开发者对部署方法有更强的控制,已经更高水平的自动化。

自动化的一个方法是使用现成的 PaaS,比如 Cloud Foundry。PaaS 为开发者提供了一种简单的方式部署和管理微服务。PaaS 可以使开发者不必考虑采购和配置 IT 资源的问题。同时,配置 PaaS 的系统和网络专家可以确保符合最佳实践满足公司的需求。另外一种自动化微服务部署的方式本质上就是开发自己的 PaaS。这通常是从使用某个集群解决方案开始的,比如结合 Docker 这类技术的 MesosKubernetes。在本系列的后续文章中我们将会看到基于软件的应用交付方法如何解决这个问题,比如NGINX,可以很容易地处理微服务层面的缓存、访问控制、API 测量和监控。

总结

构建复杂的应用本身就很难。整体架构只适合简单的轻量级应用。对于复杂的应用,如果你选用整体架构,那么最终你会生活在痛苦之中。微服务架构对于复杂的、演化的应用是更好的选择,尽管微服务架构有些缺点和实现上的挑战。

在之后的文章中,我将会深入微服务架构模式的各个方面,探讨服务发现、服务部署、整体应用程序重构策略这些主题。

请继续关注…