两个微服务之间的通信
Posted
技术标签:
【中文标题】两个微服务之间的通信【英文标题】:Communication between two microservices 【发布时间】:2016-08-10 14:41:14 【问题描述】:我正在创建一个具有微服务架构的项目。我创建了两个微服务。
其中一个用于产品实体,另一个用于票据实体。它们有自己的端点,并与网关连接在一起(我使用的是 jhipster 微服务架构)。
bill-ms 应该访问产品列表。我想知道如何在这两个 ms 之间进行通信。我想到了三种方法:
从 bill-ms 向队列发送请求 - 例如 rabbitMQ,以从 product-ms 获取具有这些 id 的这些产品(我不知道这是什么瓶颈)
向网关发送产品服务请求并从那里获取产品(由于它们之间的数据大小,我担心延迟,因此我不会直接接触数据库,所以我总是取决于网关)
我可以在bill-ms中复制repositories、services和entities(这是一种丑陋的方式,而且我认为它违反了ms-architecture的规则并且维护起来非常困难)
如果你有任何其他方法,我很感激你与我分享。
编辑
-
现在我知道瓶颈是什么了:假设有 3 个 bill-ms 实例,rabbitMQ 如何决定响应哪个实例?或者我应该如何对功能区说“给我免费的 bill-ms 实例以订阅来自 rabbitMQ 的请求”以实现负载平衡。
【问题讨论】:
你也可以重新考虑你的服务边界,也许你的服务太细了。重复也可能不是坏事,如果您认为您的产品可能会消失或可能在他们的数据库中被修改,而您希望它们在账单中长时间保持不变,在这种情况下,我不会认为这是重复,但只有存储有关您的账单所需产品的不可变信息。 “我正在使用 jhipster 微服务架构”..JHipster 只是一个没有自己架构的库.. Jhipster 不是一个库,它是一个应用生成器,恰好在 spring cloud netflix 之上实现了微服务架构。 所以更大的问题就在这里:当您实际上遇到数据可供彼此使用的问题时,为什么要采用微服务方式?我宁愿建议从一个适当模块化的单体开始。永远不要做选项 3,因为那你根本不需要微服务。不要害怕选项 2,春天的 netflix 世界有大量的解决方案来解决即将出现的问题(eureka、feign、ribbon、hystrix ......) 使用负载平衡(通过功能区)发现客户端来获取产品服务实例,或者上面提到的选项 3 似乎更好的选择。只要只有一个服务(所有者)修改数据,复制数据就可以了,而其他服务保持不变的副本。 Rabbit MQ 可用于保持重复数据的同步。 【参考方案1】:我不确定我要回答的是否正确。我还在学习自己。但我可以告诉你我是如何实现我的微服务尝试的。
首先,我从HTTP
基于通信的微服务using this blog 开始。这很好用,但问题是,您在服务之间创建 dependendies。 服务 A 需要知道 服务 B 并且需要直接调用它(当然是通过服务发现等)。这是您在开发微服务时通常要避免的。
我最近开始使用的另一种方法是使用message bus
。这实际上是您在问题中涉及的第三个选项。
我有一个服务 A,用于存储人员(只是一个示例)。该服务在创建一个新人时所做的是:它在RabbitMQ
总线上发送event
:personCreatedEvent
。
如果有任何其他服务对此类事件感兴趣,他们可以订阅。这些感兴趣的服务将他们感兴趣的相关信息保存在自己的数据存储中。
使用最后一种方法,您的服务之间没有真正的依赖关系,因为它们不直接相互通信。 服务 A 不知道 服务 B,因为 B 只是将事件发送到 RabbitMQ
到对这些事件感兴趣的任何服务,反之亦然。
当然,您在服务上的数据存储之间存在重复。但这也可以是有利可图的,例如服务 B 不需要使用与服务 A 相同的架构或数据存储机制。它只以最适合该服务的方式存储相关信息。
【讨论】:
这种发布/订阅设计模式是一个非常好的方法。 从您的建议中了解到,我将只使用我需要的产品实体的属性。这就是为什么我打算为此创建一个 DTO,并且产品实体保存在 product-ms 中。将所有数据保存在一个微服务中并从 rabbitMQ 获取数据作为 dto 是一种好方法吗?我理解正确吗? @Luxo 如果您的账单女士需要访问产品。 product-ms 需要在 RabbitMQ 总线上发布事件,如“ProductCreated”,包含产品本身。由于 bill-ms 订阅了这些事件,它将接收这些事件,并将实体的部分(本示例中的产品)保存在它自己的数据存储中。 @SerhatTR @Kaj 如果重点是孤立地开发每个微服务,那么这对开发人员有什么作用?如果 bill-ms 依赖于 product-ms,在开发中 bill-ms 将永远不会收到这些事件。很确定我在这里遗漏了一些重要的东西。【参考方案2】:你看过http://stytex.de/blog/2016/03/25/jhipster3-microservice-tutorial/第2部分:服务间通信部分。它会引导您通过一个具体的例子来说明它是如何实现的
【讨论】:
【参考方案3】:让我尝试在此场景中添加更多细节,以强调在 Product 和 Biiling 的上下文中可能或可能不符合事件的条件。只有在下订单的情况下,Billing-MS 才需要与 Product-Ms 交谈。下订单主要是针对单独的 MS,比如说 Order-MS。创建或下订单时,它将包含产品信息作为订单项。
订单的创建可以被视为一个事件。当订单创建事件发生时,它可以被推送到账单服务的队列中。队列应该在 RabbitMQ 中实现为工作队列。这样,Billing-MS 的多个实例可以订阅同一个队列,但它将由一个且只有一个 Worker 处理。在将服务注册为 RabbitMQ 的 Worker 时,RIBBON 没有任何作用。每个实例注册到一个队列,RabbitMQ 决定 RoundRobin 哪个 Billing Service 实例来处理这个事件。
为 Billing-Ms 获取订单中产品的详细信息应该是通过 Ribbon 进行负载平衡的 Service-to-Service 调用(如果您正在使用的话)。获取产品详细信息并不是真正的事件,下订单是,因此有所不同。
此外,网关应该用于公开您的边缘服务。对于 Service-to-Service 调用,通过网关服务跳转并不理想。
【讨论】:
【参考方案4】:一种选择是使用其在 eureka 注册表上的注册名称向微服务发送计费请求。
【讨论】:
【参考方案5】:您可以使用以下解决方案: 微服务 A(即 UAA-SERVICE)和微服务 B。 微服务 B 想连接微服务 A 并通过 Feign 客户端调用服务。
1)微服务 B 的代码
@AuthorizedFeignClient(name = "UAA-SERVICE")
公共接口 UaaServiceClient
@RequestMapping(method = RequestMethod.GET, path = "api/users")
public List<UserDTO> getUserList();
@RequestMapping(method = RequestMethod.PUT, path = "api/user-info")
public String updateUserInfo(@RequestBody UserDTO userDTO);
UAA-SERVICE : 通过注册表运行应用程序实例找到此名称。
2) 在微服务 B (application.yml)中 增加feign客户端连接超时------> 假装: 客户: 配置: 默认值: 连接超时:10000 读取超时:50000 enter image description here 增加hystrix线程超时-------->
hystrix: 命令: 默认: 执行: 隔离: 线: 超时毫秒:60000 shareSecurityContext: 真
enter image description here 3) 在@SpringBootApplication 主类中添加@EnableFeignClients。-------> 这个解决方案对我来说很好。
【讨论】:
为所附图像提供相关描述,以便读者更清楚地了解他们将要看到的内容以上是关于两个微服务之间的通信的主要内容,如果未能解决你的问题,请参考以下文章