01-微服务简介

原文链接:https://www.nginx.com/blog/introduction-to-microservices/

现在,微服务在社交媒体(文章、博客、论坛)和会议报告中得到了广泛的关注。它们正在快速增长到Gartner Hype Cycle中膨胀期望的高峰(Peak of Inflated Expectations)。与此同时,软件界的怀疑论者认为微服务并不是什么新鲜事。反对者声称这个想法只是对SOA的重塑。然而,尽管大肆宣传和怀疑,微服务架构模式具有显着的优势,特别是在:

  • 敏捷开发
  • 企业复杂应用程序交付

image

这篇博客文章是关于设计,构建和部署微服务的七部分系列文章中的第一篇。 将了解微服务架构模式以及它与更传统的单体架构模式的比较。本系列将介绍微服务架构的各种元素和优缺点,它是否对项目有意义,以及如何应用它。

我们先来看看为什么要考虑使用微服务。

构建单体应用

让我们假设要开始建立一个全新的出租车应用程序,旨在与Uber和Hailo竞争。 在一些初步会议和需求收集之后,可以手动创建一个新项目,也可以使用RailsSpring BootPlayMaven附带的生成器创建一个新项目。 这个新的应用程序将具有模块化的六边形体系结构,如下图所示:

image

应用程序的中心是业务逻辑,它由定义服务、域对象和事件的模块共同组成,周围是外部接口的适配器,包括:

  1. 数据库访问组件
  2. 生成和使用消息的消息传递组件
  3. 暴露API或实现UI的Web组件

尽管逻辑上具有模块化体系结构,但整个应用程序仍然作为一个整体进行打包和部署,打包后最终的格式取决于应用程序的开发语言和框架。例如:

  1. 许多Java应用程序打包为WAR文件,并部署在应用程序服务器(如Tomcat或Jetty)上
  2. 其他Java应用程序打包为自包含的可执行JAR文件
  3. Rails和Node.js应用程序打包为目录层次结构

以这种方式编写的应用程序非常常见。

  • 它们很容易开发,因为我们的IDE和其他工具专注于构建单体应用。
  • 这些类型的应用程序也很容易测试。只需启动应用程序并使用Selenium测试UI即可实现端到端测试。
  • 单体应用也易于部署。只需将打包的应用程序复制到服务器即可。还可以通过在负载均衡器后面运行多个副本来扩展应用程序。

在项目的早期阶段,它运作良好

走向单体应用的地狱

开发难题

不幸的是,这种简单的方法有很大的局限性。成功的应用程序都会随着时间的推移而增长并最终变得非常巨大。在每个开发周期内,开发团队会实现很多功能,这意味着添加许多代码行。几年之后,小型简单的单体应用将成长为一个巨大的臃肿程序

举一个极端的例子,我最近采访了一位开发人员,他正在编写一个工具来分析其数百万行代码(line of code, LOC)的应用程序中数千个JAR之间的依赖关系。我相信多年来大量开发人员的共同努力才能创造出这样的野兽。

一旦应用程序成为一个庞大而复杂的巨型组件,开发团队很可能处于一个痛苦的世界,敏捷开发和交付的任何尝试都将陷入困境。一个主要问题是应用程序非常复杂。它对于任何一个开发人员来说都太大了,无法完全理解。因此,正确修复错误和实现新功能变得困难且耗时。更重要的是,这往往是一个向下螺旋,如果代码库难以理解,则无法正确进行更改,最终会得到一个巨大的,难以理解的大泥球。应用程序的庞大规模也将减缓开发速度。应用程序越大,启动时间越长。

在最近的一项调查中,一些开发人员报告启动时间长达12分钟,也听过应用程序长达40分钟的启动时间。如果开发人员经常不得不重新启动应用程序服务器,那么他们当天的大部分时间都将花在等待上,他们的工作效率会受到影响。

持续集成难题

大型,复杂的单体应用的另一个问题是它成为了持续部署的障碍。如今,SaaS应用程序的最新技术是每天多次将更改推向生产。由于必须重新部署整个应用程序以更新它的任何一部分,因此在单体应用上使用持续部署非常困难。还有上面提到的超长的启动时间也是问题。此外,通常我们都不太了解变更将会产生的影响,因此可能需要进行大量的手动测试。所以,几乎不可能进行持续部署。

当不同模块具有相互冲突的资源需求时,单体应用也难以扩展。 例如:

  • 一个模块可能实现CPU密集型图像处理逻辑,理想情况下将部署在Amazon EC2计算优化实例中。
  • 另一个模块可能是内存数据库,最适合Amazon EC2内存优化实例。

但是,由于这些模块一起部署,所以必须在硬件选择上妥协。

可靠性难题

单体应用的另一个问题是可靠性。由于所有模块都在同一进程中运行,因此任何模块中的错误(例如内存泄漏)都可能会导致整个进程失败。此外,由于应用程序的所有实例都相同,因此该错误将影响整个应用程序的可用性。

最后,单体应用使得采用新框架和语言变得极其困难。因此,采用新技术存在巨大障碍。所以,在项目开始时所做的任何技术选择都很重要。

例如,假设使用XYZ框架编写了200万行代码。重写整个应用程序以使用更新的ABC框架是非常昂贵的(在时间和成本上),即使该框架相当好。

总而言之:拥有一个成功的业务关键型应用程序,该应用程序已经发展成为一个巨大的单体应用,开发人员很少了解它。它是使用过时的,非生产性的技术编写的,这使得招聘有才能的开发人员变得困难。该应用程序难以扩展且不可靠。因此,敏捷开发和应用程序的交付是不可能的。

所以你对此能做些什么?

微服务-解决复杂性

许多组织,如亚马逊,eBay和Netflix,通过采用现在称为微服务架构模式解决了这个问题。而不是构建一个可怕的单体应用,我们的想法是将的应用程序拆分为一组较小的,互连的服务。

服务通常实现一组不同的特性或功能,例如订单管理,客户管理等。每个微服务都是一个迷你应用程序,它有自己的六边形体系结构,包括业务逻辑和各种适配器。一些微服务会暴露其他微服务或应用程序客户端使用的API,其他微服务可能会实现Web UI。在运行时,每个实例通常是云VM或Docker容器。

例如,前面描述的系统的可能分解如下图所示:

image

现在,应用程序的每个功能模块都由自己的微服务实现。此外,Web应用程序被分成一组更简单的Web应用程序(例如一个用于乘客,一个出租车驾驶员)。这样可以更轻松地为特定用户,设备或专用用例部署不同的体验。

每个后端服务都公开一个REST API,大多数服务都使用其他服务提供的API(例如,驾驶员管理模块使用通知服务器告知空闲的司机有一个潜在的行程订单)。UI服务调用其他服务以呈现网页。服务还可以使用基于消息的异步通信。本系列后面将详细介绍服务间通信

一些REST API也暴露给司机和乘客使用的移动应用程序。但是,应用程序不能直接访问后端服务,而是由称为API网关的中介调解来进行通信。API网关负责负载均衡缓存访问控制API计量监控等任务,可以使用NGINX来部署API网关。本系列的后续文章将介绍API网关

image

微服务架构模式对应于Scale Cube的Y轴,Scale Cube是一个可扩展性的3D模型,来自《可扩展性艺术》这本书。另外两个坐标轴是:

  • X轴:在负载均衡器后面运行多个相同的应用程序副本
  • Z轴(或称为数据分区):请求的属性(例如,一行的主键或一个客户的身份)用于将请求路由到特定服务

应用程序通常一起使用这三种类型的坐标轴:

  1. Y轴将应用程序分解为微服务,如本节第一张图所示
  2. 在运行时,X轴在负载均衡器后面运行每个服务的多个实例,以实现吞吐量和可用性
  3. 某些应用程序也可能使用Z轴来对服务进行分区

下图显示了如何在Amazon EC2上通过Docker部署行程管理(Trip Management)服务。

image

在运行时,行程管理服务由多个服务实例组成,每个服务实例都是Docker容器。容器在云上的多个VM中运行,以提高可用性。在服务实例的前面是一个负载均衡器,例如NGINX,它跨服务实例分配请求。负载均衡器还可能处理其他问题,例如缓存访问控制API计量监控

微服务架构模式显着的影响应用数据库之间的关系。每个服务都有自己的数据库模式(database schema),而不是与其他服务共享单个数据库模式(database schema)。这种方法与企业范围数据模型的想法不一致,并且,这样经常导致一些数据的重复。但是,如果希望从微服务中受益,那么每个服务拥有一个数据库模式(database schema)是必不可少的,因为它可以确保松散耦合。下图显示了示例应用程序的数据库体系结构。

image

每个服务都有自己的数据库,服务可以使用最适合其需求的数据库类型,即所谓的多语言持久性体系结构。例如,驾驶员管理系统(用于发现靠近潜在乘客的驾驶员)必须使用支持高效地理查询的数据库。

从表面上看,微服务架构模式与SOA类似。这两种方法中体系结构都由一组服务组成。但是,微服务架构模式相对于面向服务架构(Service-Oriented Architecture,SOA),它没有Web服务规范(web service specifications,WS-*)的商业化和感知包,也没有企业服务总线(Enterprise Service Bus,ESB)。基于微服务的应用程序支持更简单,轻量级的协议,如REST,而不是WS-*。他们也极力避免使用ESB,而是在微服务本身中实现类似ESB的功能。微服务架构模式也拒绝SOA的其他部分(例如,canonical schema)。

微服务的优点

微服务架构模式有许多重要的好处。

  1. 解决复杂性问题。它将原本可能是一个巨大的单体应用分解为一组服务。

    虽然功能总量不变,但应用程序已分解为可管理的模块或服务。每个服务都以RPC或消息驱动的API的形式明确定义边界。微服务架构模式强制实现一定程度的模块化,实际上使用单一代码库非常难以符合要求。而且,独立的服务能够更速度的开发,而且更容易理解和维护。

  2. 每个服务都能够独立开发。只要服务符合API规范,开发人员可以自由选择任何有意义的技术。

    当然,大多数组织都希望避免完全无政府状态并限制技术选择。这种自由意味着开发人员在开始新项目可以避免使用可能过时的技术。在编写新服务时,他们可以选择使用当前流行的技术。此外,由于服务相对较小,因此使用当前新技术重写旧服务变得可行。

  3. 每个服务能够独立部署。开发人员永远不需要因为环境的变化而协调服务的部署。

    一旦测试完成,就可以部署这些类型的更改。例如,UI团队可以执行A/B测试并快速迭代UI变更。微服务架构模式使得持续部署成为可能。

  4. 每个服务能够独立扩展。可以仅部署满足其容量和可用性约束的每个服务的实例数。可以使用最符合服务资源要求的硬件。

    例如,可以在EC2计算优化实例上部署CPU密集型图像处理服务,在EC2内存优化实例上部署内存数据库服务。

微服务的缺点

正如弗雷德布鲁克斯(Fred Brooks )30年前说的那样,没有技术银弹,与其他所有技术一样,微服务架构也存在缺陷。

  1. 微服务架构模式名称本身就是一个缺点。微服务一词过分强调服务规模

    事实上,有些开发人员主张构建极其细粒度的10-100 LOC服务。虽然小型服务更可取,但重要的是要记住它们是达到目的的手段,而不是目标本身。

微服务的目标是充分分解应用程序,以促进敏捷应用程序的开发和部署

  1. 由于微服务应用本身是分布式系统所带来的复杂性

    1. 开发人员需要选择并实现基于RPC或消息驱动的进程间通信机制。
    2. 还必须编写代码来处理部分失败,因为请求的目标可能很慢或不可用。虽然这不是造火箭,但它比单体应用复杂得多,在单体应用中,模块通过语言级方法/过程调用相互通信。
  2. 微服务架构模式的另一个挑战是分区数据库架构

    更新多个业务实体的业务事务相当常见。在单个数据库的单体应用中实现这些类型的事务是微不足道的。但是,在基于微服务的应用程序中,需要更新由不同服务所拥有的多个数据库。

    通常不会选择使用分布式事务,不仅仅是因为CAP定理,更因为它们根本不受当前许多高度可扩展的NoSQL数据库和消息传递代理组件的支持。最终必须使用基于最终一致性的方法,这对开发人员来说更具挑战性。

  3. 测试微服务更复杂。

    例如,使用诸如Spring Boot之类的现代框架,编写一个启动单体Web应用程序并测试其REST API的测试类是很简单的。相反,微服务的类似测试类需要启动该服务及其依赖的任何服务(或至少为这些服务配置存根)。虽然这不是造火箭,但不要低估这样做的复杂性。

  4. 微服务架构模式的另一个挑战是实现跨多个服务的升级回滚。例如,假设正在实现一个需要同时变更服务A,服务B和服务C的功能,其中A依赖于B而B依赖于C:

    • 在单体应用中,只需更改相应的模块,集成更改,并一次性部署它们。
    • 在微服务架构模式中,需要仔细规划和协调每个服务的变更顺序。例如,需要按照C->B->A的顺序依次更新服务。幸运的是,大多数更改通常只影响一个服务,而需要协调的多服务更改相对较少。
  5. 部署微服务更复杂。

    • 单体应用只是部署在传统负载均衡器后面的一组相同服务器上。每个应用程序实例都配置有基础结构服务的位置(主机和端口),例如,数据库和消息代理组件。
    • 微服务通常由大量服务组成,例如,根据Adrian Cockcroft的说法,Hailo有160种不同的服务,Netflix有超过600种服务。
      • 每个服务都有多个运行时实例,这些都可迁移变动的部分,需要被配置,部署,扩展和监控。
      • 还需要实现一种服务发现机制,该机制使服务能够发现它需要通信的任何其他服务的位置(主机和端口)。传统的故障工单和手动的操作方法无法适用这样的复杂程度。因此,成功部署微服务需要开发人员更好地控制部署方法,并且实现高度自动化。

    一种自动化方法是使用现成的PaaS(例如,Cloud Foundry)。PaaS为开发人员提供了一种部署和管理微服务的简便方法,这使他们免受诸如采购和配置IT资源等问题的困扰。同时,配置PaaS系统和网络的专业人员可以确保遵守最佳实践和公司策略。

    自动部署微服务的另一种方法是开发私有PaaS。一个典型的方法是使用集群解决方案,如Kubernetes,以及Docker等技术。在本系列的后面部分,我们将介绍基于软件的应用程序交付方法(如,NGINX Plus,它可以轻松处理缓存访问控制API计量微服务级别的监控),这可以帮助解决这个问题。

总结

构建复杂的应用程序本身就很困难,单体应用仅适用于简单轻量级的应用程序。如果将它用于复杂的应用程序,将陷入无尽的痛苦。

尽管存在缺点和实施挑战,但微服务架构模式是复杂的不断变化发展的应用程序的更好选择。在后面的博客文章中,我将深入探讨微服务架构模式的各个方面的细节,并讨论诸如服务发现,服务部署选项以及将单体应用重构为服务的策略等主题。

上次修改: 14 April 2020