1、RabbitMQ与Spring Cloud Stream整合实战。SpringCloud Stream整体结构核心概念图,如下所示:
图示解释:Outputs输出,即消息的发送端。Inputs输入,即消息的接收端。Application Core即核心的应用。Binder是协调者的角色。Middleware是消息中间件。
2、SpringCloud Stream整体结构核心概念图,如下所示:
图示解释:SpringCloud Stream在RabbitMQ在生产者发送消息之前、消费者接收监听之后都套了一层插件。插件可以接收各种各样的不同的消息,也可以支持消息中间件的替换。
SpringCloud Stream插件的关键点,Barista接口,Barista接口是定义来作为后面类的参数,这一接口定义来通道类型和通道名称,通道名称是作为配置用,通道类型则决定了app会使用这一通道进行发送消息还是从中接收消息。
3、使用Spring Cloud Stream非常简单,只需要使用好这3个注解即可,在实现高性能消息的生产和消费的场景非常适合,但是使用SpringCloudStram框架有一个非常大的问题就是不能实现可靠性的投递,也就是没法保证消息的100%可靠性,会存在少量消息丢失的问题。这个原因是因为SpringCloudStream框架为了和Kafka兼顾所以在实际工作中使用它的目的就是针对高性能的消息通信的,这点就是在当前版本SpringCloudStream的定位。
@Output,输出注解,用于定义发送消息接口。
@Input,输入注解,用于定义消息的消费者接口。
@StreamListener,用于定义监听方法的注解。
4、创建maven项目rabbitmq-springcloudstream-producer。修改pom.xml配置文件,如下所示:
1"http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 4.0.0 6com.bie 7rabbitmq-springcloudstream-producer 80.0.1-SNAPSHOT 9 1011 16 17org.springframework.boot 12spring-boot-starter-parent 131.5.8.RELEASE 1415 18 22 23UTF-8 19UTF-8 201.8 2124 54 5525 28org.springframework.boot 26spring-boot-starter-web 2729 32 33org.springframework.boot 30spring-boot-starter 3134 37org.springframework.boot 35spring-boot-autoconfigure 3638 42 43org.springframework.boot 39spring-boot-starter-test 40test 4144 48 49org.springframework.cloud 45spring-cloud-starter-stream-rabbit 461.3.4.RELEASE 4750 53org.springframework.boot 51spring-boot-starter-actuator 5256 63 6457 6258 61org.springframework.boot 59spring-boot-maven-plugin 60
修改rabbitmq-springcloudstream-producer的application.properties配置文件。如下所示:
1 # 端口号 2 server.port=8001 3 # 请问访问路径 4 server.servlet.context-path=/producer 5 6 # 应用的名称 7 spring.application.name=producer 8 # 将交换机和队列绑定到了通道output_channel上面 9 # 交换机名称 10 spring.cloud.stream.bindings.output_channel.destination=exchange-3 11 # 队列名称 12 spring.cloud.stream.bindings.output_channel.group=queue-3 13 # 对集群环境进行配置 14 spring.cloud.stream.bindings.output_channel.binder=rabbit_cluster 15 16 # rabbit_cluster对应上面的spring.cloud.stream.bindings.output_channel.binder的值。名称可以自定义 17 spring.cloud.stream.binders.rabbit_cluster.type=rabbit 18 # 使用的环境是rabbit 19 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.110.133:5672 20 # 账号 21 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest 22 # 密码 23 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest 24 # 虚拟主机 25 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/
创建启动类,如下所示:
1 package com.bie; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 6 /** 7 * 8 * @author biehl 9 * 10 */ 11 @SpringBootApplication 12 public class SpringCloudStreamRabbitMQProducerApplication { 13 14 public static void main(String[] args) { 15 SpringApplication.run(SpringCloudStreamRabbitMQProducerApplication.class, args); 16 } 17 }
创建Barista接口,用于创建输出通道,将消息和输出通道进行绑定,如下所示:
1 package com.bie.stream; 2 3 import org.springframework.cloud.stream.annotation.Output; 4 import org.springframework.messaging.MessageChannel; 5 6 /** 7 * 这里的Barista接口是定义来作为后面类的参数, 8 * 9 * 这一接口定义来通道类型和通道名称。 10 * 11 * 通道名称是作为配置用, 12 * 13 * 通道类型则决定了app会使用这一通道进行发送消息还是从中接收消息。 14 */ 15 public interface Barista { 16 17 // String INPUT_CHANNEL = "input_channel"; // 输入通道 18 String OUTPUT_CHANNEL = "output_channel"; // 输出通道 19 20 // 注解@Input声明了它是一个输入类型的通道, 21 // 名字是Barista.INPUT_CHANNEL,也就是position3的input_channel。 22 // 这一名字与上述配置app2的配置文件中position1应该一致, 23 // 表明注入了一个名字叫做input_channel的通道,它的类型是input, 24 // 订阅的主题是position2处声明的mydest这个主题。 25 // @Input(Barista.INPUT_CHANNEL) 26 // SubscribableChannel loginput(); 27 28 // 注解@Output声明了它是一个输出类型的通道, 29 // 名字是output_channel。这一名字与app1中通道名一致, 30 // 表明注入了一个名字为output_channel的通道,类型是output,发布的主题名为mydest。 31 @Output(Barista.OUTPUT_CHANNEL) // 输出通道 32 MessageChannel logoutput(); 33 34 // String INPUT_BASE = "queue-1"; 35 // String OUTPUT_BASE = "queue-1"; 36 // @Input(Barista.INPUT_BASE) 37 // SubscribableChannel input1(); 38 // MessageChannel output1(); 39 40 }
创建生产者类,如下所示:
1 package com.bie.stream; 2 3 import java.util.Map; 4 5 import org.springframework.beans.factory.annotation.Autowired; 6 import org.springframework.cloud.stream.annotation.EnableBinding; 7 import org.springframework.messaging.Message; 8 import org.springframework.messaging.MessageHeaders; 9 import org.springframework.messaging.support.MessageBuilder; 10 import org.springframework.stereotype.Service; 11 12 /** 13 * 14 * @author biehl 15 * 16 */ 17 @EnableBinding(Barista.class) // 启动该绑定 18 @Service // 注入到Spring容器中 19 public class RabbitmqProducer { 20 21 @Autowired // 将barista注入到spirng容器中 22 private Barista barista; 23 24 // 发送消息 25 public String sendMessage(Object message, Mapproperties) throws Exception { 26 try { 27 // 设置消息头 28 MessageHeaders mhs = new MessageHeaders(properties); 29 // 创建消息,使用消息和消息头创建消息。 30 Message msg = MessageBuilder.createMessage(message, mhs); 31 // 调用barista进行消息的发送。 32 boolean sendStatus = barista.logoutput().send(msg); 33 // 打印查看消息发送的情况 34 System.out.println("========================sending========================"); 35 System.out.println("发送数据:" + message + ",sendStatus: " + sendStatus); 36 } catch (Exception e) { 37 System.out.println("========================error========================"); 38 e.printStackTrace(); 39 throw new RuntimeException(e.getMessage()); 40 } 41 return null; 42 } 43 44 }
5、创建消费者rabbitmq-springcloudstream-consumer。修改配置文件,如下所示:
1"http://maven.apache.org/POM/4.0.0" 2 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 4 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 4.0.0 6com.bie 7rabbitmq-springcloudstream-consumer 80.0.1-SNAPSHOT 9 1011 16 17org.springframework.boot 12spring-boot-starter-parent 131.5.8.RELEASE 1415 18 22 23UTF-8 19UTF-8 201.8 2124 49 5025 28org.springframework.boot 26spring-boot-starter-web 2729 32org.springframework.boot 30spring-boot-starter 3133 37 38org.springframework.boot 34spring-boot-starter-test 35test 3639 43 44org.springframework.cloud 40spring-cloud-starter-stream-rabbit 411.3.4.RELEASE 4245 48org.springframework.boot 46spring-boot-starter-actuator 4751 58 5952 5753 56org.springframework.boot 54spring-boot-maven-plugin 55
修改rabbitmq-springcloudstream-consumer的application.properties配置文件。如下所示:
1 # 端口号 2 server.port=8002 3 # 访问路径 4 server.context-path=/consumer 5 6 # 应用的名称 7 spring.application.name=consumer 8 # 交换机名称 9 spring.cloud.stream.bindings.input_channel.destination=exchange-3 10 # 队列名称 11 spring.cloud.stream.bindings.input_channel.group=queue-3 12 # 对集群环境进行配置 13 spring.cloud.stream.bindings.input_channel.binder=rabbit_cluster 14 # 设置默认监听数 15 spring.cloud.stream.bindings.input_channel.consumer.concurrency=1 16 # 是否支持重新放回队列 17 spring.cloud.stream.rabbit.bindings.input_channel.consumer.requeue-rejected=false 18 # 接收模式是手工接收 19 spring.cloud.stream.rabbit.bindings.input_channel.consumer.acknowledge-mode=MANUAL 20 # 服务断开,3秒钟重连 21 spring.cloud.stream.rabbit.bindings.input_channel.consumer.recovery-interval=3000 22 # 是否启用持久化订阅 23 spring.cloud.stream.rabbit.bindings.input_channel.consumer.durable-subscription=true 24 # 最大的监听数 25 spring.cloud.stream.rabbit.bindings.input_channel.consumer.max-concurrency=5 26 27 # rabbit_cluster对应上面的spring.cloud.stream.bindings.output_channel.binder的值。名称可以自定义 28 # 使用的环境是rabbit 29 spring.cloud.stream.binders.rabbit_cluster.type=rabbit 30 # 访问地址和端口号 31 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.addresses=192.168.110.133:5672 32 # 账号 33 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.username=guest 34 # 密码 35 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.password=guest 36 # 虚拟主机 37 spring.cloud.stream.binders.rabbit_cluster.environment.spring.rabbitmq.virtual-host=/
创建启动类,如下所示:
1 package com.bie; 2 3 import org.springframework.boot.SpringApplication; 4 import org.springframework.boot.autoconfigure.SpringBootApplication; 5 6 /** 7 * 8 * @author biehl 9 * 10 */ 11 @SpringBootApplication 12 public class SpringCloudStreamRabbitMQConsumerApplication { 13 14 public static void main(String[] args) { 15 SpringApplication.run(SpringCloudStreamRabbitMQConsumerApplication.class, args); 16 } 17 18 }
创建Barista接口,用于创建输入通道,将消息和输入通道进行绑定,如下所示:
1 package com.bie.stream; 2 3 import org.springframework.cloud.stream.annotation.Input; 4 import org.springframework.messaging.SubscribableChannel; 5 6 /** 7 * 这里的Barista接口是定义来作为后面类的参数, 8 * 9 * 这一接口定义来通道类型和通道名称。 10 * 11 * 通道名称是作为配置用, 12 * 13 * 通道类型则决定了app会使用这一通道进行发送消息还是从中接收消息。 14 * 15 * @author biehl 16 * 17 */ 18 19 public interface Barista { 20 21 // 输入管道,用于接收消息 22 String INPUT_CHANNEL = "input_channel"; 23 24 // 注解@Input声明了它是一个输入类型的通道, 25 // 名字是Barista.INPUT_CHANNEL, 26 // 也就是position3的input_channel。 27 // 这一名字与上述配置app2的配置文件中position1应该一致, 28 // 表明注入了一个名字叫做input_channel的通道,它的类型是input, 29 // 订阅的主题是position2处声明的mydest这个主题。 30 @Input(Barista.INPUT_CHANNEL) 31 SubscribableChannel loginput(); 32 33 }
创建消费者,如下所示:
1 package com.bie.stream; 2 3 import org.springframework.amqp.support.AmqpHeaders; 4 import org.springframework.cloud.stream.annotation.EnableBinding; 5 import org.springframework.cloud.stream.annotation.StreamListener; 6 import org.springframework.messaging.Message; 7 import org.springframework.stereotype.Service; 8 9 import com.rabbitmq.client.Channel; 10 11 @EnableBinding(Barista.class) // 启动该绑定 12 @Service // 注入到Spring容器中 13 public class RabbitmqConsumer { 14 15 /** 16 * @StreamListener接收RabbitMQ的消息。指定了输入管道。 17 * 18 * 19 * 接收消息 20 * @param message 21 * @throws Exception 22 */ 23 @StreamListener(Barista.INPUT_CHANNEL) 24 public void consumer(Message message) throws Exception { 25 // 获取到具体的channel 26 Channel channel = (com.rabbitmq.client.Channel) message.getHeaders().get(AmqpHeaders.CHANNEL); 27 // 获取到deliveryTag 28 Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG); 29 // 消费消息 30 System.out.println("Input Stream 1 接受数据:" + message); 31 System.out.println("消费完毕------------"); 32 // 手动ack确认机制,false代表了1条一条进行消息签收 33 channel.basicAck(deliveryTag, false); 34 } 35 36 }
启动消费者进行消息的监听,在生产者项目编写测试类进行代码测试,发送消息,看生产者是否可以接收到消息并进行消费处理。
1 package com.bie; 2 3 import java.util.Date; 4 import java.util.HashMap; 5 import java.util.Map; 6 7 import org.apache.http.client.utils.DateUtils; 8 import org.junit.Test; 9 import org.junit.runner.RunWith; 10 import org.springframework.beans.factory.annotation.Autowired; 11 import org.springframework.boot.test.context.SpringBootTest; 12 import org.springframework.test.context.junit4.SpringRunner; 13 14 import com.bie.stream.RabbitmqProducer; 15 16 @RunWith(SpringRunner.class) 17 @SpringBootTest 18 public class ApplicationTests { 19 20 @Autowired 21 private RabbitmqProducer rabbitmqProducer; 22 23 @Test 24 public void sendMessageTest1() throws InterruptedException { 25 for (int i = 0; i < 1; i++) { 26 try { 27 Mapproperties = new HashMap (); 28 properties.put("serial_number", "12345"); 29 properties.put("bank_number", "abc"); 30 properties.put("plat_send_time", DateUtils.formatDate(new Date(), "yyyy-MM-dd HH:mm:ss.SSS")); 31 rabbitmqProducer.sendMessage("Hello, I am amqp sender num :" + i, properties); 32 } catch (Exception e) { 33 System.out.println("================================error================================"); 34 e.printStackTrace(); 35 } 36 } 37 Thread.sleep(50000); 38 } 39 40 }
运行效果如下所示:
作者:别先生
博客园:https://www.cnblogs.com/biehongli/
如果您想及时得到个人撰写文章以及著作的消息推送,可以扫描上方二维码,关注个人公众号哦。