两个微服务之间的通信

50

我正在创建一个微服务架构的项目。我创建了两个微服务。

其中之一是产品实体的微服务,另一个是账单实体的微服务。它们有自己的终端点,并且使用网关相互连接(我正在使用jhipster微服务架构)。

账单-ms应该访问产品列表。我想知道如何在这两个微服务之间进行通信。我有三种方法:

  1. 从账单-ms发送请求到队列 - 如rabbitMQ ,以获取来自产品-ms 的具有这些ID的产品(我不知道瓶颈在哪里)。

  2. 向网关发送请求以获取产品服务,并从那里获取产品(我担心由于数据大小而产生的延迟,并且以这种方式我没有直接接触数据库,因此我始终依赖于网关)。

  3. 我可以在账单-ms 中复制存储库、服务和实体(这是一种丑陋的方式,我认为它违反了ms-architecture规则,而且维护非常困难)。

如果您有其他方法,请与我分享。

编辑

  1. 现在我知道瓶颈在哪里:假设有3个账单-ms 的实例,rabbitMQ如何决定哪个实例作出响应?或者我应该怎样告诉ribbon“给我免费的账单-ms 实例以订阅来自rabbitMQ的请求”进行负载均衡。

2
你也可以重新考虑一下你的服务边界,也许你的服务过于细粒度了。此外,如果你考虑到你的产品可能会消失或在数据库中被修改,而你希望它们在账单中长时间保持不变,那么重复可能并不是坏事,在这种情况下,我不会认为这是重复,而只是存储关于产品的不可变信息,以满足你的账单需求。 - Gaël Marziou
我正在使用JHipster微服务架构。JHipster只是一个库,没有自己的架构。 - Ali Dehghani
4
Jhipster不是一个库,而是一个应用程序生成器,它在Spring Cloud Netflix之上实现了微服务架构。 - Gaël Marziou
所以这里的更大问题是:当您实际上有使数据彼此可用的问题时,为什么要走微服务的路线?我宁愿建议从适当模块化的单体应用开始。永远不要选择选项3,因为那样您根本不需要微服务。不要害怕选项2,Spring Netflix世界有很多解决即将出现的问题的解决方案(Eureka、Feign、Ribbon、Hystrix等)。 - daniel.eichten
使用负载均衡(由Ribbon提供)的发现客户端来获取产品服务的实例,或者选择上述第三个选项似乎是更好的选择。只要只有一个服务(所有者)对其进行修改,其他服务保持相同的不可变副本,复制数据应该是可以的。可以使用Rabbit MQ来保持重复数据同步。 - Munish Chandel
6个回答

46

我不确定我的回答是否正确。但是,我可以告诉您我如何实现微服务尝试。

首先,我从使用 HTTP 通信的微服务开始,参考了这篇博客。这种方式很好,但问题在于您会在服务之间创建依赖关系。例如,A服务需要知道一个B服务的存在,并需要直接调用它(当然是通过服务发现等方式)。而这正是开发微服务时要避免的。

另一种方法是使用 消息总线,这是您最近开始使用的第三种选项。

我有一个A服务,它存储个人信息(仅作为示例)。当该服务创建新的个人信息时,它会在 RabbitMQ 总线上发送一个 personCreatedEventevent。如果其他服务对此类事件感兴趣,则可以订阅它们并将其自己感兴趣的相关信息保存在自己的数据存储中。

使用这种最后一种方法,服务之间没有真正的依赖关系,因为它们不直接彼此通信。例如,B服务并不需要直接与 A服务 通信,而是向 RabbitMQ 发送事件,并将这些事件发送到对此类事件感兴趣的任何服务,反之亦然。

当然,您的服务之间会有数据存储副本。但是,这也可能是有利可图的,例如B服务不需要使用与A服务相同的模式或数据存储机制,它只需以最适合该服务的方式存储相关信息即可。


1
这种发布/订阅设计模式是一个非常好的方法。 - KevinO
根据您的建议,我理解我将只使用产品实体所需的属性。因此,我计划为此创建一个DTO,并将产品实体保存在product-ms中。将所有数据保存在一个微服务中并从rabbitMQ获取数据作为dto,这是一个好的方法吗?我是否正确理解了您的意思?@Luxo - SerhatTR
如果您的账单管理系统需要访问产品,那么产品管理系统需要在RabbitMQ总线上发布事件,例如“ProductCreated”,其中包含产品本身。由于账单管理系统已经订阅了这些事件,它将接收到这些事件,并将实体的部分(例如此示例中的产品)保存在自己的数据存储中。@SerhatTR - Kaj
@Kaj 如果目的是在隔离环境中开发每个微服务,那么这对开发人员来说如何工作呢?如果bill-ms依赖于product-ms,在开发过程中,bill-ms将永远不会收到这些事件。我很确定我在这里漏掉了一些重要的东西。 - kenchilada

2

0
让我尝试添加更多细节来强调在产品和计费上下文中什么可能或不可能被视为事件。只有在下订单时,计费MS才需要与产品MS交流。下订单通常是由另一个MS(比如Order-MS)完成的。当创建或下订单时,它将包含产品作为行项目的信息。
创建订单可以被视为事件。当订单创建事件发生时,可以将其推送到RabbitMQ中的队列以供计费服务使用。队列应该实现为工作队列。这样,多个计费MS实例可以订阅同一个队列,但只会由一个Worker处理。在注册服务作为RabbitMQ的Worker时,RIBBON没有任何作用。每个实例都注册到队列中,RabbitMQ决定RoundRobin哪个计费服务实例将处理此事件。
对于Billing-Ms中订单中的产品详细信息,应使用Ribbon进行负载均衡的服务对服务调用(如果您正在使用)。获取产品详细信息并不是真正的事件,下订单才是,因此存在差异。
此外,网关应用于公开您的边缘服务。对于服务对服务调用,通过网关服务跳转并不理想。

0

一种选择是使用Eureka注册表上的已注册名称向账单微服务发送请求。


0

一般来说,你有两个好的选择来通信微服务,还有几个要尽可能避免的:

  • 不要使用共享数据库
  • 不要使用FTP文件共享
  • 不要使用内存共享数据库
  • 不要使用CSV或类似格式

应该使用什么:

  • 选项1(最佳):使用队列系统共享消息,因此如果微服务B需要从微服务A获取某些信息,则A将向队列发送消息,B将消耗它。这是最强大的解决方案,因为如果B停机,它在恢复时仍会消耗消息。
  • 选项2:使用RESTFul端点,您可以从A调用以通知B或从B获取A的信息。问题是,如果接收者停机或失败,请求将中断,您将脱节。然后需要实现circuit-breaker以避免丢失。

您可以在我的文章正确通信微服务中找到更多关于如何正确通信微服务的信息。


0
您可以使用以下解决方案: 微服务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客户端连接超时时间------> feign: client: config: default:
connectTimeout:10000 readTimeout:50000 enter image description here 增加hystrix线程超时时间-------->

hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds:60000 shareSecurityContext:true

enter image description here 3)在主@SpringBootApplication类中添加@EnableFeignClients。-------> 这个解决方案对我来说很好用。


为了让读者更清楚地了解他们将要看到的内容,请为附加的图像提供相关描述。 - Sai Sarath C P

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接