当前位置:   article > 正文

微服务:从设计到部署_服务设计转换与服务部署的区别和联系

服务设计转换与服务部署的区别和联系

微服务:从设计到部署

基于《微服务:从设计到部署》所做的个人心得。

1. 介绍

1. 单体架构

​ 单体应用是一个程序。这个程序包含了:项目所有业务的实现、一个大而全的数据库和对外暴露的API(HTTP接口)或实现了UI的web组件(如:模板引擎)。

​ 以这种风格编写的应用是很常见的。他们很容易开发,也很容易测试,易于部署。

​ 不幸的是,这种简单的方法有很大的局限性。成功的应用有一个趋势,随着时间推移

而变得越来越臃肿。应用程序变得非常复杂。

应用复杂导致的问题:

  1. 对于任何一个开发人员来说显得过于庞大,难以理解业务。最终,正确修复 bug 和实现新功能变得非常困难而耗时。
  2. 应用程序越大,启动时间越长。
  3. 复杂的单体应用本身就是持续部署的障碍,因为需要重新部署整个应用程序才能更新其中任何一部分。
  4. 当不同模块存在资源需求冲突时,单体应用可能难以扩展。如果一个应用包含图形处理业务(需要高性能CPU)和数据存储业务(需要内存优化),就必须在硬件选择上做出妥协。
  5. 可靠性也有问题。因为所有模块都运行在同一进程中。任何模块的一个 bug,比如内存泄漏,可能会拖垮整个进程。
  6. 单体应用使得采用新框架和语言变得非常困难。
2. 微服务 - 解决复杂问题

​ 微服务的思路是:将应用程序分解成一套较小的互连服务。

​ 一个服务通常实现了一组不同的特性或功能,例如订单管理、客户管理等。每一个微服务都是一个迷你应用,有自己独立的数据库,独立部署在虚拟机或Docker容器呢,向外暴露一个供其他微服务(RPC)或应用客户端消费(HTTP)的 API。

Snipaste_2020-09-22_09-38-40

微服务架构模式相当于此伸缩立方的 Y 轴坐标,上图是一个来自《架构即未来》的三维伸缩模型。

  • Y 轴坐标将应用分解成微服务。

  • X 坐标轴上运行着服务的多个实例,每个服务配合负载均衡器以满足吞吐

    量和可用性。

  • Z轴,每个服务器运行一份完全相同的代码。在这个角度上,它与X轴伸缩类似。最大的区别在于每个服务器只负责数据的一个子集。系统的某些组件负责将请求路由到合适的服务器上。一个常用的路由标准是请求的属性,如被访问的实体主键(如:根据客户customer的id,进行区分。比如10000号之前的客户的流量导入到副本A,10000号之后的导入到副本B)。另一个常用路由标准是客户类型。例如,应用可以将SLA(服务水平协议, Service Level Agressment)更高的付费客户的请求路由到处理能力更强的服务器上去。

微服务的优点:

  1. 解决了业务复杂问题。将业务分成不同模块,模块间通过RPC或消息进行沟通
  2. 这种架构使得每个服务都可以由一个团队独立专注开发。
  3. 微服务架构模式可以实现每一个微服务独立部署。

微服务的缺点:

  1. 微服务拆分服务需要确定细粒度,这个分寸不好把握。我们要记住:拆分服务只是一种手段,而不是主要目标。微服务的目标在于充分分解应用程序以方便应用敏捷开发和部署。
  2. 微服务是一个分布式系统,其使得整体变得复杂。
  3. 分区数据库架构,在基于微服务的应用程序中,您需要更新不同服务所用的数据库。通常不会选择分布式事务,不仅仅是因为 CAP 定理。他们根本不支持如今高度可扩展的 NoSQL 数据库和消息代理。您最后不得不使用基于最终一致性的方法,这对于开发人员来说更具挑战性
  4. 测试微服务应用程序也很复杂,因为会依赖其它的服务。
  5. 部署基于微服务的应用程序也是非常复杂的,要成功部署微服务应用程序,需要求开发人员能高度控制部署方式和高度自动化。

构建复杂的微服务应用程序本质上是困难的。

单体架构模式只适用于简单、轻量级的应用程序,如果您使用它来构建复杂应用,您最终会陷入痛苦的境地。

微服务架构模式是复杂、持续发展应用的一个更好的选择。尽管它存在着缺点和实现挑战。

2. 实现微服务

1. API网关
1.1 客户端与微服务直接通信

每个微服务都有一个公开的域名,客户端通过对应直接访问数据。

问题:

  1. 一个页面涉及多个服务数据时,通过客户端分别访问各自服务是不合理的。
  2. 有些服务可能没有提供web接口。
  3. 难以重构微服务,服务大了后不好划分。
1.2 使用API网管通信

API 网关是一个服务器,是系统的单入口点。它类似于面向对象设计模式中的门面(Facade)模式。

API 网关负责请求路由、组合和协议转换。所有的客户端请求首先要通过 API 网关,

之后请求被路由到适当的服务。

优点:

  1. API 网关封装了应用程序的内部结构。客户端只需要与网关通信,而不必调用特定的服务。

缺点:

  1. API 网关是另一个高度可用的组件,需要开发、部署和管理。
  2. API 网关可能会成为开发瓶颈。开发人员必须更新 API 网关以暴露每个微服务的端点。

重要的是更新 API 网关的过程应尽可能地放缓一些。否则,开发人员将被迫排队等待

网关更新。尽管 API 网关存在这些缺点,但对于大多数的真实应用来说,使用 API 是

合理的。

1.3 设计API网关

API 网关的性能和可扩展性是相当重要的。

可以使用不同的技术来实现一个可扩展的 API 网关。

  1. 在 JVM 上,您可以使用基于 NIO 的框架,如 Netty、Vertx、Spring Reactor、 JBoss Undertow或者zuul,zuul是一个能够实现动态路由、监控、弹性扩展并且安全的API网关组件。

  2. 一个流行的非 JVM 选择是使用 Node.js,它是一个建立在 Chrome 的 JavaScript 引擎之上的平台。

  3. 使用 NGINX Plus。

API网关需要实现的功能:

  1. 并发执行请求。
  2. 如果多个请求是相互依赖的,就使用响应式编程模型(请求后通过观察者模式等待返回结果在进行之后的业务逻辑,java8有CompletableFuture类)实现API网关。
  3. 调用服务需要进程间通讯,可以使用基于消息的异步机制,或者使用HTTP、Thrift或Dubbo进行同步调用。
  4. 发现服务不能硬编程(使用配置服务ip地址直接访问),要能够使用服务注册中心。
  5. 出现问题要有数据返回机制(熔断:服务不可用或请求超时,使用提前编写好的数据返回或返回缓存数据)。
2. 进程间通信
2.1 简介

服务部署与不同机器,服务之间使用进程间通信(IPC)

当为服务选择一种 IPC 机制时,首先需要考虑服务如何交互。有许多种客户端 — 服

务交互方式。它们可以分为两个类。第一类是一对一交互与一对多交互:

  • 一对一 每个客户端请求都由一个服务实例处理。

  • 一对多 每个请求由多个服务实例处理。

第二类是同步交互与异步交互:

  • 同步 客户端要求服务及时响应,在等待过程中可能会发生阻塞。

  • 异步 客户端在等待响应时不会发生阻塞,但响应(如果有)不一定立即返回。

一对一一对多
同步请求/响应(客户端向服务发出请求并等待响应。)-
异步通知(客户端向服务发送请求,但不要求响应。)发布/订阅(发布通知消息,由零个或多个感兴趣的服务消费。)
异步请求/异步响应(客户端向服务发送请求,服务异步响应。客户端在等待时不发生阻止,适用于假)发布/异步响应(客户端发布请求消息,之后等待一定时间来接收消费者的响应。)
2.2 确定API通讯格式和请求方式

服务 API 是服务与客户端之间的契约。无论您选择何种 IPC 机制,使用接口定义语言(interface definition language,IDL)来严格定义服务 API 都是非常有必要的。

IPC(InterProcess Communication) : 进程间通信

  • 基于同步请求/响应的通信机制:HTTP 的 REST 或 Dubbo。

    如今,开发 RESTful 风格的 API 是很流行的。REST 是一种使用了 HTTP (几乎总是)

    的 IPC 机制。

    Dubbo 是常见的PRC通讯框架。

  • 使用异步、基于消息的通信机制: AMQP 或STOMP。

    有大量的开源消息系统可供选择,包括 RabbitMQ、Apache Kafka、Apache ActiveMQ

    和 NSQ。从高层而言,他们都支持某种形式的消息和通道。他们都力求做到可靠、高

    性能和可扩展。然而,每个代理的消息传递模型细节上都存在着很大差异。

2.3 处理跨进程故障
  • 网络超时

    在等待响应时,不要无限期地阻塞,始终使用超时方案。使用超时方案确保资源不被无限地消耗。

  • 限制未完成的请求数量

    对客户端拥有特定服务的未完成请求的数量设置上限。如果达到了上限,发出的额外请求可能是毫无意义的,因此这些尝试需要立即失败。

  • 断路器模式

    追踪成功和失败请求的数量。如果错误率超过配置阈值,则断开断路器,以便后续的尝试能立即失败。如果出现大量请求失败,则表明服务不可用,发送请求将是无意义的。发生超时后,客户端应重新尝试,如果成功,则关闭断路器。

  • 提供回退

    请求失败时执行回退逻辑。例如,返回缓存数据或者默认值,如一组空白的推荐数据。

阿里巴巴 的 Sentinel 是一个实现上述和其他模式的开源库。如果您正在使用 JVM,那么您一定要考虑使用 Sentinel 。如果您在非 JVM 环境中运行,则应使用相等作用的库。

随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。

2.4 返回消息格式

有两种主要的消息格式:文本和二进制。

  • 基于文本格式的例子有 JSON 和 XML。

    优点:

    1. 方便人查看
    2. 方便跨语言

    缺点:

    1. 消息冗长,消息是自描述的,每个消息除了它们的值之外还包含属性的名称
    2. 解析文本的开销
  • 二进制格式

    Thrift(跨语言,可以使用Protocol Buffers 消息格式)或者Dubbo

2.5 总结

微服务必须使用进程间通信机制进行通信。在设计服务如何进行通信时,您需要考虑各种问题:服务如何交互、如何为每个服务指定 API、如何演变 API 以及如何处理局部故障。微服务可以使用两种 IPC 机制:异步消息传递和同步请求/响应。为了进行通信,一个服务必须能够找到另一个服务。

3 服务使用
3.1 服务注册中心(service registry)

服务注册中心是服务发现的一个关键部分。它是一个包含了服务实例网络位置的数据库。

Nacos 官网致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据及流量管理。

服务注册中心必须是高可用和最新的。虽然客户端可以缓存从服务注册中心获得的网络位置,但该信息最终会过期,客户端将无法发现服务实例。因此,服务注册中心由使用了复制协议(replication protocol)来维护一致性的服务器集群组成。

3.2 服务注册方式

服务实例必须在服务注册中心中注册与注销。有几种不同的方式来处理注册和注销。

  • 服务实例自我注册,即自注册模式。

    自注册模式

    当使用自注册模式时,服务实例负责在服务注册中心注册和注销自己。此外,如果有必要,服务实例将通过发送心跳请求来防止其注册信息过期。

    Snipaste_2020-09-25_11-15-48

    自注册模式有好有坏。一个好处是它相对简单,不需要任何其他系统组件。然而,主要缺点是它将服务实例与服务注册中心耦合。您必须为服务使用的每种编程语言和框架都实现注册代码。

  • 使用其他系统组件来管理服务实例的注册,即第三方注册模式

    当使用第三方注册模式时,服务实例不再负责向服务注册中心注册自己。相反,该工作将由被称为服务注册器(service registrar)的另一系统组件负责。服务注册器通过轮询部署环境或订阅事件来跟踪运行实例集的变更情况。当它检测到一个新的可用服务实例时,它会将该实例注册到服务注册中心。此外,服务注册器可以注销终止的服务实例。

    Snipaste_2020-09-25_11-10-57

    第三方注册模式同样有好有坏。一个主要的好处是服务与服务注册中心之间解耦。您不需要为开发人员使用的每种编程语言和框架都实现服务注册逻辑。相反,仅需要在专用服务中以集中的方式处理服务实例注册。

    该模式的一个缺点是,除非部署环境内置,否则您同样需要引入这样一个高可用的系统组件,并进行设置和管理。

3.3. 服务如果找到依赖服务(服务发现)

服务实例具有动态分配的网络位置。此外,由于自动扩缩、故障与升级,整组服务实

例会动态变更。因此,您的客户端代码需要使用更精确的服务发现机制。

有两种主要的服务发现模式:

  • 客户端发现(client-side discovery)

    当使用客户端发现模式时,客户端负责确定可用服务实例的网络位置和请求负载均衡。

    客户端查询服务注册中心(service registry),它是可用服务实例的数据库。之后,客户端利用负载均衡算法选择一个可用的服务实例并发出请求。

    Snipaste_2020-09-27_15-18-55

    服务实例的网络位置在服务注册中心启动时被注册。当实例终止时,它将从服务注册中心中移除。通常使用心跳机制周期性地刷新服务实例的注册信息。

  • 服务端发现(server-side discovery)

    Snipaste_2020-09-25_11-24-11

    客户端通过负载均衡器向服务发出请求。负载均衡器查询服务注册中心并将每个请求路由到可用的服务实例。与客户端发现一样,服务实例由服务注册中心注册与销毁。

3.4 总结

在微服务应用程序中,运行的服务实例集会动态变更。实例具有动态分配的网络位置。

  1. 有一个同一管理服务(记录所有服务并拥有注册和销毁功能)的功能:服务注册中心

    使用服务的一个关键部分是服务注册中心。

    ​ 服务注册中心是一个可用服务实例的数据库。服务注册中心提供了管理 API 和查询 API 的功能。服务实例通过使用管理 API 从服务注册中心注册或者注销。系统组件使用查询 API 来发现可用的服务实例。

  2. 服务注册与注销

    一个是服务实例向服务注中心自我注册,即自注册模式。

    另一个是使用其他系统组件代表服务完成注册与注销,即第三方注册模式。

  3. 服务查找依赖服务

    有两种主要的服务发现模式:客户端发现与服务端发现。

    在使用了客户端服务发现的系统中,客户端查询服务注册中心,选择一个可用实例并发出请求。

    在使用了服务端发现的系统中,客户端通过路由进行请求,路由将查询服务注册中心,并将请求转发到可用实例。

4. 事件驱动数据管理
4.1 分布式业务数据的问题
  1. 单体应用数据存储

    ​ 单体应用程序通常具有一个单一的关系型数据库。使用关系型数据库的一个主要优点是应用程序可以使用 ACID 事务,这些事务提供了以下重要保障:

    • 原子性(Atomicity) 所作出的改变是原子操作,不可分割

    • 一致性(Consistency) 数据库的状态始终保持一致

    • 隔离性(Isolation) 即使事务并发执行,但他们看起来更像是串行执行

    • 永久性(Durable) 一旦事务提交,它将不可撤销

    因此,应用程序可以很容易地开始事务、更改(插入、更新和删除)多个行,并提交事务

  2. 微服务数据存储

    • 每个服务都有自己的私有数据

      ​ 当我们转向微服务架构时,数据访问将变得非常复杂。这是因为每个微服务所拥有的数据对当前微服务来说是私有的,只能通过其提供的 API 进行访问。封装数据可确保微服务松耦合,独立演进。

    • 使用数据库管理工具(mysql)和NoSQL混合存储数据(混合持久化(polyglot persistence))

      不同的微服务经常使用不同类型的数据库。现代应用程序存储和处理着各种数据,而关系型数据库并不总是最佳选择。在某些场景,特定的 NoSQL 数据库可能具有更方便的数据模型,提供了更好的性能和可扩展性。例如,存储和查询文本的服务使用文本搜索引擎(如 Elasticsearch)是合理的。类似地,存储社交图数据的服务应该可以使用图数据库,例如 Neo4j。因此,基于微服务的应用程序通常混合使用 SQL 和 NoSQL 数据库,即所谓的混合持久化(polyglot persistence)方式。

  3. 微服务数据问题

    一个分区的数据存储混合持久化架构具有许多优点,包括了松耦合的服务以及更好的

    性能与可扩展性。然而,它也引入了一些分布式数据管理方面的挑战:

    • 如何实现维护多个服务之间的业务事务一致性。

      Order Service 无法直接访问 CUSTOMER 表。它只能使用客户服务提供的 API。订单

      服务可能使用了分布式事务,也称为两阶段提交(2PC)。然而,2PC 在现代应用中通

      常是不可行的。CAP 定理要求您在可用性与 ACID 式一致性之间做出选择,可用性通

      常是更好的选择。此外,许多现代技术,如大多数 NoSQL 数据库,都不支持 2PC。维

      护服务和数据库之间的数据一致性至关重要,因此我们需要另一套解决方案。

    • 如何实现从多个服务中检索数据。

4.2 事件驱动框架

案例:

​ Customer Service (顾客服务)维护客户相关的信息,包括信用额度。

​ Order Service (订单)负责管理订单,并且必须验证新订单,不得超过客户的信用额度。

​ 在微服务架构中,ORDER (订单)和 CUSTOMER (顾客)表对其各自的服务都是私有的。

在此架构中,微服务在发生某些重要事件时发布一个事件,例如更新业务实体时。其他微服务订阅了这些事件,当微服务接收到一个事件时,它可以更新自己的业务实体,这可能导致更多的事件被发布。

您可以使用事件实现跨多服务的业务事务。一个事务由一系列的步骤组成。每个步骤包括了微服务更新业务实体和发布事件所触发的下一步骤。

下图依次展示了如何在创建订单时使用事件驱动方法来检查可用信用额度。

微服务通过 Message Broker (消息代理)进行交换事件,流程如下:

  • Order Service(订单服务)创建一个状态为 NEW 的订单,并发布一个 Order Created (订单创建)事件。

    Snipaste_2020-09-25_11-28-03

  • Customer Service (客户服务)消费了 Order Created 事件,为订单预留信用额度,并发布 Credit Reserved 事件。

    Snipaste_2020-09-27_15-19-05

  • Order Service 消费了 Credit Reserved (信用预留)事件并将订单的状态更改为 OPEN。

    Snipaste_2020-09-27_15-15-04

  • 还可以 使 用 事 件 来 维 护 多 个 微 服 务 预 先 加 入 所 拥 有 的 数 据 的 物 化 视 图(materialized view)

    Snipaste_2020-09-27_15-30-32

    图 5-5 展示了 Customer Order View Updater Service (客户订单视图更新服务)根据 Customer Service 和 Order Service 发布 的事件更新 Customer Order View (客户订单服务)。

    当 Customer Order View Updater Service(客户订单视图更新服务) 接收到 Customer 或 Order 事件时,它会更新 Customer Order View 数据存储。 我们可以使用如 MongoDB 之类的文档数据库实现 Customer Order View,并为每个 Customer 存储一个文档。

    Customer Order View Query Service (客户订单视图查询服务)通过查询 Customer Order View 数据存储来处理获取一位客户和最近的订单的请求。

事务驱动框架的优缺点:

  • 优点:

    • 实现跨越多服务并提供最终一致性事务
    • 应用程序能够维护物化视图
  • 缺点:

    • 编程模型比使用 ACID 事务更加复杂
    • 订阅者必须要检测和忽略重复的事件
4.3事件驱动的原子性

Order Service 必须在 ORDER 表中插入一行数据,并发布 Order Created 事件。这两个操作必须原子完成。

  1. 方法一:使用本地事务发布事件

    应用程序使用仅涉及本地事务的多步骤过程来发布事件。诀窍在于存储业务实体状态的数据库中有一个用作消息队列的 EVENT 表。

    应用程序开启一个(本地)数据库事务,更新业务实体状态,将事件插入到 EVENT 表中,之后提交事务。

    一个单独的应用程序线程或进程查询 EVENT 表,将事件发布到 Message Broker,然后使用本地事务将事件标记为已发布。设计如图 5-6 所示:

    Snipaste_2020-09-27_15-27-19

    步骤:

    1. Order Service 将一行记录插入到 ORDER 表中,并将一个 Order Created 事件插入到 EVENT 表中。
    2. Event Publisher(事件发布者)线程或进程从 EVENT 表中查询未发布的事件,之后发布这些事件,最后更新 EVENT 表以将事件标记为已发布。

    缺点:

    1. 很容易出错,因为开发人员必须要记得发布事件。
    2. 由于其有限的事务和查询功能,在使用某些 NoSQL 数据库时,实现起来将是一大挑战。
  2. 方法二:挖掘数据库事务日志

    使用线程或进程发布事件,该线程或进程对数据库的事务或者提交日志进行挖掘。

    当应用程序更新数据库时,更改信息被记录到数据库的事务日志中。Transaction Log Miner 线程或进程读取事务日志并向 Message Broker 发布事件。设计如图 5-7 所示:

    Snipaste_2020-09-27_15-27-19

    优点:

    1. 能保证被发布的事件每次更新都不依赖于 2PC。
    2. 事务日志挖掘还可以通过将事件发布与应用程序的业务逻辑分离来简化应用程序。

    缺点:

    1. 事务日志的格式对于每个数据库来说都是专有的,甚至在数据库版本之间格式就发生了改变。
    2. 记录于事务日志中的低级别更新可能难以对高级业务事件进行逆向工程。
    3. 事务日志挖掘消除了应用程序在做一件事时对 2PC 的依赖:更新数据库。
  3. 方法三:使用事件溯源

    溯源:类似git,记录每次提交的修改,而不是直接修改数据,通过基础数据加操作记录得到当前数据的实际状态,但是原先一条数据直接修改一条,现在一条数据要保存每次的操作状态,数据量变大。

    存储的数据不再是操作的业务实体,而是每次操作的流程和相关数据,通过操作流程和相关数据可以重建对应的业务实体。

    业务实体的状态发生变化,其都会将状态追加到事件列表中。由于保存事件是一个单一操作,因此具有原子性。

    ​ 在传统方式中,每个订单都与 ORDER 表中的某行记录相映射,也可以映射到例如ORDER_LINE_ITEM 表中的记录。

    ​ 但当使用事件溯源时,Order Service 将以状态更改事件的形式存储 Order:Created(创建)、Approved(批准)、Shipped(发货)、Cancelled(取消)。每个状态记录包含足够的数据来反推出操作时 Order 的内容。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x0eyyBGj-1603418847755)(images/Snipaste_2020-09-27_15-43-40-1603413738167.png)]

    事件存储应用有一个事件数据库。该应用有一个用于添加和检索实体事件的 API。

    事件存储还与我们之前描述的架构中的 Message Broker 类似。它提供了一个 API,使得服务能够订阅事件。事件存储向所有感兴趣的订阅者派发所有事件。可以说事件存储是事件驱动微服务架构的支柱。

    优点:

    1. 可以在状态发生变化时可靠地发布事件,解决了微服务架构中的数据一致性问题
    2. 由于它持久化的是事件,而不是领域对象,所以它主要避免了对象关系阻抗失配问题。
    3. 事件溯源还提供了对业务实体所做更改的 100% 可靠的审计日志,可以实现在任何时间点对实体进行时间查询以确定状态。
    4. 业务逻辑包括松耦合的交换事件业务实体,这使得从单体应用程序迁移到微服务架构将得更加容易。

    缺点:

    1. 一种不同而陌生的编程风格,因此存在学习曲线。
    2. 事件存储仅支持通过主键查找业务实体。您必须使用命令查询责任分离(CQRS)来实现查询。因此,应用程序必须处理最终一致的数据。

3. 部署

部署单体应用程序意味着运行一个或多个相同副本的单个较大的应用程序。您通常会在每台服务器上配置 N 个服务器(物理或虚拟)并运行 M 个应用程序实例。单体应用程序的部署并不总是非常简单,但它比部署微服务应用程序要简单得多。

微服务应用程序由数十甚至上百个服务组成。服务以不同的语言和框架编写。每个都是一个迷你的应用程序,具有自己特定的部署、资源、扩展和监视要求。例如,您需要根据该服务的需求运行每个服务的一定数量的实例。此外,必须为每个服务实例提供相应的 CPU、内存和 I/O 资源。更具挑战性的是尽管如此复杂,部署服务也必须快速、可靠和具有成本效益。

1. 单主机多服务实例模式

单主机多服务实例模式(Multiple Service Instances per Host):提供一个或多个物理主机或虚拟主机,并在每个上运行多个服务实例。

Snipaste_2020-10-23_08-53-06

优点:

  1. 资源使用率相对较高。
  2. 是部署服务实例相对较快。
  3. 启动一个服务是非常快的。

缺点:

  1. 是服务实例很少或者没有隔离,除非每服务实例是一个单独的进程。
  2. 部署服务的运维团队必须了解执行此操作的具体细节。
2. 每个主机一个服务实例模式

每个主机一个服务实例(Service Instance per Host)模式:在主机上单独运行每个服务实例。

这种模式有两种不同形式:

  • 每个虚拟机一个服务实例模式

    Snipaste_2020-10-23_08-54-05

  • 每个容器一个服务实例模式(Service Instance per Container)。

    Snipaste_2020-10-23_08-54-47

    每个服务实例都在其自己的容器中运行。容器是一个操作系统级虚拟化机制。一个容器是由一个或多个运行在沙箱中的进程组成。从进程的角度来看,它们有自己的端口命名空间和根文件系统。您可以限制容器的内存和 CPU 资源。一些容器实现也具有I/O 速率限制。容器技术的相关例子有 Docker

    要使用此模式,请将您的服务打包成一个容器镜像。容器镜像是由运行服务所需的应用程序和库组成的文件系统镜像。

    将服务打包成一个容器镜像后,您将启动一个或多个容器。通常在每个物理或虚拟主机上运行多个容器。您可以使用集群管理工具(如 Kubernetes 或 Marathon)来管理容器。集群管理工具将主机视为一个资源池。它根据容器所需的资源和每个主机上可用的资源来决定每个容器放置的位置。

    优点:

    1. 将服务实例彼此隔离,可以轻松地监控每个容器所消耗的资源。
    2. 容器封装了服务实现技术。容器管理 API 作为管理您的服务的 API。
    3. 容器是轻量级技术。容器镜像通常可以非常快速地构建。
    4. 可以很快地启动,因为没有繁琐的操作系统引导机制。

    缺点:

    1. 容器基础架构正在快速发展走向成熟,但它并不像虚拟机的基础架构那么成熟。
    2. 容器不像 VM 那样安全,因为容器彼此共享了主机的 OS 内核。
    3. 需要负责未划分的容器镜像管理。
3.server-less(无服务器)部署

AWS Lambda 就是一个 serverless 部署技术示例。它支持 Java、Node.js 和 Python 服务。要部署微服务,请将其打包成 ZIP 文件并将上传到 AWS Lambda。您还要提供元数据,其中包括了被调用来处理请求(又称为事件)的函数的名称。AWS Lambda 自动运行足够的微服务服务实例来处理请求。您只需根据每个请求所用时间和内消耗来付费。当然,问题往往出现在细节上,您很快注意到了 AWS Lambda 的局限性。但是,作为开发人员的您或组织中的任何人都无需担心服务器、虚拟机或容器的任何方面 ,这非常有吸引力,足以令人难以置信。

Lambda 函数是无状态服务。它通常通过调用 AWS 服务来处理请求。例如,当图片上传到 S3 存储桶时 Lambda 函数将被调用,可插入一条记录到 DynamoDB 图片表中,并将消息发布到 Kinesis 流以触发图片处理。 Lambda 函数还可以调用第三方 Web 服务。

有四种方法调用 Lambda 函数:

  • 直接使用 Web 服务请求

  • 自动响应一个 AWS 服务(如 S3、DynamoDB、Kinesis 或 Simple Email Service)生成的事件

  • 通过 AWS API 网关自动处理来自应用程序客户端的 HTTP 请求

  • 按照一个类似 cron 的时间表,定期执行

正如您所见,AWS Lambda 是一个便捷的微服务部署方式。基于请求的定价意味着您只需为服务实际执行的工作支付。另外,由于您不需要对 IT 基础架构负任何责任,因此可以专注于开发应用程序。

然而,其也存在一些明显的局限性。Lambda 函数不适用于部署长时间运行的服务,例如消耗第三方消息代理消息的服务。请求必须在 300 秒内完成。服务必须是无状态的,63微服务:从设计到部署因为理论上,AWS Lambda 可能为每个请求运行一个单独的实例。他们必须使用受支持的语言之一来编写。服务也必须快速启动,否则,他们可能会因超时而终止。

4. 重构单体变微服务

一个不要使用的策略是“大爆炸”重写。就是您将所有的开发工作都集中在从头开始构建新的基于微服务的应用程序。

策略一:停止挖掘(不在单体上扩展业务)

当您的单体应用变得难以管理时,这是一个很好的建议。换句话说,您应该停止扩张,避免使单体变得更大。

这意味着当您要实现新功能时,您不应该向单体添加更多的代码。相反,这一策略的主要思想是将新代码放在独立的微服务中。

除了新服务和传统的单体,还有另外两个组件。第一个是请求路由,它处理传入的(HTTP)请求,类似于第二章中描述的 API 网关。路由向新服务发送与新功能相对应的请求。它将遗留的请求路由到单体。

另一个组件是粘合代码,它将服务与单体集成。一个服务很少孤立存在,通常需要访问单体的数据。位于单体、服务或两者中的粘合代码负责数据集成。该服务使用粘合代码来读取和写入单体数据。

服务可以使用三种策略来访问单体数据:

  • 调用由单体提供的远程 API

  • 直接访问单体数据库

  • 维护自己的数据副本,与单体数据库同步

粘合代码有时被称为防护层(anti-corruption layer)。这是因为粘合代码阻止了服务被遗留的单体领域模型的概念所污染,这些服务具有自己的原始领域模型。粘合代码在两种不同的模型之间转换。防

策略二:前后端分离
策略三:提取服务(模块转换为服务)

第三个重构策略是将庞大的现有模块转变为独立的微服务。每次提取一个模块并将其转换成服务时,单体就会缩小。一旦您转换了足够的模块,单体将不再是一个问题。或者它完全消失,或者变得足够小,它就可以被当做一个服务看待。

  1. 优先将哪些模块转换为微服务?

    • 提取频繁更改的模块通常是有益的。
    • 提取这些与单体的其他模块有显著不同的模块也是有益的。通过将具有特定资源需求的模块转为服务,您可以使应用程序更加容易、廉价地扩展。
    • 当找到要提取的模块时,寻找现有的粗粒度边界(又称为接缝)是有用的。
  2. 如何提取模块?

    第一步:在模块和单体之间定义一个粗粒度的接口。

    ​ 因为单体需要服务拥有的数据,它很可能是一个双向 API,反之亦然。

    ​ 一旦实现了粗粒度的接口,您就可以将模块变成独立的服务。要做到这点,您必须编写代码以使单体和服务通过使用进程间通信(IPC)机制的 API 进行通信。

    第二个重构步骤是将模块转换为一个独立服务。

    ​ 一旦您提取了一个模块,您就可以独立于单体和任何其他服务开发、部署和扩展其他服务。您甚至可以从头开始重写服务。

    总结

    将现有应用程序迁移到微服务的过程是应用程序现代化的一种形式。您不应该从头开始重写您的应用来迁移到微服务。相反,您应该将应用程序逐渐重构为一组微服务。

    可以使用这三种策略:

    • 将新功能实现为微服务
    • 三层架构,读写分离
    • 将单体中的现有模块转换为服务

    随着时间推移,微服务的数量将会增长,您的开发团队的灵活性和速度也同样会增加。

注1:同步-异步-阻塞-非阻塞
注2:线程实现和jjava线程调度
注2:分布式事务

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号