翻译自:progressivecoder.com/microservic…
微服务之间的通信是分布式系统的骨干。通常,开发人员尽量避免完全担心复杂性的增加。但是,Spring Cloud Stream和RabbitMQ的组合可以使处理微服务之间的通信相对容易。
Spring Cloud Stream也是Spring Cloud项目组的一部分。它以最低的配置轻松与各种消息代理集成。以下是整体模式的高级视图。
Spring Cloud Stream帮助在两个应用程序或微服务之间交换消息。它可以与Rabbit MQ,Kafka等消息代理无缝协作。
在本文中,我们将使用Spring Boot和Spring Cloud Stream实现微服务之间的通信。以下是我们的高级计划。
- 步骤1–使用Docker创建RabbitMQ服务器
- 步骤2–使用Spring Cloud Stream创建Spring Boot应用程序以侦听消息。我们将此应用程序称为“订户应用程序”。
- 步骤3–在RabbitMQ上发布消息,以便我们的订户应用程序可以收听这些消息。
- 步骤4 –使用Spring Cloud Stream创建另一个Spring Boot应用程序,以将消息发布到Rabbit MQ。我们称此应用程序为Publisher-application。
- 步骤5 –使用发布者应用程序发布消息,订阅者应用程序将使用这些消息。
因此,让我们开始这个过程。
步骤1–使用Docker创建RabbitMQ服务器
为了演示Spring Cloud Stream,我们将使用RabbitMQ作为消息代理。但是,Spring Cloud Stream可以轻松与其他消息代理以及Kafka或Amazon Kinesis一起使用。因此,您可以很好地使用它们。 为了简单起见,我们将使用RabbitMQ。
RabbitMQ可以在您的机器上轻松设置。一种方法是遵循官方的下载指南,并根据指南获取适用于您选择的操作系统的RabbitMQ。
但是,获得RabbitMQ的另一种简单方法是通过Docker。如果您不熟悉Docker,则可以按照我的详细文章了解Docker的基础知识。可通过此链接获得RabbitMQ的官方Docker映像。
使用以下命令可以轻松启动RabbitMQ服务器。
docker run -d --hostname kubernetes.docker.internal --name backend-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-management
该命令从Docker集线器中提取RabbitMQ映像,并在端口5672上启动服务器。我们还在端口15672上公开RabbitMQ管理控制台。
RabbitMQ根据默认为主机名的节点名存储数据。在这种情况下,我们将主机名提供为backend-rabbit。我们还可以使用以下命令检查RabbitMQ服务器的日志。
docker logs backend-rabbit
但是,我们还公开了RabbitMQ服务器的管理控制台。基本上,它提供了图形用户界面来管理RabbitMQ。您可以在http://localhost:15672 上访问它。
如果您看到上面的登录页面,则说明您的机器上的RabbitMQ安装成功。现在,我们可以转到步骤2。
步骤2–创建订户应用程序
在这一步中,我们可以使用Spring Boot和Spring Cloud Stream创建一个应用程序来侦听消息。如果您不了解Spring Boot,请参阅Spring Boot Microservices的详细指南。
以下是我们目前的基本计划。
基本上,我们将首先构建连接到RabbitMQ服务器的订户应用程序。我们将直接通过RabbitMQ Web控制台发布消息。而且我们的Spring Boot应用程序将具有一个称为Sink的东西,它将帮助我们处理传入的消息。在下一节中,我们将介绍什么是接收器。
选择依赖项
我们可以使用start.spring.io 快速引导应用程序。需要注意的重要事项是Spring Cloud Stream和RabbitMQ的以下依赖项。这两个依赖关系共同构成了我们集成的基础。此时,您还可以根据自己的选择选择Kafka或其他选项(代替RabbitMQ)。
您可以生成项目,之后可以查看POM.xml文件(在您选择的IDE中),其中提到了这些依赖项。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
添加消息处理程序
即使在这一点上,我们的Spring Boot应用程序也可以运行。但是,它没有任何功能。
按照我们的计划,我们希望该应用程序侦听RabbitMQ上发布的消息。为此,我们需要添加一个可以处理传入消息的侦听器。
下面是我们将如何做。
@SpringBootApplication
@EnableBinding(Sink.class)
public class SubscriberApplication {
public static void main(String[] args) {
SpringApplication.run(SubscriberApplication.class, args);
}
@StreamListener(Sink.INPUT)
public void handleMessage(Message message){
System.out.println("Received Message is: " + message);
}
public static class Message{
private String message;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
@Override
public String toString() {
return "Message{" +
"message='" + message + '\'' +
'}';
}
}
}
这里要注意的重要事项如下:
我们已经通过使用@EnableBinding注释启用了Sink绑定。此步骤向基础框架发出信号,以创建与消息传递中间件的必要绑定。换句话说,它将创建目标项目,例如队列,主题等。 另外,我们添加了一个处理程序方法。此方法用于接收消息类型为Message的传入消息。这是Spring Cloud Stream最强大的功能之一。框架尝试将传入消息自动转换为Message类型。
这样,我们基本上完成了应用程序所需的最少设置。
步骤3–通过RabbitMQ发布消息
我们已经准备好测试我们的订户应用程序,并在实际中看到它。
您可以使用以下命令简单地启动应用程序:
clean package spring-boot:run
该应用程序将自动尝试连接到位于http://localhost:5672 的RabbitMQ服务器。您应该在应用程序启动日志中看到类似的内容。
2019-06-25 17:35:22.304 INFO 8115 --- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#34688e58:0/SimpleConnection@1a981cf0 [delegate=amqp://guest@127.0.0.1:5672/, localPort= 52000]
2019-06-25 17:35:22.355 INFO 8115 --- [ main] o.s.i.monitor.IntegrationMBeanExporter : Registering MessageChannel input.anonymous.KWrQWmqZSYKPjOBkx4DotA.errors
2019-06-25 17:35:22.432 INFO 8115 --- [ main] o.s.c.stream.binder.BinderErrorChannel : Channel 'application.input.anonymous.KWrQWmqZSYKPjOBkx4DotA.errors' has 1 subscriber(s).
2019-06-25 17:35:22.432 INFO 8115 --- [ main] o.s.c.stream.binder.BinderErrorChannel : Channel 'application.input.anonymous.KWrQWmqZSYKPjOBkx4DotA.errors' has 2 subscriber(s).
2019-06-25 17:35:22.454 INFO 8115 --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.KWrQWmqZSYKPjOBkx4DotA
2019-06-25 17:35:22.464 INFO 8115 --- [ main] c.p.d.s.SubscriberApplication : Started SubscriberApplication in 3.52 seconds (JVM running for 18.305)
为了测试我们的应用程序是否能够收听消息,我们可以通过RabbitMQ管理控制台发布消息。
为此,您可以使用默认的userid/password作为guest/guest登录到http://localhost:15672 的控制台。
此时,您应该看到如下所示的交易所列表。注意列表中的最后一次交换,称为input。这基本上是在我们启动应用程序时自动创建的。
此时,您应该看到如下所示的交易所列表。注意列表中的最后一次交换,称为input。这基本上是在我们启动应用程序时自动创建的。
同样,在输入交换下创建一个队列,我们可以在其中发布消息。
单击“发布”后,您将看到如下消息打印在订阅者应用程序日志中。
2019-06-25 17:35:22.454 INFO 8115 --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.KWrQWmqZSYKPjOBkx4DotA
2019-06-25 17:35:22.464 INFO 8115 --- [ main] c.p.d.s.SubscriberApplication : Started SubscriberApplication in 3.52 seconds (JVM running for 18.305)
Received Message is: Message{message='Hello World'}
这样,我们就完成了应用程序的订户部分。现在,我们可以继续下一步。
步骤4–创建发布者应用程序
在此步骤中,我们将创建一个发布者应用程序,该应用程序的端点是将消息发布到RabbitMQ服务器的端点。
以下是我们的高层计划。
如您所见,现在我们有了发布者应用程序而不是Web控制台。在发布者应用程序中,我们有一个称为Source的东西。认为它与我们先前在订户应用程序中看到的Sink接口相反。我们将在本节中详细介绍Source接口。
选择依赖项
为了快速引导应用程序,我们可以像对订户应用程序一样使用Spring Initializr。这次,我们将包括一个称为Spring Web Starter的额外依赖项。
您可以单击“生成项目”以在本地计算机上获取zip文件。
添加消息发布逻辑
要使用Spring Cloud Stream和RabbitMQ发布消息,我们如下修改发布者应用程序的主类。
@SpringBootApplication
public class PublisherApplication {
public static void main(String[] args) {
SpringApplication.run(PublisherApplication.class, args);
}
}
@RestController
@EnableBinding(Source.class)
class MessagePublisher{
@Autowired
private Source source;
@GetMapping(value = "/api/publish")
public void sendMessage(){
Message message = new Message("Hello World from Publisher");
source.output().send(MessageBuilder.withPayload(message).build());
}
}
class Message{
String message;
public Message(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
这里要注意的重要事项是:
- 我们创建一个MessagePublisher类,并将其注释为@RestController。
- 另外,我们使用@EnableBinding注释控制器类。但是,不是将其绑定到Sink(就像我们在订阅服务器中所做的那样),而是将此类与Source.class绑定。基本上,Source和Sink是Spring Cloud Stream提供的绑定接口。
- 我们自动关联Source类的实例,并在/ api / publish调用中使用它来将Message对象发布到RabbitMQ。
- 我们还定义了Message类来创建新消息。
您需要在application.properties文件中执行另一个重要设置。我们需要如下定义输出绑定。
spring.cloud.stream.bindings.output.destination=input
spring.cloud.stream.default.contentType=application/json
下图可以帮助您理解此属性的含义。
基本上,Source接口公开了一个我们绑定到输入交换机的Output通道。发布者和订阅者必须具有这种关系才能交换消息。
2019-06-26 17:18:50.366 INFO 5138 --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.Z0h4KjScTG2guIvw1KxRxQ
2019-06-26 17:18:50.374 INFO 5138 --- [ main] c.p.d.s.SubscriberApplication : Started SubscriberApplication in 2.939 seconds (JVM running for 11.335)
Received Message is: Message{message='Hello World from Publisher'}
步骤5–使用发布者应用程序发布消息
现在,我们也可以启动发布者应用程序。应用程序启动后,我们需要触发端点http://localhost:8080/api/publish 。
任何内容都不会在浏览器或客户端中显示为响应。但是,如果您访问订阅者应用程序的日志,我们将能够看到打印的消息。
另外,您将能够在输入队列的RabbitMQ控制台中看到绿色的尖峰。这显示了将消息传输到订户应用程序正在侦听的输入队列。
结论
这样,我们就成功地使用Spring Cloud Stream和RabbitMQ开发了一个小型应用程序来发布和订阅消息。