本文已参与「新人创作礼」活动,一起开启掘金创作之路
生产者:发送方确认,失败通知
消费者:手动应答
1.创建一个springboot-maven工程,pom.xml引入amqp与web
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.rabbitmq</groupId>
<artifactId>fisher</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>fisher</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.配置application.properties
spring.application.name=springboot-rabbitmq
spring.rabbitmq.host=192.168.42.111
spring.rabbitmq.port=5672
spring.rabbitmq.username=fisher
spring.rabbitmq.password=123456
spring.rabbitmq.publisher-confirms=true
spring.rabbitmq.virtual-host=fisher
3.启动类
package com.rabbitmq.fisher;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class FisherApplication {
public static void main(String[] args) {
SpringApplication.run(FisherApplication.class, args);
System.out.println("Hello World!!!");
}
}
4.常量类Constant
package com.rabbitmq.fisher;
public class Constant {
public final static String QUEUE_HELLO = "fisher.hello";
public final static String QUEUE_USER = "fisher.user";
public final static String TOPIC_EMAIL = "fisher.info.email";
public final static String TOPIC_USER = "fisher.info.user";
public final static String TOPIC_ERROR = "errorKey";
public final static String FANOUT_A = "fisher.fanout.a";
public final static String EXCHANGE_TOPIC = "fisher.topic.exchange";
public final static String EXCHANGE_FANOUT = "fisher.fanout.exchange";
}
5.定义一个配置类RabbitConfig,在connectionFactory方法中设置开启发送发确认;在rabbitTemplate方法中设置失败通知,发送方确认方法,失败回调方法;在messageContainer方法中设置消费手动应答,绑定fisher.user队列,设置消费确认方法UserReceiver
package com.rabbitmq.fisher.config;
import com.rabbitmq.fisher.Constant;
import com.rabbitmq.fisher.hello.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;
@Configuration
public class RabbitConfig {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private String 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;
//连接工厂
@Bean
public ConnectionFactory connectionFactory(){
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setAddresses(host + ":" + port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
//开启发送方确认
connectionFactory.setPublisherConfirms(publisherConfirms);
return connectionFactory;
}
//rabbitAdmin类封装对rabbitMq的管理操作
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory) {
return new RabbitAdmin(connectionFactory);
}
//使用Template
@Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
//失败通知
template.setMandatory(true);
//发送方确认
template.setConfirmCallback(confirmCallback());
//失败回调
template.setReturnCallback(returnCallback());
return template;
}
//使用Rabbit缺省的交换器(direct交换器)
//申明队列
@Bean
public Queue helloQueue(){
return new Queue(Constant.QUEUE_HELLO);
}
@Bean
public Queue userQueue() {
return new Queue(Constant.QUEUE_USER);
}
//topic exchange
@Bean
public Queue queueEmailMessage() {
return new Queue(Constant.TOPIC_EMAIL);
}
@Bean
public Queue queueUserMessage() {
return new Queue(Constant.TOPIC_USER);
}
//申明topic交换器
@Bean
public TopicExchange exchange() {
return new TopicExchange(Constant.EXCHANGE_TOPIC);
}
//绑定
@Bean
public Binding bindingEmailMessage() {
return BindingBuilder.bind(queueEmailMessage()).to(exchange()).with("fisher.*.email");
}
@Bean
public Binding bindingUserMessage() {
return BindingBuilder.bind(queueUserMessage()).to(exchange()).with("fisher.*.user");
}
//fanout exchange
//申明队列
@Bean
public Queue AMessage() {
return new Queue(Constant.FANOUT_A);
}
//申明fanout交换器
@Bean
public FanoutExchange fanoutExchange() {
return new FanoutExchange(Constant.EXCHANGE_FANOUT);
}
//绑定
@Bean
public Binding bindingAMessage() {
return BindingBuilder.bind(AMessage()).to(fanoutExchange());
}
//生产者发送确认
@Bean
public RabbitTemplate.ConfirmCallback confirmCallback(){
return new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
System.out.println("发送者确认发送给mq成功!");
}else {
System.out.println("发送者确认发送给mq失败,考虑重新发送:" + cause);
}
}
};
}
//失败通知回调
@Bean
public RabbitTemplate.ReturnCallback returnCallback(){
return new RabbitTemplate.ReturnCallback(){
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routeKey) {
System.out.println("无法路由的消息,需要另行处理。");
System.out.println("Return replyText:" + replyText);
System.out.println("Return exchange:" + exchange);
System.out.println("Return routeKey:" + routeKey);
String msg = new String(message.getBody());
System.out.println("Return message:" + msg);
}
};
}
//消费者确认
@Bean
public SimpleMessageListenerContainer messageContainer() {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(connectionFactory());
//绑定fisher.user队列
container.setQueues(userQueue());
//手动提交
container.setAcknowledgeMode(AcknowledgeMode.MANUAL);
//消费确认方法,设置监听
container.setMessageListener(userReceiver);
return container;
}
}
6.创建direct交换器生产者DefaultSender,分别向自动应答队列fisher.hello与手动应答队列fisher.user发送消息
package com.rabbitmq.fisher.hello;
import com.rabbitmq.fisher.Constant;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DefaultSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
String sendMsg = msg + "======" + System.currentTimeMillis();
System.out.println("SendMsg:" + sendMsg);
//普通消息处理,自动ack
rabbitTemplate.convertAndSend(Constant.QUEUE_HELLO, sendMsg);
//消费者处理时,有手动应答
rabbitTemplate.convertAndSend(Constant.QUEUE_USER, sendMsg);
}
}
7.创建自动应答消费者HelloReceiver,添加@RabbitListener(queues = "fisher.hello")监听队列fisher.hello
package com.rabbitmq.fisher.hello;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "fisher.hello")
public class HelloReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("HelloReceiver:" + hello);
}
}
8.创建手动应答消费者UserReceiver,实现接口ChannelAwareMessageListener,重写onMessage方法;因为在RabbitConfig中,通过container.setMessageListener(userReceiver)设置了监听,所以此处不需要加@RabbitListener(queues = "fisher.user")
package com.rabbitmq.fisher.hello;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.listener.api.ChannelAwareMessageListener;
import org.springframework.stereotype.Component;
/**
* 在RabbitConfig中设置了监听,所以此处不需要加@RabbitListener(queues = "fisher.user")
**/
@Component
public class UserReceiver implements ChannelAwareMessageListener {
@Override
public void onMessage(Message message, Channel channel) throws Exception {
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) {
System.out.println(e.getMessage());
channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
System.out.println("UserReceiver>>>消息已拒绝,要求MQ重新发送");
}
}
}
9.创建fanout交换器生产者FanoutSender,路由键可以不填
package com.rabbitmq.fisher.fanout;
import com.rabbitmq.fisher.Constant;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FanoutSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(String msg) {
String sendMsg = msg + "==============" + System.currentTimeMillis();
System.out.println("FanoutSender:" + sendMsg);
rabbitTemplate.convertAndSend(Constant.EXCHANGE_FANOUT, "", sendMsg);
}
}
10.创建fanout消费者FanoutReceiver,监听队列fisher.fanout.a
package com.rabbitmq.fisher.fanout;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "fisher.fanout.a")
public class FanoutReceiver {
@RabbitHandler
public void process(String hello) {
System.out.println("FanoutReceiver:" + hello);
}
}
11.创建topic交换器生产者TopicSender,分别向队列fisher.info.email与队列fisher.info.user中发送消息,因为没有定义队列errorKey,所以rabbitTemplate.convertAndSend(Constant.EXCHANGE_TOPIC, Constant.TOPIC_ERROR, error)会因为无法路由,产生失败通知
package com.rabbitmq.fisher.topic;
import com.rabbitmq.fisher.Constant;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TopicSender {
@Autowired
private RabbitTemplate rabbitTemplate;
public void send(){
String email = "I am email msg############";
System.out.println("TopicSender email:" + email);
rabbitTemplate.convertAndSend(Constant.EXCHANGE_TOPIC, Constant.TOPIC_EMAIL, email);
String user = "I am user msg############";
System.out.println("TopicSender user:" + user);
rabbitTemplate.convertAndSend(Constant.EXCHANGE_TOPIC, Constant.TOPIC_USER, user);
String error = "I am error msg############";
System.out.println("TopicSender error:" + error);
rabbitTemplate.convertAndSend(Constant.EXCHANGE_TOPIC, Constant.TOPIC_ERROR, error);
}
}
12.创建topic消费者TopicEmailMessageReceiver,监听队列fisher.info.email
package com.rabbitmq.fisher.topic;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "fisher.info.email")
public class TopicEmailMessageReceiver {
@RabbitHandler
public void process(String email) {
System.out.println("TopicEmailMessageReceiver:" + email);
}
}
13.创建topic消费者TopicUserMessageReceiver,监听队列fisher.info.user
package com.rabbitmq.fisher.topic;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
@Component
@RabbitListener(queues = "fisher.info.user")
public class TopicUserMessageReceiver {
@RabbitHandler
public void process(String user) {
System.out.println("TopicUserMessageReceiver:" + user);
}
}
14.创建一个测试类RabbitTest
package com.rabbitmq.fisher.controller;
import com.rabbitmq.fisher.fanout.FanoutSender;
import com.rabbitmq.fisher.hello.DefaultSender;
import com.rabbitmq.fisher.topic.TopicSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/rabbit")
public class RabbitTest {
@Autowired
private DefaultSender defaultSender;
@Autowired
private FanoutSender fanoutSender;
@Autowired
private TopicSender topicSender;
//普通类型测试
@RequestMapping("/hello")
public void hello() {
defaultSender.send("hellomsg!");
}
//fanout测试
@RequestMapping("/fanout")
public void fanout() {
fanoutSender.send("hellomsg!");
}
//topic测试
@RequestMapping("/topic")
public void topic() {
topicSender.send();
}
}
15.启动项目,请求http://localhost:8080/rabbit/hello,查看打印
SendMsg:hellomsg!======1589467774678
HelloReceiver:hellomsg!======1589467774678
UserReceiver>>>接收到的消息:hellomsg!======1589467774678
UserReceiver>>>消息已消费
发送者确认发送给mq成功!
发送者确认发送给mq成功!
16.请求http://localhost:8080/rabbit/fanout,查看打印
FanoutSender:hellomsg!==============1589468091019
FanoutReceiver:hellomsg!==============1589468091019
发送者确认发送给mq成功!
17.请求http://localhost:8080/rabbit/topic,查看打印
TopicSender email:I am email msg############
TopicSender user:I am user msg############
TopicSender error:I am error msg############
TopicEmailMessageReceiver:I am email msg############
TopicUserMessageReceiver:I am user msg############
无法路由的消息,需要另行处理。
Return replyText:NO_ROUTE
Return exchange:fisher.topic.exchange
Return routeKey:errorKey
Return message:I am error msg############
发送者确认发送给mq成功!
发送者确认发送给mq成功!
发送者确认发送给mq成功!