本文有整合springboot项目,也有纯rabbitmq的使用介绍
rabbitmq介绍:消息中间件,在很多场景下可将一些复杂业务代码解偶出来的同时,提高系统总体响应速度。
例如业务:用户下单且成功后,需要发送邮件通知用户,最后返回响应。假设每个过程用时1s,总的耗时就是3s,使用rabbitmq将发送邮件的需求抽离出来,下单成功后,发送消息给rabbitmq异步给用户发送邮件,那么总体的响应时间就是2s,快了1s也算是不错滴,嘿嘿
本文目录
fanout路由模型(广播)
介绍:只要生产者发送了消息,所有的消费者都能接收到消息,且效果类似于轮循,交替消费
生产者
发送消息到交换机上即可
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//交换机名字、交换机类型
channel.exchangeDeclare("emsExchange","fanout");
//参数一:交换机名称
//参数二:队列名称 routing
//参数三:持久化消息
//参数四:消息内容
for (int i = 0; i < 20; i++) {
channel.basicPublish("emsExchange", "", null, (i+"fanout type message").getBytes());
}
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
消费者一
构建临时队列,临时队列绑定fanout交换机,从临时队列中消费消息即可,且没有要匹配路由的限制
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
Connection connection = null;
try {
connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//交换机名字、交换机类型
channel.exchangeDeclare("emsExchange","fanout");
//临时队列
String queue = channel.queueDeclare().getQueue();
//此通道交换机绑定队列、参数三:路由
channel.queueBind(queue, "emsExchange", "");
//参数一:消费哪个队列的消息
//参数二:开始消息的自动确认机制
//参数三:消费时的回掉接口
channel.basicConsume(queue, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
System.out.println("消费者一:" + new String(body));
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
消费者二同上
修改打印语句为消费者二即可
效果
先打开消费者一和消费者二,继而打开生产者,消费者一和二都能消费到这些消息
work路由模型
介绍:简单的路由匹配消息
核心:基于路由
生产者
生产者仅指定路由发送消息
String routingKey = "user";
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//参数1:交换机名称 参数2:路由名称 参数3:持久化消息 参数4:消息内容
for (int i = 0; i < 20; i++) {
channel.basicPublish("", routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, (i + "topic动态路由模型").getBytes());
}
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
消费者
routingKey匹配上了,就能消费
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
Connection connection = null;
try {
connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//参数一:消费哪个队列的消息
//参数二:开始消息的自动确认机制
//参数三:消费时的回掉接口
channel.basicConsume("user", true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
System.out.println("消费者:" + new String(body));
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
direct路由模型
介绍:动态路由消息
核心:交换机+队列+路由都需指定
生产者
交换机类型声明为direct类型,发送消息指定:路由+交换机
String routingKey = "user.zzh";
String exchange = "directExchange";
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//交换机名称、交换机类型
channel.exchangeDeclare(exchange, "direct");
//参数1:交换机名称 参数2:路由名称 参数3:持久化消息 参数4:消息内容
for (int i = 0; i < 20; i++) {
channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, (i + "direct动态路由模型").getBytes());
}
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
消费者
通道绑定direct交换机,消费时路由到指定的消息上面
String routingKey = "user.zzh";
String exchange = "directExchange";
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
Connection connection = null;
try {
connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String queue = channel.queueDeclare().getQueue();
//交换机名称、交换机类型
channel.exchangeDeclare(exchange, "direct");
//临时队列绑定交换机指定的路由上面
channel.queueBind(queue, exchange, routingKey);
//参数一:消费哪个队列的消息
//参数二:开始消息的自动确认机制
//参数三:消费时的回掉接口
channel.basicConsume(queue, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
System.out.println("direct消费者一:" + new String(body));
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
topic路由模型
介绍:用法和direct差不多,就是多了通配符的路由匹配。
假设发送消息的路由为user.zzh.aaa
user.#
#匹配一个,最多匹配user.zzh类型的路由
(user.*)
*匹配多个,可以匹配任意以user.开头的路由
生产者
修改交换机类型为topic,发送消息指定交换机、路由key
String routingKey = "user.zzh";
String exchange = "topicExchange";
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
try {
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//交换机名称、交换机类型
channel.exchangeDeclare(exchange, "topic");
//参数1:交换机名称 参数2:路由名称 参数3:持久化消息 参数4:消息内容
for (int i = 0; i < 20; i++) {
channel.basicPublish(exchange, routingKey, MessageProperties.PERSISTENT_TEXT_PLAIN, (i + "topic动态路由模型").getBytes());
}
channel.close();
connection.close();
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
消费者
对比direct修改路由user.*
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("127.0.0.1");
connectionFactory.setPort(5672);
connectionFactory.setVirtualHost("ems");
connectionFactory.setUsername("ems");
connectionFactory.setPassword("ems");
Connection connection = null;
try {
connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
String queue = channel.queueDeclare().getQueue();
//临时队列绑定交换机指定的路由上面
channel.queueBind(queue, "topicExchange", "user.*");
//参数一:消费哪个队列的消息
//参数二:开始消息的自动确认机制
//参数三:消费时的回掉接口
channel.basicConsume(queue, true, new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties,
byte[] body) throws IOException {
System.out.println("消费者:" + new String(body));
}
});
} catch (IOException e) {
e.printStackTrace();
} catch (TimeoutException e) {
e.printStackTrace();
}
rabbitmq整合springboot
添加配置文件(用户名、密码、虚拟主机、ip、端口号)
spring:
rabbitmq:
host: 127.0.0.1
username: ems
password: ems
virtual-host: ems
port: 5672
四大生产者汇总
@Autowired
RabbitTemplate rabbitTemplate;
/**
* HELLO
*/
@Test
void contextLoads() {
rabbitTemplate.convertAndSend("user", "hello");
}
/**
* work路由模型
*/
@Test
void contextLoad3() {
for (int i = 0; i <10; i++) {
rabbitTemplate.convertAndSend("work", "work路由模型"+i);
}
}
/**
* fanout
*/
@Test
void contextLoads2() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("fanoutExchange", "", "fanout路由模型-" + i);
}
}
/**
* topic
*/
@Test
void contextLoads3() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("topicExchange", "user.zzh.zzhh", "topic路由模型-" + i);
}
}
/**
* direct
*/
@Test
void contextLoads4() {
for (int i = 0; i < 10; i++) {
rabbitTemplate.convertAndSend("directExchange", "warns.zzh", "direct路由模型-" + i);
}
}
work路由模式消费者、springboot版本
/**
* work路由模型:发送消息指定队列即可
*/
@Component
public class workCustomer {
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receivel(String msg) {
System.out.println("work消费者一msg:" + msg);
}
@RabbitListener(queuesToDeclare = @Queue("work"))
public void receivel2(String msg) {
System.out.println("work消费者二msg:" + msg);
}
}
fanout路由模式消费者、springboot版本
@Queue:临时队列
/**
* fanout广播:一个生产者发送消息,所有的消费者都能接受到消息
*/
@Component
public class fanoutCustomer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "fanoutExchange", type = "fanout"))
})
public void receivel(String msg) {
System.out.println("fanout消费者1msg:" + msg);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "fanoutExchange", type = "fanout"))
})
public void receivel2(String msg) {
System.out.println("fanout消费者2msg:" + msg);
}
}
topic路由模式消费者、springboot版本
key:匹配路由
@Component
public class topicCustomer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "topicExchange", type = "topic"),
key = "user.*")
})
public void receivel(String msg) {
System.out.println("topic1消费者msg:" + msg);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "topicExchange", type = "topic"),
key = ("user.#"))
})
public void receivel2(String msg) {
System.out.println("topic2消费者msg:" + msg);
}
}
direct路由模式消费者、springboot版本
/**
* springBoot整合direct(路由)
*/
@Component
public class directCustomer {
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "directExchange",type ="direct"),
key = {
"logs","errors"})
})
public void receivel(String msg) {
System.out.println("direct消费者一msg:" + msg);
}
@RabbitListener(bindings = {
@QueueBinding(
value = @Queue,
exchange = @Exchange(value = "directExchange", type = "direct"),
key = {
"logs","warns.zzh"})
})
public void receivel2(String msg) {
System.out.println("direct消费者二msg:" + msg);
}
}
配置文件写法交换机、队列
声明交换机(指定类型)、队列(指定参数)、绑定(绑定队列、交换机)
@Bean
public TopicExchange cTopicExchange() {
TopicExchange topicExchange = new TopicExchange("cTopicExchange", true, true, null);
return topicExchange;
}
@Bean
public Queue cTopicQueue() {
Queue queue = new Queue("cTopicQueue", true);
return queue;
}
@Bean
public Binding bind() {
Binding binding = BindingBuilder.bind(cTopicQueue()).to(cTopicExchange()).with("user.*");
return binding;
}
配置文件写法消费者
由于我们指定了相关的队列交换机的关系,直接监听对应的队列即可,由于可能消息比较复杂,发送的消息是什么类型就用什么类型接收
- containerFactory:指定消息监听器的相关配置(例如接收的消息序列化相关配置)
@Component
public class allCustomerDemo {
/**
* cTopicQueue在配置文件中已经经过配置,只要消息发送没问题就能监听的到消息
* @param user
*/
@RabbitListener(queues = "cTopicQueue",containerFactory = "simpleRabbitListenerContainerFactory")
public void listenerTopic(User user) {
System.out.println("msg:" + user.getUsername());
}
}
配置文件生产者
MessagePostProcessor:可以在消息发送的途中对消息进行干预(例如设置消息过期时间、是否持久化…)
@Autowired
RabbitTemplate rabbitTemplate;
/**
* topic路由模型之发送
*/
public void topicSend() {
User zzh = new User().setAge(21).setUsername("张子行").setId(1872).setVersion("1.0");
rabbitTemplate.setExchange("cTopicExchange");
rabbitTemplate.setRoutingKey("user.dshjdjsh");
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.convertAndSend(zzh, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
MessageProperties msgProperties = message.getMessageProperties();
msgProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
log.info("发送消息成功!");
return message;
}
});
}
死信队列
介绍:消息在死信队列中过期了,自动转发至别的队列
死信队列配置
和普通队列区别就是多了额外参数配置:消息过期自动把消息转发到cTopicExchange
params.put("x-dead-letter-exchange", "cTopicExchange");
@Bean
public TopicExchange cdTopicExchange() {
TopicExchange topicExchange = new TopicExchange("cdTopicExchange", true, false, null);
return topicExchange;
}
/**
* 消息超时发送给cTopicExchange,相当于转发。
* 故cTopicExchange与cdTopicExchange的路由要相同,或者是包含关系
*
* @return
*/
@Bean
public Queue cdTopicQueue() {
HashMap<String, Object> params = new HashMap<>();
params.put("x-dead-letter-exchange", "cTopicExchange");
Queue queue = new Queue("cdTopicQueue", true, false, false, params);
return queue;
}
@Bean
public Binding cdBind() {
Binding binding = BindingBuilder.bind(cdTopicQueue()).to(cdTopicExchange()).with("user.dead");
return binding;
}
死信队列生产者
/**
* 发送至死信队列
*/
public void deadSend(){
User zzh = new User().setAge(21).setUsername("张子行").setId(1872).setVersion("1.0");
rabbitTemplate.setExchange("cdTopicExchange");
rabbitTemplate.setRoutingKey("user.dead");
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
rabbitTemplate.convertAndSend(zzh, new MessagePostProcessor() {
@Override
public Message postProcessMessage(Message message) throws AmqpException {
MessageProperties msgProperties = message.getMessageProperties();
//设置过期时间:10s
msgProperties.setExpiration("10000");
//设置持久化
msgProperties.setDeliveryMode(MessageDeliveryMode.PERSISTENT);
log.info("发送消息成功!");
return message;
}
});