首先搭建一个boot项目,pom引入rabbitMq
<!--rabbitMq-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
编写application-dev.yml文件
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
publisher-returns: true
connection-timeout: 20000
listener:
simple:
acknowledge-mode: manual
concurrency: 1
max-concurrency: 1
retry:
enabled: true
1 简单队列(1对1)
创建SimpleQueueConfig 简单队列配置类
package com.example.rabbitConfig;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SimpleQueueConfig {
/**
* 定义简单队列名.
*/
private String simpleQueue = "queue_simple";
@Bean
public Queue simpleQueue() {
return new Queue(simpleQueue);
}
}
编写生产者
package com.example.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class SimpleProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
for (int i = 0; i < 5; i++) {
String message = "简单消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend( "queue_simple", message);
}
}
}
编写消费者
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class SimpleConsumers {
@RabbitListener(queues = "queue_simple")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息:"+new String(message.getBody()));
}
}
编写访问类
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {
@Autowired
private SimpleProducer simpleProducer;
@RequestMapping(value = "simpleQueueTest")
@ResponseBody
public String simpleQueueTest() {
simpleProducer.sendMessage();
return "success";
}
}
测试启动项目访问 simpleQueueTest:
结果:
2 工作队列(1生产者对多个消费者)
编写工作配置
package com.example.rabbitConfig;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class WorkQueueConfig {
/**
* 队列名.
*/
private final String work = "work_queue";
@Bean
public Queue workQueue() {
return new Queue(work);
}
}
编写生产者
package com.example.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class WorkProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
for (int i = 0; i < 10; i++) {
String message = "工作消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("work_queue", message);
}
}
}
编写消费者1
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class WorkConsumers1 {
@RabbitListener(queues = "work_queue")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息111:" + new String(message.getBody()));
}
}
编写消费者2
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class WorkConsumers2 {
@RabbitListener(queues = "work_queue")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息222222:" + new String(message.getBody()));
}
}
编写测试方法
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {
@Autowired
private WorkProducer workProducer;
@RequestMapping(value = "workQueueTest")
@ResponseBody
public String workQueueTest() {
workProducer.sendMessage();
return "success";
}
}
启动项目,访问
测试结果:
控制台打印,发现10条消息 偶数条消费者1获取,奇数条消费者2获取,并且平均分配。当然通过代码实现按需分配,即谁的性能强,谁优先原则,实现负载均衡。
配置可控分配数
3 Fanout Exchange(订阅模式–多个消费者监听不同的队列,但队列都绑定同一个交换机)
编写订阅配置类
package com.example.rabbitConfig;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class FanoutQueueConfig {
/**
* 声明队列名.
*/
private final String fanout1 = "fanout_queue_1";
private final String fanout2 = "fanout_queue_2";
/**
* 声明交换机的名字.
*/
private final String fanoutExchange = "fanoutExchange";
/**
* 声明队列.
*
* @return
*/
@Bean
public Queue fanoutQueue1() {
return new Queue(fanout1);
}
@Bean
public Queue fanoutQueue2() {
return new Queue(fanout2);
}
/**
* 声明交换机.
*/
@Bean
public FanoutExchange exchange() {
return new FanoutExchange(fanoutExchange);
}
/**
* 队列绑定交换机,也可在可视化工具中进行绑定.
*
* @return
*/
@Bean
public Binding bindingFanoutQueue1(Queue fanoutQueue1, FanoutExchange exchange) {
return BindingBuilder.bind(fanoutQueue1).to(exchange);
}
@Bean
public Binding bindingFanoutQueue2(Queue fanoutQueue2, FanoutExchange exchange) {
return BindingBuilder.bind(fanoutQueue2).to(exchange);
}
}
编写订阅生产者
package com.example.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class FanoutProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage() {
for (int i = 0; i < 5; i++) {
String message = "订阅模式消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("fanoutExchange", "", message);
}
}
}
编写订阅消费者1
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class FanoutConsumers1 {
@RabbitListener(queues = "fanout_queue_1")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息111:" + new String(message.getBody()));
}
}
编写订阅消费者2
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class FanoutConsumers2 {
@RabbitListener(queues = "fanout_queue_2")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息222222:" + new String(message.getBody()));
}
}
编写测试方法
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {
@Autowired
private FanoutProducer fanoutProducer;
@RequestMapping(value = "fanoutQueueTest")
@ResponseBody
public String fanoutQueueTest() {
fanoutProducer.sendMessage();
return "success";
}
}
启动项目访问,测试结果:
控制台打印 ,发现两个绑定了不同队列的消费者都接受到了同一条消息
查看RabbitMq 服务器:
4 Direct Exchange(路由模式,订阅模式+指定routingKey)
编写路由配置类
package com.example.rabbitConfig;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class DirectQueueConfig {
/**
* 声明队列名.
*/
private final String direct1 = "direct_queue_1";
private final String direct2 = "direct_queue_2";
/**
* 声明交换机的名字.
*/
private final String directExchange = "directExchange";
/**
* 声明队列.
*
* @return
*/
@Bean
public Queue directQueue1() {
return new Queue(direct1);
}
@Bean
public Queue directQueue2() {
return new Queue(direct2);
}
/**
* 声明路由交换机.
*
* @return
*/
@Bean
public DirectExchange directExchange() {
return new DirectExchange(directExchange);
}
/**
* 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
*
* @return
*/
@Bean
Binding bindingDirectExchange1(Queue directQueue1, DirectExchange exchange) {
return BindingBuilder.bind(directQueue1).to(exchange).with("update");
}
/**
* 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
*
* @return
*/
@Bean
Binding bindingDirectExchange2(Queue directQueue2, DirectExchange exchange) {
return BindingBuilder.bind(directQueue2).to(exchange).with("add");
}
}
编写生产者
package com.example.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class DirectProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessageA() {
for (int i = 0; i < 5; i++) {
String message = "路由模式--routingKey=update消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("directExchange", "update", message);
}
}
public void sendMessageB() {
for (int i = 0; i < 5; i++) {
String message = "路由模式--routingKey=add消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("directExchange", "add", message);
}
}
}
编写消费者1
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class DirectConsumers1 {
@RabbitListener(queues = "direct_queue_1")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息111:" + new String(message.getBody()));
}
}
消费者2
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class DirectConsumers2 {
@RabbitListener(queues = "direct_queue_2")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息222222:" + new String(message.getBody()));
}
}
编写访问类
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {
@Autowired
private DirectProducer directProducer;
@RequestMapping(value = "directQueueTest1")
@ResponseBody
public String directQueueTest1() {
directProducer.sendMessageA();
return "success";
}
@RequestMapping(value = "directQueueTest2")
@ResponseBody
public String directQueueTest2() {
directProducer.sendMessageB();
return "success";
}
}
启动项目,访问路径。
测试结果:
directQueueTest1
directQueueTest2
5 Topic Exchange(路由模式)
编写路由配置类
package com.example.rabbitConfig;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class TopicQueueConfig {
/**
* 声明队列名.
*/
private final String topic1 = "topic_queue_1";
private final String topic2 = "topic_queue_2";
/**
* 声明交换机的名字.
*/
private final String topicExchange = "topicExchange";
/**
* 声明队列.
*
* @return
*/
@Bean
public Queue topicQueue1() {
return new Queue(topic1);
}
@Bean
public Queue topicQueue2() {
return new Queue(topic2);
}
/**
* 声明路由交换机.
*
* @return
*/
@Bean
public TopicExchange topicExchange() {
return new TopicExchange(topicExchange);
}
/**
* 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
*
* @return
*/
@Bean
Binding bindingTopicExchange1(Queue topicQueue1, TopicExchange exchange) {
return BindingBuilder.bind(topicQueue1).to(exchange).with("topic.keyA");
}
/**
* 队列绑定交换机,指定routingKey,也可在可视化工具中进行绑定.
* 绑定的routing key 也可以使用通配符:
* *:匹配不多不少一个词
* #:匹配一个或多个词
*
* @return
*/
@Bean
Binding bindingTopicExchange2(Queue topicQueue2, TopicExchange exchange) {
return BindingBuilder.bind(topicQueue2).to(exchange).with("topic.#");
}
}
编写生产者
package com.example.controller;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TopicProducer {
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessageA() {
for (int i = 0; i < 5; i++) {
String message = "通配符模式--routingKey=topic.keyA消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("topicExchange", "topic.keyA", message);
}
}
public void sendMessageB() {
for (int i = 0; i < 5; i++) {
String message = "通配符模式--routingKey=topic.#消息" + i;
System.out.println("我是生产信息的:" + message);
rabbitTemplate.convertAndSend("topicExchange", "topic.keyD.keyE", message);
}
}
}
编写消费者1
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class TopicConsumers1 {
@RabbitListener(queues = "topic_queue_1")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息111:" + new String(message.getBody()));
}
}
编写消费者2
package com.example.service;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
public class TopicConsumers2 {
@RabbitListener(queues = "topic_queue_2")//队列的名称
public void readMessage(Message message, Channel channel) throws IOException {
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
System.out.println("我是消费信息222222:" + new String(message.getBody()));
}
}
编写访问类
package com.example.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(value = "rabbitMq")
public class RabbitMqController {
@Autowired
private TopicProducer topicProducer;
@RequestMapping(value = "topicQueueTest1")
@ResponseBody
public String topicQueueTest1() {
topicProducer.sendMessageA();
return "success";
}
@RequestMapping(value = "topicQueueTest2")
@ResponseBody
public String topicQueueTest2() {
topicProducer.sendMessageB();
return "success";
}
}
启动项目,访问路径。
结果
topicQueueTest1方法,两个消费者都能消费
topicQueueTest2方法只有消费者2 可以消费
至此,五种队列的实现已结束,如果你觉得本篇文章对你有所帮助的话,点个赞哈哈。
实现生产者消息确认
编写消息发送确认类 RabbitConfirmCallback
package com.example.rabbitConfig;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
System.out.println("=======ConfirmCallback=========");
System.out.println("correlationData = " + correlationData);
System.out.println("ack = " + ack);
System.out.println("cause = " + cause);
System.out.println("=======ConfirmCallback=========");
}
}
编写消息发送交换机返回机制RabbitConfirmReturnCallBack
package com.example.rabbitConfig;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
public class RabbitConfirmReturnCallBack implements RabbitTemplate.ReturnCallback {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println("--------------ReturnCallback----------------");
System.out.println("message = " + message);
System.out.println("replyCode = " + replyCode);
System.out.println("replyText = " + replyText);
System.out.println("exchange = " + exchange);
System.out.println("routingKey = " + routingKey);
System.out.println("--------------ReturnCallback----------------");
}
}
在我们的rabbit队列配置类里设置RabbitTemplate
举例
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void initRabbitTemplate() {
// 设置生产者消息确认
rabbitTemplate.setConfirmCallback(new RabbitConfirmCallback());
rabbitTemplate.setReturnCallback(new RabbitConfirmReturnCallBack());
}
启动项目发送消息,消息被正常消费,confim回调返回ack=true
如果我们将exchange修改,发送到一个不存在的exchange中,会怎么样呢?会发现confirm回调为false,打印出结果为不存在topicExchange1111的交换机
如果我们在消费端处理逻辑时出错会怎么样呢?修改消费端代码我们在消费时让它报错
confirm回调为true,但是在rabbitmq的web界面会发现存在一条没有消费的消息
如果我们把channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);中最后一个参数改为false呢,会发现在web管理界面没有未被消费的消息,说明这条消息已经被摒弃。
实际开发中,到底是打回到队列呢还是摒弃,要看自己的需求,但是打回队列应该有次数限制,不然会陷入死循环。
继续测试,将routingKey修改为一个没有的key,
结论:
如果消息没有到exchange,则confirm回调,ack=false
如果消息到达exchange,则confirm回调,ack=true
exchange到queue成功,则不回调return
exchange到queue失败,则回调return