一、Windows下启动RabbitMQ服务及安装详解
启动RabbitMQ服务需要Erlang的环境。
如下地址同时下载和安装:
Erlang:http://www.erlang.org/download.html
RabbitMQ :http://www.rabbitmq.com/download.html
安装RabbitMQ时注意如下图:
RabbitMQ Service默认是自动勾选中的,这里我们取消勾选。如选中启动RabbitMQ服务时会报错。
安装完成后,Erlang和RabbitMQ环境变量是没有配置的,需要自己手动去配置,如下图:
变量名分别为:ERLANG_HOME 、RABBITMQ_SERVICE;同时把变量为配置在path环境变量中即可。
然后我们启动RabbitMQ服务,如下图:
我们看上图 completed with 0 plugins. 意思是插件为0;我们服务启动了但是访问不了http://localhost:15672/ 就是缺少插件。也就是界面管理工具所需的插件,找到RabbitMQ的安装目录sbin
执行第一种方式,如下命令:
rabbitmq-plugins enable rabbitmq_management
第二种方式,如下命令:
rabbitmq-plugins enable plugin-name
其中的 plugin-name 包括下面的名字,下面每一个输入执行一次即可:
mochiweb
webmachine
rabbitmq_mochiweb
amqp_client
rabbitmq_management_agent
rabbitmq_management
建议使用第一种方式执行,执行完命令如下图说明已经成功的安装插件:
最后,我们启动RabbitMQ服务即可(启动rabbitmq-server.bat),如下:
## ##
## ## RabbitMQ 3.7.0. Copyright (C) 2007-2017 Pivotal Software, Inc.
########## Licensed under the MPL. See http://www.rabbitmq.com/
###### ##
########## Logs: C:/Users/Jiacheng/AppData/Roaming/RabbitMQ/log/RABBIT~1.LOG
C:/Users/Jiacheng/AppData/Roaming/RabbitMQ/log/rabbit@DESKTOP-A15KK90_upgrade.log
Starting broker...
completed with 3 plugins.
显示已有3个插件,最后我们在浏览器输入:http://localhost:15672/ 进行测试;
默认的登陆账号为:guest,密码为:guest。
二、Spring Boot 整合 RabbitMQ
1、引入rabbitmq的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2、新增rabbimq yml配置信息
#rabbitmq
application:
name: springboot-rabbitmq
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
publisher-confirms: true
virtual-host: /
3、新建RabbitMqApplication应用类
1、创建初始化队列
2、Initializing exchange, binding exchange with routingKey
package ncu.jerry.orangeplus.rabbitMq;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
/**
* Created by Jiacheng on 2017/12/18.
*/
@Component
public class RabbitMqApplication {
final static String queueName = "helloQueue";
final static String userQueueName = "userQueue";
@Bean
public Queue helloQueue() {
return new Queue(queueName);
}
@Bean
public Queue userQueue() {
return new Queue(userQueueName);
}
//topic Exchange Queue
@Bean
public Queue queueMessage() {
return new Queue("topic.message");
}
@Bean
public Queue queueMessages() {
return new Queue("topic.messages");
}
@Bean
public Queue AMessage() {
return new Queue("fanout.A");
}
@Bean
public Queue BMessage() {
return new Queue("fanout.B");
}
@Bean
public Queue CMessage() {
return new Queue("fanout.C");
}
@Bean
TopicExchange exchange() {
return new TopicExchange("exchange");
}
@Bean
TopicExchange exchange2() {
return new TopicExchange("exchange2");
}
@Bean
FanoutExchange fanoutExchange() {
return new FanoutExchange("fanoutExchange");
}
/**
* 将队列topic.message与exchange绑定,binding_key为topic.message,完全匹配
* @param queueMessage
* @param exchange
* @return
*/
@Bean
Binding bindingExchangeMessage(Queue queueMessage, TopicExchange exchange) {
return BindingBuilder.bind(queueMessage).to(exchange).with("topic.message");
}
/**
* 将队列topic.messages与exchange绑定,binding_key为topic.#,模糊匹配
* @param queueMessages
* @param exchange
* @return
*/
@Bean
Binding bindingExchangeMessages(Queue queueMessages, TopicExchange exchange) {
return BindingBuilder.bind(queueMessages).to(exchange).with("topic.#");
}
/**
* 将队列topic.message与exchange2绑定,binding_key为topic.message,完全匹配
* @param queueMessage
* @param exchange2
* @return
*/
@Bean
Binding bindingExchange2Message(Queue queueMessage, TopicExchange exchange2) {
return BindingBuilder.bind(queueMessage).to(exchange2).with("topic.message");
}
/**
* 任何发送到Fanout Exchange的消息都会被转发到与该Exchange绑定(Binding)的所有Queue上
*
* @param AMessage
* @param fanoutExchange
* @return
*/
@Bean
Binding bindingExchangeA(Queue AMessage,FanoutExchange fanoutExchange) {
return BindingBuilder.bind(AMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeB(Queue BMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(BMessage).to(fanoutExchange);
}
@Bean
Binding bindingExchangeC(Queue CMessage, FanoutExchange fanoutExchange) {
return BindingBuilder.bind(CMessage).to(fanoutExchange);
}
}
4、各类情景实现
(基础知识补充:JMS基本概念)
4.1 P2P模式
(文章该部分只简单描述场景,具体源码实现可在文章底部GitHub仓库下载☺)
1、最简单的hello生产和消费实现(单生产者和单消费者)
2、单生产者-多消费者
3、多生产者-多消费者
4、实体类传输
Spring Boot完美的支持对象的发送和接收,不需要格外的配置。其中实体类必须实现序列化接口。
4.2 Pub/Sub模式
1、topic ExChange示例
topic 是RabbitMQ中最灵活的一种方式,可以根据binding_key自由的绑定不同的队列。
首先对topic规则配置,这里使用两个队列来测试(也就是在RabbitMqApplication类中创建的topic.message和topic.messages两个队列)。
其次创建2个ExChange(exchange、exchange2)。将队列topic.message、topic.messages与exchange绑定;将队列topic.message与exchange2绑定。其中topic.message的binding_key为“topic.message”,topic.messages的binding_key为“topic.#”。(#为模糊匹配)
生产者:
@Component
public class TopicSender {
protected final static Logger logger = LoggerFactory.getLogger(TopicSender.class);
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
String msg1 = "topic.mesaage msg1";
logger.info("sender1 : " + msg1);
this.rabbitTemplate.convertAndSend("exchange", "topic.message", msg1);
String msg2 = "topic.mesaages msg2";
logger.info("sender2 : " + msg2);
this.rabbitTemplate.convertAndSend("exchange", "topic.messages", msg2);
String msg3 = "topic.mesaages msg3";
logger.info("sender3 : " + msg3);
this.rabbitTemplate.convertAndSend("exchange2", "topic.message", msg3);
}
}
消费者1(topic.message):
@Component
@RabbitListener(queues = "topic.message")
public class topicMessageReceiver {
protected final static Logger logger = LoggerFactory.getLogger(topicMessageReceiver.class);
@RabbitHandler
public void process(String msg) {
logger.info("topicMessageReceiver : " +msg);
}
}
消费者2(topic.messages):
@Component
@RabbitListener(queues = "topic.messages")
public class topicMessagesReceiver {
protected final static Logger logger = LoggerFactory.getLogger(topicMessagesReceiver.class);
@RabbitHandler
public void process(String msg) {
logger.info("topicMessagesReceiver : " +msg);
}
}
测试controller及日志结果:
/**
* topic exchange类型测试
*/
@PostMapping("/topicTest")
public void topicTest() {
topicSender.send();
}
topic ExChange总结:
sender1发送的消息,routing_key是“topic.message”,所以exchange里面的绑定的binding_key是“topic.message”,topic.#都符合路由规则;所以sender1发送的消息,两个队列都能接收到;sender2发送的消息,routing_key是“topic.messages”,所以exchange里面的绑定的binding_key只有topic.#都符合路由规则,sender2发送的消息只有队列topic.messages能收到;sender3发送的消息通过exchange2,队列topic.messages能收到。
2、fanout ExChange示例
Fanout 就是我们熟悉的广播模式或者订阅模式,给Fanout转发器发送消息,绑定了这个转发器的所有队列都收到这个消息。
这里使用三个队列来测试(也就是在RabbitMqApplication类中创建和绑定的fanout.A、fanout.B、fanout.C)这三个队列都和RabbitMqApplication中创建的fanoutExchange转发器绑定。
生产者:
@Component
public class FanoutSender {
@Autowired
private AmqpTemplate rabbitTemplate;
public void send() {
String msgString="fanoutSender :hello";
System.out.println(msgString);
//这种模式不需要routingKey
this.rabbitTemplate.convertAndSend("fanoutExchange", StringUtils.EMPTY, msgString);
}
}
消费者A/B/C:
@Component
@RabbitListener(queues = "fanout.A")
public class FanoutReceiverA {
@RabbitHandler
public void process(String msg) {
System.out.println("FanoutReceiverA : " + msg);
}
}
测试controller及日志结果:
/**
* fanout exchange类型测试
*/
@PostMapping("/fanoutTest")
public void fanoutTest() {
fanoutSender.send();
}
fanout ExChange总结:fanoutSender发送消息的时候,无需指定routing_key(设置为StringUtils.EMPTY),所有接收者均能接收到消息。
3、带callback的消息发送
增加回调处理。该示例中没有新建队列和exchange,用的是topic ExChange示例中的topic.messages队列和exchange转发器。消费者也是topic ExChange示例中的topicMessagesReceiver。
关于发布确认机制:
(1) 需置CachingConnectionFactory的publisherConfirms属性为true;
(2) 生产者需调用setConfirmCallback(ConfirmCallback callback),Confirms就会回调给生产者;
(3) 消费者需考虑消息去重处理。
注意:一个RabbitTemplate只能支持一个ConfirmCallback。
@Component
public class CallBackSender implements RabbitTemplate.ConfirmCallback{
protected final static Logger logger = LoggerFactory.getLogger(CallBackSender.class);
@Autowired
private RabbitTemplate rabbitTemplatenew;
public void send() {
rabbitTemplatenew.setConfirmCallback(this);
String msg="callbackSender : i am callback sender";
logger.debug(msg);
CorrelationData correlationData = new CorrelationData(UUID.randomUUID().toString());
logger.debug("callbackSender UUID: " + correlationData.getId());
this.rabbitTemplatenew.convertAndSend("exchange", "topic.messages", msg, correlationData);
}
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
logger.debug("callbakck confirm: " + correlationData.getId());
}
}
测试日志结果: