如何基于 DDD 构建微服务?

本文将讨论微服务与 DDD 涉及到的概念、策划和设计方法,并且尝试将一个单体应用拆分成多个基于 DDD 的微服务。

微服务的定义

微服务中的“微”虽然表示服务的规模,但它并不是使应用程序成为微服务的唯一标准。当团队转向基于微服务的架构时,他们的目标是提高敏捷性,即自主且频繁地部署功能。

因此,很难给微服务架构风格下一个简单的定义。我喜欢 Adrian Cockcroft 关于微服务的简短定义:“面向服务的架构由具有界限上下文、松散耦合的元素组成。”

尽管这定义了一种高级的设计启发式方法,但微服务架构具有的特性,使其有别于以往的面向服务架构。根据以往的文章,我们总结了微服务架构应具备的一些特征:

  1. 服务以业务上下文为中心定义了良好的边界,而不是以任意的技术抽象为中心;

  2. 隐藏实现细节,并通过意图接口暴露功能;

  3. 服务不会共享超出其边界的内部结构,例如不共享数据库;

  4. 服务具有故障快速恢复能力;

  5. 团队职能独立,能够自主发布变更;

  6. 团队拥护自动化文化,例如自动化测试、持续集成和持续交付。

简而言之,我们可以将这种架构风格总结如下:

松散耦合的面向服务的架构,其中每个服务都封装在定义良好的界限上下文中,支持应用程序快速、频繁且可靠的交付。

领域驱动设计和界限上下文

微服务的强大之处在于清晰地定义了它们的职责并划定了它们之间的边界。它的目的是在边界内建立高内聚,在边界外建立低耦合。也就是说,倾向于一起改变的事物应该放在一起。正如现实生活中的许多问题一样,但这说起来容易做起来难,业务在不断发展,设想也随之改变。因此,重构能力是设计系统时考虑的另一项关键问题。

在我们看来,领域驱动设计 (DDD) 是关键,它是设计微服务时必不可少的工具,无论是对单体应用进行拆分还是从头开始构建一个新项目。领域驱动设计因 Eric Evans 的著作而出名,它是一组思想、原则和模式,可以帮助我们基于业务领域的底层模型设计软件系统。开发人员和领域专家一起使用统一的通用语言创建业务模型。然后将这些模型绑定到有意义的系统上,在这些系统和处理这些服务的团队之间建立协作协议。更重要的是,它们设计了系统之间的概念轮廓或边界。

微服务设计从这些概念中汲取了灵感,因为所有这些原理都有助于构建可以独立变更和发展的模块化系统。

在继续深入之前,让我们快速浏览一下 DDD 的一些基本术语。对领域驱动设计的完整概述不在本文的讨论范围之内。

领域(Domain): 代表组织所做的工作。例如零售或电子商务。

子域(Subdomain): 组织或组织内的业务部门。一个领域由多个子域组成。

统一语言(Ubiquitous language):这是用于表达模型的语言。在下面的例子中(图 1),Item 是一个模型,它是每个子域的统一语言。开发人员、产品经理、领域专家和业务各涉众方都能就使用这种语言达成一致,并在他们的工件(代码、产品文档等)中使用该语言。

图 1:电子商务领域中的子域和界限上下文

界限上下文(Bounded Contexts):领域驱动设计将界限上下文定义为“一个单词或语句出现时确定其含义的设置”。简而言之,这意味着模型在边界内是有含义的。在上面的例子中(图 1),“Item”在每个上下文中都有不同的含义。在 Catalog 上下文中,Item 表示可出售的产品,而在 Cart 上下文中,它表示客户已添加到购物车中的商品选项。在 Fulfillment 上下文中,它表示将要运送给客户的仓库物料。这些模型各不相同,每个模型都有不同的含义,并且可能包含不同的属性。通过将这些模型分离并将其隔离在各自的边界内,我们就可以自由地表达这些模型,而不会产生歧义。

注意: 必须理解子域和界限上下文之间的区别。子域属于问题空间,即我们的业务要如何看待问题,而界限上下文属于解决方案空间,即我们将如何实施问题的解决方案。理论上,每个子域可能有多个界限上下文,尽管我们努力每个子域只提供一个界限上下文。

微服务和界限上下文如何关联

现在,微服务适用于哪些地方?每个界限上下文都能映射到对应的微服务吗?不一定。我们来看看原因。在某些情况下,界限上下文的边界或轮廓可能会非常大。

图 2:界限上下文和微服务

考虑上面的例子。定价界限上下文有三个不同的模型:价格(Price)、定价项(Priced items) 和折扣(Discounts),分别负责目录项的价格、计算列表项的总价以及各自使用的折扣。我们可以创建一个包含上述所有模型的单一系统,但它可能是一个不合理的大型应用程序。如前所述,每个数据模型都有其不变性和业务规则。随着时间的推移,如果我们不小心的话,这个系统就可能会变成一个大泥球,界限模糊,职责重叠,甚至很可能会回到我们开始的地方——单体应用。

对这个系统建模的另一种方法是将相关模型分离或分组到单独的微服务中。在 DDD 中,这些模型(价格、定价项和折扣)被称为聚合(Aggregates)。聚合是由相关模型组成的自包含模型。我们只能通过已发布的接口来变更聚合的状态,并且聚合可以确保一致性,而且不变量可以始终保持良好状态。

在形式上,聚合是关联对象的集群,被视为数据变更的单元。外部引用仅限于指定聚合的一个成员,即聚合根。在聚合的边界内需应用一组一致性规则。

图 3:定价上下文中的微服务

同样,没有必要将每个聚合都建模为一个不同的微服务。事实证明,图 3 中的服务(聚合)就是如此,但这不一定是一个规则。在某些情况下,在单个服务中托管多个聚合可能是有意义的,特别是在我们不完全了解业务领域的情况下。需要注意的一点是,一致性只在单个聚合中才能得到保证,并且聚合只能通过已发布的接口进行修改。任何违反这些规则的行为都有增加应用程序变成一个大泥球的风险。

上下文映射:一种精确划分微服务边界的方法

另一个基本工具是上下文映射,同样,它也是来自领域驱动设计。一个单体应用通常由不同的模型组成,这些模型大多是紧密耦合的,模型之间可能知道彼此的实现细节,变更一个模型可能造成另一个模型的副作用等等。当你分解单体应用时,确定这些模型(在这里是聚合)及其关系是至关重要的。上下文映射可以帮助我们做到这一点。它们用于识别和定义各种界限上下文和聚合之间的关系。在上面的例子中,界限上下文定义了模型的边界(价格、折扣等等)。而上下文映射定义了这些模型之间以及不同上下文之间的关系。在确定了这些依赖关系之后,我们就可以确定下来实现这些服务的团队之间的正确协作模型了。

对上下文映射的完整探索不在本文的讨论范围之内,但我们将用一个示例来说明。下图显示了处理电子商务订单支付的各种应用程序。

购物车上下文负责订单的在线授权;订单上下文处理订单履行完成后的支付流程,如结算;联络中心处理任何异常情况,如支付重试和变更订单使用的支付方式。为了简单起见,我们假设所有这些上下文都是作为单独的服务实现的,所有这些上下文封装了同一个模型。请注意,这些模型在逻辑上是相同的。也就是说,它们都遵循相同的统一领域语言——支付方式、授权和结算。只是它们是不同上下文的一部分。

另一个迹象表明,同一个模型在不同的上下文中传播,所有这些模型都直接与单个支付网关相集成,并且彼此执行相同的操作。

图 4:定义错误的上下文映射

重新定义服务边界:将聚合映射到正确的上下文

在上面的设计中有一些非常明显的问题(图 4)。支付聚合是多个上下文的一部分。在各种服务之间强制执行不变性和一致性是不可能的,更不用说这些服务之间的并发问题了。例如,如果在订单服务尝试按之前提交的付款方式进行结算的过程中,联络中心更改了与订单关联的付款方式会发生什么情况。另外,请注意,支付网关中的任何更改都将迫使对多个服务进行更改,可能会涉及到多个团队,因为它们共同拥有这些上下文。

通过一些调整并将聚合与正确的上下文对齐,我们就可以更好地表示这些子域了(图 5)。需要进行很多的更改。

我们来看下更改的点:

  1. 支付聚合有了一个新家——支付服务。该服务还从其他需要支付服务的服务中提取了支付网关。由于单个界限上下文现在拥有了单个聚合,所以不变量很容易管理;所有事务都在同一个服务的边界内进行,从而避免了任何并发问题。

  2. 支付聚合使用了反腐层(ACL)将核心领域模型与支付网关的数据模型隔离开来,后者通常是由第三方提供的,可能会发生变化。在以后的文章中,我们将深入研究基于“端口和适配器”模式的应用程序设计。ACL 层通常包含将支付网关的数据模型转换为支付聚合数据模型的适配器。

  3. 购物车服务通过直接调用 API 的方式来调用支付服务,因为当客户在网站上购物时,购物车服务需要完成支付授权。

  4. 记录订单和支付服务之间的交互作用。订单服务发出一个域事件(稍后会在本文中对此进行详细介绍)。支付服务监听此事件并完成订单的结算

  5. 联络中心服务可能有许多聚合,但我们只对该用例中的订单聚合感兴趣。当更改付款方式时,此服务发出一个事件,支付服务将通过以下方式对此事件做出响应:将先前使用的信用卡撤销,再处理新的信用卡。

图 5:重新定义的上下文映射

通常,单体或遗留应用程序有许多聚合,且边界重叠。创建这些聚合及其依赖关系的上下文映射,将有助于我们理解从这些单体应用中获取任何新微服务的轮廓。请记住,微服务架构的成败取决于聚合之间的低耦合以及聚合之内的高内聚。

还需要注意的是,界限上下文本身就是合适的内聚单元。即使上下文有多个聚合,也可以将整个上下文及其聚合组成单个微服务。我们发现这种启发式方法对于有些模糊的领域特别有用,比如组织正在涉足的新业务领域。我们可能对分离的正确边界没有足够的了解,并且任何过早的聚合分解都可能导致昂贵的重构。试想一下,由于数据迁移,不得不将两个数据库合并为一个,因为我们偶然发现两个聚合属于同一类。但是要确保这些聚合通过接口是充分隔离的,这样它们就不知道彼此的复杂细节了。

事件风暴:另一种识别服务边界的技术

事件风暴(Event Storming)是识别系统中聚合(以及微服务)的另一种必不可少的技术。它是一个非常有用的工具,既可用于分解单体应用,也可用于设计复杂的微服务生态系统。我们已经使用这种技术分解了一个复杂的应用程序,并打算写一篇单独的文章来介绍我们的事件风暴经验。在本文中,我们只给出一个快速的高层概述。

简而言之,事件风暴是在应用程序团队(这里,指单体)中进行的头脑风暴,以识别系统中发生的各种领域事件和流程。团队还需确定这些事件影响的总和或模型,以及由此产生的任何后续影响。当团队做这个头脑风暴时,他们将识别不同的重叠概念、模棱两可的领域语言和相互冲突的业务流程。他们对相关的模型进行分组,重新定义聚合并识别重复的流程。随着这些工作的进行,这些聚合所属的界限上下文变得清晰起来。如果所有团队都在同一个房间(物理或虚拟)里,并开始在 Scrum 风格的白板上绘制事件、命令和流程的映射,那么事件风暴研讨就会非常有用。在本练习结束时,通常会产出如下成果:

  1. 重新定义的聚合列表。这些可能会成为新的微服务

  2. 需要在这些微服务之间流动的领域事件

  3. 其他应用程序或用户直接调用的命令

下面是我们在一次事件风暴研讨会结束时产生的一个示例样板。对于团队来说,就正确的聚合和界限上下文达成一致是一次很棒的协作活动。此外,团队在本次会议结束时还对领域、统一语言和精确的服务边界有着共同的理解。

图 6:事件风暴板

微服务之间的通信

快速回顾一下,一个单体应用在单个流程边界内拥有多个聚合。因此,可以在这个边界内管理各个聚合的一致性。例如,如果客户下了订单,我们可以减少商品库存,并向客户发送电子邮件,所有这些都在一个事务中完成。所有操作要么都成功,要么都会失败。但是,当我们分解了单体并将聚合分散到不同的上下文中时,我们将拥有数十个甚至数百个微服务。但目前为止,存在于单体应用单一边界内的流程,现在被分散到了多个分布式系统中。要在所有这些分布式系统中实现事务的完整性和一致性是非常困难的,而且要以系统的可用性为代价。

微服务也是分布式系统。因此,CAP 定理也适用于它们:“一个分布式系统只能提供三个所需特性中的两个:一致性、可用性和分区容错(CAP 中的‘C’——Consistency、‘A’——Availability 和 ‘P’——Partition Tolerance)。”在现实世界的系统中,分区容错是不可协商的——网络是不可靠的、虚拟机可以宕机、区域之间的延迟可能会变得更糟等等。

因此,我们可以选择“可用性”或“一致性”。现在,我们知道,在任何现代应用程序中,牺牲“可用性”也不是一个好主意。

图 7:CAP 定理

围绕最终一致性设计应用程序

如果我们想要跨多个分布式系统构建事务,那么我们将再次陷入单体应用的困境。但这一次它会是最糟糕的一种单体:一个分散的单体应用。如果这些系统中的任何一个变得不可用,则整个流程不可用,通常会导致极差的客户体验、承诺的失败等等。此外,对一个服务的变更通常会导致另一个服务的变更,从而引起复杂和昂贵的部署。因此,我们最好根据自己的用例来设计应用程序,容忍稍微的不一致,以提高可用性。对于上面的例子,我们可以使所有流程异步,从而达到最终的一致性。我们可以独立于其他流程,异步发送电子邮件;如果已经承诺的商品以后在仓库中不可用,那么该商品可能需要补货,或者我们可以停止接受超出某个阈值的该商品的订单。

有时,我们可能会遇到这样的场景:可能需要跨越不同流程边界的两个聚合的强 ACID 式的事务。这是一个重新审视这些聚合并将它们组合成一个聚合的极好迹象。开始在不同流程边界中分解这些聚合之前,事件风暴和上下文映射将有助于我们及早识别这些依赖关系。将两个微服务合并为一个的成本很高,这是我们应该努力避免的。

支持事件驱动的架构

微服务可以将发生在其聚合上的基本更改发出来,这些称为领域事件(Domain Event),并且任何对这些更改感兴趣的服务都可以监听这些事件并在其领域内执行相应的操作。这种方法避免了任何行为上的耦合(一个领域无需规定其他领域应该做什么)以及时间上的耦合(一个流程的成功完成不依赖于所有系统同时可用)。当然,这将意味着系统最终是一致的。

图 8:事件驱动架构

在上面的示例中,订单服务发布一个事件:订单已取消。订阅了该事件的其他服务处理各自的领域功能:支付服务退款,库存服务调整商品的库存,等等。为确保此集成的可靠性和弹性,需要注意以下几点:

  1. 生产者应确保至少发出了一次事件。如果过程中出现失败,则应确保存在回退机制以重新触发事件

  2. 消费者应该确保以幂等的方式消费事件。如果再次发生同一事件,不会对消费者产生任何副作用。事件也可能不是顺序到达的。消费者可以使用时间戳或版本号字段来保证事件的唯一性。

由于某些用例的特性,不一定总是可以使用基于事件的集成。请看一下购物车服务和支付服务之间的集成。这是一个同步集成,因此我们需要注意一些事项。这是一个行为耦合的例子——购物车服务可能会从支付服务调用一个 REST API,并指示它授权订单的支付,而时间耦合——支付服务需要对购物车服务可用,它才能接受订单。这种耦合降低了这些上下文的自治性,也可能会引入不必要的依赖。有几种方法可以避免这种耦合,但是如果使用了所有这些选项,我们将失去向客户提供即时反馈的能力。

  1. 将 REST API 转换为基于事件的集成。但是,如果支付服务仅公开 REST API,则此选项可能不可用

  2. 购物车服务立即接受订单,并且有一个批处理作业来接管订单并调用支付服务 API

  3. 购物车服务生成一个本地事件,然后调用支付服务 API

在出现失败和上游依赖项(支付服务)不可用的情况下,将上述方法与重试相结合,可以产生更具弹性的设计。例如,在发生故障的情况下,可以通过基于事件或批处理的重试来备份购物车和支付服务之间的同步集成。这种方法会对客户体验产生额外的影响:客户可能输入了不正确的支付详细信息,当我们离线处理支付时,无法强制他们在线。或者,收回失败的支付可能会增加企业的成本。但在所有可能的情况下,让购物车服务对支付服务的不可用性或故障具有弹性,利大于弊。例如,如果我们无法离线收款,我们可以通知客户。简而言之,在用户体验、弹性和运营成本之间存在着权衡,在设计系统时,明智的做法是充分考虑这些折衷。

避免为了满足调用者的特定数据需求而编排服务

存在于任何面向服务架构的一个反模式是:服务迎合调用者的特定访问模式。通常,当调用者团队与服务提供者团队紧密合作时,就会发生这种情况。如果团队正在开发一个单体应用程序,它们通常会创建一个跨越不同聚合边界的 API,从而使这些聚合紧密耦合在一起。我们来看一个例子。比如说 Web 中的订单详情页面,移动应用程序需要在单个页面上显示订单详情和订单退款处理详情。在一个单体应用程序中,订单获取 API(Order-GET-API,假设它是 REST API)需要同时查询订单和退款,合并两个聚合并向调用方发送一个复合响应。由于聚合属于同一流程边界,因此可以在没有太多开销的情况下实现这一点。调用者可以在一次会话中获得所需的所有数据。

如果订单和退款是不同上下文的一部分,那么数据不再出现在单个微服务或聚合边界内。为调用者保留相同功能的一个选项是,让订单服务负责调用退款服务并创建一个复合响应。这种方法会引起以下几个问题:

  1. 订单服务现在与另一个服务集成,纯粹是为了支持那些需要退款数据和订单数据的调用者。现在订单服务的自治性降低了,因为退款聚合的任何更改都会导致订单聚合的更改。

  2. 订单服务有另一个集成,因此需要考虑另一个故障点:如果退款服务出现故障,订单服务是否仍可以发送部分数据,并且调用者是否可以优雅地处理故障呢?

  3. 如果调用者需要变更,以从退款聚合中获取更多的数据,那么现在需要两个团队同时进行变更

  4. 如果跨平台都遵循这种模式,则可能会导致各种域服务之间形成复杂的依赖关系网,这都是因为这些服务迎合了调用者特定的访问模式。

专门服务于前端的后端(BFFs)

一种减轻这种风险的方法是让调用者团队管理各种域服务之间的编排。毕竟,调用方更了解访问模式,并且可以完全控制对这些模式的任何更改。这种方法将域服务从表示层解耦出来,让它们专注于核心业务流程。但是,如果 Web 和移动应用程序开始直接调用不同的服务,而不是从单体中调用一个复合 API,这可能会给这些应用程序带来性能开销——在较低带宽的网络上进行多次调用,处理和合并来自不同 API 的数据,等等。

相反,可以使用另一种称为用于前端的后端模式(Backend for Front-ends)。在这种设计模式中,由消费者创建和管理的后端服务,在本例中是 Web 和移动团队,它负责对多个域服务进行集成,纯粹是为了向客户提供前端体验。Web 和移动团队现在可以根据它们所需要的用例来设计数据契约。它们甚至可以使用 GraphQL 而不是 REST API 来灵活地查询并获取所需的内容。需要注意的是,该服务是由消费者团队拥有和维护的,而不是提供域服务的团队。前端团队现在可以根据它们的需求进行优化——移动应用程序可以请求更小的有效负载,减少来自移动应用程序的会话次数,等等。下面是修订后的业务流程图。BFF 服务现在为其用例调用“订单”和“退款”域服务。

图 9:用于前端的后端

尽早构建 BFF 服务也很有用,这样可以避免从单体系统中分解出过多的服务。否则,要么域服务必须支持域间编排,要么 Web 和移动应用程序必须直接从前端调用多个服务。这两个选项都会导致性能开销、一次性工作以及团队之间缺乏自治。

相关阅读:

1.  Eric Evans’ Domain Driven Design

2.  Vaughn Vernon’s Implementing Domain Driven Design

3.  Martin Fowler’s article on Microservices

4.  Sam Newman’s Building Microservices

5.  Event storming

7.  Backend for Frontends

8.  Fallacies of distributed computing

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308683.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Core ResponseCache【缓存篇(一)】

一、前言源码1、最近一直在看项目性能优化方式,俗话说的好项目优化第一步那当然是添加缓存,我们的项目之所以卡的和鬼一样,要么就是你的代码循环查询数据库(这个之前在我们的项目中经常出现,现在慢慢在修正&#xff09…

[Swagger2]分组和接口注释及小结

分组和接口注释及小结 配置API分组 1、如果没有配置分组,默认是default。通过groupName()方法即可配置分组: Bean public Docket docket(Environment environment) {return new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo()).groupName(&qu…

Asp.Net Core 中的“虚拟目录”

写在前面现在部署Asp.Net Core应用已经不再限制于Windows的IIS上,更多的是Docker容器、各种反向代理来部署。也有少部分用IIS部署的,IIS部署确实是又快又简单,图形化操作三下五除二就可以发布好一个系统了。在过去Asp.Net MVC 项目部署的时候…

[mybatis]缓存_缓存有关的设置以及属性

缓存有关的设置以及属性 全局配置中的设置 和缓存有关的设置/属性1.cacheEnabletrue;false;关闭缓存(二级缓存关闭)(一级缓存一直可用的)2.每个select标签都有useCache“true”; false;不使用缓存(一级缓存依然使用,二…

EF批量插入太慢?那是你的姿势不对

大概所有的程序员应该都接触过批量插入的场景,我也相信任何的程序员都能写出可正常运行的批量插入的代码。但怎样实现一个高效、快速插入的批量插入功能呢?由于每个人的工作履历,工作年限的不同,在实现这样的一个需求时&#xff0…

[RabbitMQ]什么是MQ

什么是MQ MQ(message queue),从字面意思上看,本质是个队列,FIFO 先入先出,只不过队列中存放的内容是message 而已,还是一种跨进程的通信机制,用于上下游传递消息。在互联网架构中,MQ 是一种非常…

.NET Core 实现基于Websocket的在线聊天室

什么是Websocket我们在传统的客户端程序要实现实时双工通讯第一想到的技术就是socket通讯,但是在web体系是用不了socket通讯技术的,因为http被设计成无状态,每次跟服务器通讯完成后就会断开连接。在没有websocket之前web系统如果要做双工通讯…

用 Natasha 写个类型调用的架子

一、想法自上篇文章,我一直琢磨整个好点的例子来展示 Natasha 动态编程能力, 于是就写了一个简单的类型调用的架子,耗时40分钟左右, 项目地址:https://github.com/NMSAzulX/TypeCaller二、功能特点a)、简单的注入功能支持无参构造注入支持递…

Natasha v4.0.0.0 动态编程新篇章

一、简介Natasha 基于 Roslyn 的 C# 动态程序集构建库,该库允许开发者在运行时使用 C# 代码构建域 / 程序集 / 类 / 结构体 / 枚举 / 接口 / 方法等,使得程序在运行的时候可以增加新的模块及功能。Natasha 集成了域管理/插件管理,可以实现域隔…

[RabbitMQ]RabbitMQ概念_四大核心概念

RabbitMQ RabbitMQ 的概念 RabbitMQ 是一个消息中间件:它接受并转发消息。你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ …

.Net Core in Docker极简入门(下篇)

点击上方蓝字"小黑在哪里"关注我吧Docker-Compose代码修改yml fileup & down镜像仓库前言上一篇【.Net Core in Docker极简入门(上篇)】讲解了docker的一些基本命令和操作,并成功构建了自己的asp.net core web应用的镜像&#…

这么多Apache顶级项目,SkyWalking为何一枝独秀?

吴晟读完需要5分钟速读仅需 2 分钟吴晟Apache基金会会员,Apache SkyWalking创始人、项目VP和PMC成员,Apache孵化器PMC成员,Apache ShardingSphere PMC成员,Apache APISIX (incubating) PPMC成员,Apache ECharts (incub…

[RabbitMQ]工作原理_原理名词解释

RabbitMQ 核心部分 各个名词介绍 RabbitMQ工作原理 Broker: 接收和分发消息的应用,RabbitMQ Server 就是 Message Broker Virtual host: 出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类…

Istio 中的授权策略详解

本文节选自 ServiceMesher 社区出品的开源电子书《Istio Handbook——Istio 服务网格进阶实践》,阅读地址:https://www.servicemesher.com/istio-handbook/授权功能是 Istio 中安全体系的一个重要组成部分,它用来实现访问控制的功能&#xff…

[RabbitMQ]创建Java开发环境_消费者_生产者

我们将用 Java 编写两个程序。发送单个消息的生产者和接收消息并打印出来的消费者。我们将介绍 Java API 中的一些细节。 在下图中&#xff0c;“ P”是我们的生产者&#xff0c;“ C”是我们的消费者。中间的框是一个队列-RabbitMQ 代表使用者保留的消息缓冲区 引入依赖 <…

如何利用Gitlab-CI持续部署到远程机器?

长话短说&#xff0c;今天聊一聊使用Gitlab-CI 自动部署到远程服务器。如果看过《基于docker-compose的Gitlab CI/CD实践&排坑指南》这篇文章的朋友&#xff0c;会注意到我是在 Gitlab-Runner服务器上自动部署的站点&#xff0c;本次我们结合ssh部署到远程机器(将CI服务器和…

[RabbitMQ]工作队列原理_代码实现

Work Queues 工作队列(又称任务队列)的主要思想是避免立即执行资源密集型任务&#xff0c;而不得不等待它完成。 相反我们安排任务在之后执行。我们把任务封装为消息并将其发送到队列。在后台运行的工作进程将弹出任务并最终执行作业。当有多个工作线程时&#xff0c;这些工作…

使用ImpromptuInterface反射方便的创建自定义DfaGraphWriter

在本文中&#xff0c;我为创建的自定义的DfaGraphWriter实现奠定了基础。DfaGraphWriter是公开的&#xff0c;因此您可以如上一篇文章《将终结点图添加到你的ASP.NET Core应用程序中》中所示在应用程序中使用它&#xff0c;但它使用的所有类均已标记为internal。这使得创建自己…

[RabbitMQ]消息应答概念_消息手动应答代码

消息应答 概念 消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消 息标记为删除。在这种情况下&#xff0c;突然有…

rust火箭基地主楼开启方法_Rust 为什么能成为 Stack Overflow 最受欢迎的语言?

每年&#xff0c;开发者问答网站 Stack Overflow 都会对程序员社区展开年度调查&#xff0c;包括他们最喜爱的技术到工作偏好的所有内容。 在2017 年和2018 年Stack Overflow 年度开发者调查中&#xff0c;Rust语言已经连续两年成为最受欢迎语言Top 1。2018 年 Stack Overflow …