RabbitMQ与SpirngBoot2.1.2的集成
准备工作:idea/eclipse(我使用Idea开发工具),JDK1.8,Maven
创建MAVEN工程
1、创建生产者的一个简单的Maven工程,创建方式这里就不描述了,创建后的目录结构如下
在这里我只使用了其中一个工程,把消费者和生产者写到一起,节约自己的时间,后期有时间把消费者和生产者拆开。
pom文件添加依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>rabbitmq-springboot</artifactId>
<groupId>kevin</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>mq-product</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
创建yml配置文件
spring:
application:
name: rabbitmq-product
rabbitmq:
host: 192.168.244.101
username: kevin
password: 123456
port: 5672
virtual-host: myhost
publisher-confirms: true
定义常量
package com.kevin.consts;
/**
* 描述:常量定义<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 11:19<br/>
* 版本:1.0
*/
public class RabbitMQConst {
//默认direct hello队列名称
public final static String QUEUE_HELLO = "q.hello";
//默认direct user队列名称
public final static String QUEUE_USER = "q.user";
//topic eamil队列名称
public final static String QUEUE_TOPIC_EMAIL = "q.info.email";
//topic user队列名称
public final static String QUEUE_TOPIC_USER = "q.info.user";
//fanout 队列名称
public final static String QUEUE_FANOUT = "q.fanout";
//topic email 路由键
public final static String RK_EMAIL = "q.info.email";
//topic user路由键
public final static String RK_USER = "q.info.user";
//topic交换器
public final static String EXCHANGE_TOPIC = "exchange.topic";
//fanout交换器
public final static String EXCHANGE_FANOUT = "exchange.fanout";
}
创建Configuration配置类
在这里主要申明bean,加载组件什么的。
package com.kevin.config;
import com.kevin.consts.RabbitMQConst;
import com.kevin.sender.UserReceiver;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.lang.Nullable;
/**
* 描述:配置类<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 11:01<br/>
* 版本:1.0
*/
@Configuration
public class RabbitMQConfig {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private int port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
@Value("${spring.rabbitmq.publisher-confirms}")
private boolean publisherConfirms;
@Autowired
private UserReceiver userReceiver;
/**
* 连接工厂
* @return
*/
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
connectionFactory.setPublisherConfirms(publisherConfirms);
return connectionFactory;
}
/**
* rabbitAdmin分装了对RabbitMQ的管理操作
* @param connectionFactory
* @return
*/
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
/**
* 使用template
* @param connectionFactory
* @return
*/
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory){
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
//设置失败通知
rabbitTemplate.setMandatory(true);
//发送方确认
rabbitTemplate.setConfirmCallback(confirmCallback());
//失败回调
rabbitTemplate.setReturnCallback(returnCallback());
return rabbitTemplate;
}
///消费者确认//
@Bean
public SimpleMessageListenerContainer messageListenerContainer(){
SimpleMessageListenerContainer simpleMessageListenerContainer
= new SimpleMessageListenerContainer(connectionFactory());
simpleMessageListenerContainer.setQueues(userQueue());
simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
simpleMessageListenerContainer.setMessageListener(userReceiver);
return simpleMessageListenerContainer;
}
///发送方确认//
@Bean
public RabbitTemplate.ConfirmCallback confirmCallback(){
return (@Nullable CorrelationData correlationData, boolean ack, @Nullable String cause) ->{
if(ack){
System.out.println("发送者确认发送给了MQ成功。");
}else{
System.out.println("发送者发送给MQ失败,考虑重发:" + cause);
}
};
}
///失败通知//
@Bean
public RabbitTemplate.ReturnCallback returnCallback(){
return (Message message, int replyCode, String replyText, String exchange, String routingKey) -> {
System.out.println("无法路由消息,需要考虑另外处理。");
System.out.println("Returned replyText:" + replyText);
System.out.println("Returned exchange:" + exchange);
System.out.println("Returned routingKey:" + routingKey);
String messageJson = new String(message.getBody());
System.out.println("Returned message:" + messageJson);
};
}
///使用RabbitMQ系统缺省的交换器(direct交换器)//
@Bean
public Queue helloQueue(){
return new Queue(RabbitMQConst.QUEUE_HELLO);
}
@Bean
public Queue userQueue(){
return new Queue(RabbitMQConst.QUEUE_USER);
}
///Topic交换器//
@Bean
public Queue topicEmailQueue(){
return new Queue(RabbitMQConst.QUEUE_TOPIC_EMAIL);
}
@Bean
public Queue topicUserQueue(){
return new Queue(RabbitMQConst.QUEUE_TOPIC_USER);
}
/**
* 申明交换器
* @return
*/
@Bean
public TopicExchange topicExchange(){
return new TopicExchange(RabbitMQConst.EXCHANGE_TOPIC);
}
/**
* 绑定路由键
* @return
*/
@Bean
public Binding bindingTopicEmailExchangeMessage(){
return BindingBuilder.bind(topicEmailQueue())
.to(topicExchange())
.with("q.*.email");
}
/**
* 绑定路由键
* @return
*/
@Bean
public Binding bindingTopicUserExchangeMessage(){
return BindingBuilder.bind(topicUserQueue())
.to(topicExchange())
.with("q.*.user");
}
///Fanout交换器//
/**
* 申明交换器
* @return
*/
@Bean
public FanoutExchange fanoutExchange(){
return new FanoutExchange(RabbitMQConst.EXCHANGE_FANOUT);
}
@Bean
public Queue fanoutQueue(){
return new Queue(RabbitMQConst.QUEUE_FANOUT);
}
@Bean
public Binding bindingFanoutExchange(){
return BindingBuilder.bind(fanoutQueue())
.to(fanoutExchange());
}
}
默认direct的生产者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 描述:默认direct的生产者组件定义<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 16:40<br/>
* 版本:1.0
*/
@Component
public class DefaultSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String message){
String sendMessage = message + "---" + System.currentTimeMillis();
System.out.println("Sender:" + sendMessage);
this.rabbitTemplate.convertAndSend(RabbitMQConst.QUEUE_HELLO,sendMessage);
}
}
direct通过RabbitListener监听消费者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 描述:通过监听队列来处理消息<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 16:52<br/>
* 版本:1.0
*/
@Component
@RabbitListener(queues = RabbitMQConst.QUEUE_HELLO)
public class HelloReceiver {
@RabbitHandler
public void process(String hello){
System.out.println("HelloReceiver>>>>>>>>>" + hello);
}
}
direct实现ChannelAwareMessageListener接口手动应答模式
package com.kevin.sender;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
/**
* 描述:实现ChannelAwareMessageListener 接口,手动应答<br/>
* 如果配置里配置了手动应答,那么消费者必须实现ChannelAwareMessageListener 来处理<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 16:46<br/>
* 版本:1.0
*/
@Component
public class UserReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
try {
String msg = new String(message.getBody());
System.out.println("UserReceiver>>>>>>>接收到消息:" + msg);
try {
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
System.out.println("UserReceiver>>>>>>>消息已消费");
}catch (Exception e){
channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
System.out.println("UserReceiver>>>>>>>拒绝消息,要求MQ重新派发");
throw e;
}
}catch (Exception ex){
System.out.println(ex.getMessage());
}
}
}
fanout生产者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 描述:Fanout生产者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 17:00<br/>
* 版本:1.0
*/
@Component
public class FanoutSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String message){
String sendMessage = message + "---" + System.currentTimeMillis();
System.out.println("Sender:" + sendMessage);
this.rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_FANOUT,"",sendMessage);
}
}
fanout消费者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 描述:Fanout消费者<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 17:02<br/>
* 版本:1.0
*/
@Component
@RabbitListener(queues = RabbitMQConst.QUEUE_FANOUT)
public class FanoutReceiver {
@RabbitHandler
public void process(String message){
System.out.println("FanoutReceiver >>>>>>>>>" + message);
}
}
topic生产者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
/**
* 描述:Topic生产者<br/>
* 这里模拟发送了3条消息<br/>
* 分别发送到eamil队列,user队列和一个error队列<br/>
* 其中消费者eamil,user正常接收消息,error无法路由,回到发送失败通知回调里<br/>
* 创建人: Trouble Li <br/>
* 创建时间: 2019-10-8 17:05<br/>
* 版本:1.0
*/
@Component
public class TopicSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(){
String message1 = "I am email message....";
System.out.println("TopicSender send the 1st>>>>>>>>>>>" + message1);
this.rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_TOPIC,RabbitMQConst.RK_EMAIL,message1);
String message2 = "I am user message....";
System.out.println("TopicSender send the 2nd>>>>>>>>>>>" + message2);
this.rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_TOPIC,RabbitMQConst.RK_USER,message2);
String message3 = "I am error message....";
System.out.println("TopicSender send the 3rd>>>>>>>>>>>" + message3);
this.rabbitTemplate.convertAndSend(RabbitMQConst.EXCHANGE_TOPIC,"errorKey",message3);
}
}
topic email消费者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 描述:Topic eamil消费者,只消费自己监听的队列<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 17:09<br/>
* 版本:1.0
*/
@Component
@RabbitListener(queues = RabbitMQConst.QUEUE_TOPIC_EMAIL)
public class TopicEmailReceiver {
@RabbitHandler
public void process(String message){
System.out.println("TopicEmailReceiver <<<<<<<<" + message);
}
}
topic user消费者
package com.kevin.sender;
import com.kevin.consts.RabbitMQConst;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 描述:Topic user消费者,只消费自己监听的队列<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 17:09<br/>
* 版本:1.0
*/
@Component
@RabbitListener(queues = RabbitMQConst.QUEUE_TOPIC_USER)
public class TopicUserReceiver {
@RabbitHandler
public void process(String message){
System.out.println("TopicUserReceiver <<<<<<<<" + message);
}
}
定义一个简单controller做测试
package com.kevin.controller;
import com.kevin.sender.DefaultSender;
import com.kevin.sender.FanoutSender;
import com.kevin.sender.TopicSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 描述:测试的controller,测试rabbitmq与springboot集成<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 16:25<br/>
* 版本:1.0
*/
@RestController
@RequestMapping("/rabbit")
public class RabbitController {
@Autowired
private DefaultSender defaultSender;
@Autowired
private TopicSender topicSender;
@Autowired
private FanoutSender fanoutSender;
@RequestMapping("/hello")
public void hello(){
defaultSender.send("hello");
}
@RequestMapping("/topic")
public void topic(){
topicSender.send();
}
@RequestMapping("/fanout")
public void fanout(){
fanoutSender.send("fanout hello");
}
}
最后定义一个springboot入口
package com.kevin;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* 描述:sprigboot启动入口<br/>
* 创建人: Kevin Lea <br/>
* 创建时间: 2019-10-8 10:59<br/>
* 版本:1.0
*/
@SpringBootApplication
public class RabbitMQProductApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitMQProductApplication.class,args);
}
}
到这里RabbitMQ与SpringBoot集成就集成完成了。
我们简单的看下topic的测试结果:
在浏览器中输入http://localhost:8080/rabbit/topic
后台日志会输出对应结果
TopicSender send the 1st>>>>>>>>>>>I am email message....
TopicSender send the 2nd>>>>>>>>>>>I am user message....
TopicEmailReceiver <<<<<<<<I am email message....
TopicSender send the 3rd>>>>>>>>>>>I am user message....
发送者确认发送给了MQ成功。
TopicUserReceiver <<<<<<<<I am user message....
发送者确认发送给了MQ成功。
无法路由消息,需要考虑另外处理。
Returned replyText:NO_ROUTE
Returned exchange:exchange.topic
Returned routingKey:errorKey
Returned message:I am user message....
发送者确认发送给了MQ成功。
从结果上看email和user都能够正常的消费,但是error无法路由,进入了失败者通知