DDD:使用Spring Cloud Stream和RabbitMQ的微服务通信

673 阅读9分钟

翻译自:progressivecoder.com/microservic…

微服务之间的通信是分布式系统的骨干。通常,开发人员尽量避免完全担心复杂性的增加。但是,Spring Cloud Stream和RabbitMQ的组合可以使处理微服务之间的通信相对容易。

Spring Cloud Stream也是Spring Cloud项目组的一部分。它以最低的配置轻松与各种消息代理集成。以下是整体模式的高级视图。

image.png

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 上访问它。

image.png image.png 如果您看到上面的登录页面,则说明您的机器上的RabbitMQ安装成功。现在,我们可以转到步骤2。

步骤2–创建订户应用程序

在这一步中,我们可以使用Spring BootSpring Cloud Stream创建一个应用程序来侦听消息。如果您不了解Spring Boot,请参阅Spring Boot Microservices的详细指南。

以下是我们目前的基本计划。

image.png

基本上,我们将首先构建连接到RabbitMQ服务器的订户应用程序。我们将直接通过RabbitMQ Web控制台发布消息。而且我们的Spring Boot应用程序将具有一个称为Sink的东西,它将帮助我们处理传入的消息。在下一节中,我们将介绍什么是接收器

选择依赖项

我们可以使用start.spring.io 快速引导应用程序。需要注意的重要事项是Spring Cloud Stream和RabbitMQ的以下依赖项。这两个依赖关系共同构成了我们集成的基础。此时,您还可以根据自己的选择选择Kafka或其他选项(代替RabbitMQ)。

image.png

您可以生成项目,之后可以查看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。这基本上是在我们启动应用程序时自动创建的。

image.png

同样,在输入交换下创建一个队列,我们可以在其中发布消息。

image.png

单击“发布”后,您将看到如下消息打印在订阅者应用程序日志中。

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服务器的端点。

以下是我们的高层计划。

image.png

如您所见,现在我们有了发布者应用程序而不是Web控制台。在发布者应用程序中,我们有一个称为Source的东西。认为它与我们先前在订户应用程序中看到的Sink接口相反。我们将在本节中详细介绍Source接口。

选择依赖项

为了快速引导应用程序,我们可以像对订户应用程序一样使用Spring Initializr。这次,我们将包括一个称为Spring Web Starter的额外依赖项。

image.png

您可以单击“生成项目”以在本地计算机上获取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

下图可以帮助您理解此属性的含义。

image.png

基本上,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控制台中看到绿色的尖峰。这显示了将消息传输到订户应用程序正在侦听的输入队列。

image.png

结论

这样,我们就成功地使用Spring Cloud Stream和RabbitMQ开发了一个小型应用程序来发布和订阅消息。