RabbitMQ介绍
MQ全称为MessageQueue,即消息队列,RabbitMQ是由erlang语言开发,基于AMQP(AdvancedMessageQueue高级消息队列协议)协议实现的消息队列,它是一种应用程序之间的通信方法,消息队列在分布式系统开发中应用非常广泛。RabbitMQ官方地址:http://www.rabbitmq.com/
开发中消息队列通常有如下应用场景:
1,任务异步处理
2,应用程序解耦合
RabbitMQ的工作原理:

组成说明:
Broker:消息队列服务进程,此进程包括两个部分:Exchange和Queue。
Exchange:消息队列交换机,按一定的规则将消息路由转发到某个队列,对消息进行过虑。
Queue:消息队列,存储消息的队列,消息到达队列并转发给指定的消费方。
Producer:消息生产者,即生产方客户端,生产方客户端将消息发送到MQ。
Consumer:消息消费者,即消费方客户端,接收MQ转发的消息。
快速入门(测试类,了解原理 没啥用)
创建maven工程
1 <dependency> 2 <groupId>com.rabbitmq</groupId> 3 <artifactId>amqp-client</artifactId> 4 <version>4.0.3</version><!--此版本与spring boot 1.5.9版本匹配--> 5 </dependency> 6 <dependency> 7 <groupId>org.springframework.boot</groupId> 8 <artifactId>spring-boot-starter-logging</artifactId> 9 </dependency>
生产者
发送端操作流程:1,创建连接 2,创建通道 3,声明队列 4,发送消息
1 public class Producer01 {
2
3 //队列
4 private static final String QUEUE = "helloworld";
5
6 public static void main(String[] args) {
7 //通过连接工厂创建新的连接和mq建立连接
8 ConnectionFactory connectionFactory = new ConnectionFactory();
9 connectionFactory.setHost("127.0.0.1");
10 connectionFactory.setPort(5672);//端口
11 connectionFactory.setUsername("guest");
12 connectionFactory.setPassword("guest");
13 //设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
14 connectionFactory.setVirtualHost("/");
15 Connection connection = null;
16 Channel channel = null;
17 try {
18 //建立新连接
19 connection = connectionFactory.newConnection();
20 //创建会话通道,生产者和mq服务所有通信都在channel通道中完成
21 channel = connection.createChannel();
22 //声明队列,如果队列在mq 中没有则要创建
23 //参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
24 /**
25 * 参数明细
26 * 1、queue 队列名称
27 * 2、durable 是否持久化,如果持久化,mq重启后队列还在
28 * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
29 * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
30 * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
31 */
32 channel.queueDeclare(QUEUE,true,false,false,null);
33 //发送消息
34 //参数:String exchange, String routingKey, BasicProperties props, byte[] body
35 /**
36 * 参数明细:
37 * 1、exchange,交换机,如果不指定将使用mq的默认交换机(设置为"")
38 * 2、routingKey,路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认交换机,routingKey设置为队列的名称
39 * 3、props,消息的属性
40 * 4、body,消息内容
41 */
42 //消息内容
43 String message = "hello world 黑马程序员";
44 channel.basicPublish("",QUEUE,null,message.getBytes());
45 System.out.println("send to mq "+message);
46 } catch (Exception e) {
47 e.printStackTrace();
48 } finally {
49 //关闭连接
50 //先关闭通道
51 try {
52 channel.close();
53 } catch (IOException e) {
54 e.printStackTrace();
55 } catch (TimeoutException e) {
56 e.printStackTrace();
57 }
58 try {
59 connection.close();
60 } catch (IOException e) {
61 e.printStackTrace();
62 }
63 }
64 }
65
66 }
消费者
接收端操作流程:1,创建连接 2,创建通道 3,声明队列 4,监听队列 5,接收消息
1 public class Consumer01 {
2
3 //队列
4 private static final String QUEUE = "helloworld";
5
6 public static void main(String[] args) throws IOException, TimeoutException {
7 //通过连接工厂创建新的连接和mq建立连接
8 ConnectionFactory connectionFactory = new ConnectionFactory();
9 connectionFactory.setHost("127.0.0.1");
10 connectionFactory.setPort(5672);//端口
11 connectionFactory.setUsername("guest");//账户
12 connectionFactory.setPassword("guest");//密码
13 //设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
14 connectionFactory.setVirtualHost("/");
15
16 //建立新连接
17 Connection connection = connectionFactory.newConnection();
18 //创建会话通道,生产者和mq服务所有通信都在channel通道中完成
19 Channel channel = connection.createChannel();
20
21 //监听队列
22 //声明队列,如果队列在mq 中没有则要创建
23 //参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
24 /**
25 * 参数明细
26 * 1、queue 队列名称
27 * 2、durable 是否持久化,如果持久化,mq重启后队列还在
28 * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
29 * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
30 * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
31 */
32 channel.queueDeclare(QUEUE,true,false,false,null);
33
34 //实现消费方法
35 DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
36
37 /**
38 * 当接收到消息后此方法将被调用
39 * @param consumerTag 消费者标签,用来标识消费者的,在监听队列时设置channel.basicConsume
40 * @param envelope 信封,通过envelope
41 * @param properties 消息属性
42 * @param body 消息内容
43 * @throws IOException
44 */
45 @Override
46 public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
47 //交换机
48 String exchange = envelope.getExchange();
49 //消息id,mq在channel中用来标识消息的id,可用于确认消息已接收
50 long deliveryTag = envelope.getDeliveryTag();
51 //消息内容
52 String message= new String(body,"utf-8");
53 System.out.println("receive message:"+message);
54 }
55 };
56
57 //监听队列
58 //参数:String queue, boolean autoAck, Consumer callback
59 /**
60 * 参数明细:
61 * 1、queue 队列名称
62 * 2、autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,如果将此参数设置为tru表示会自动回复mq,如果设置为false要通过编程实现回复
63 * 3、callback,消费方法,当消费者接收到消息要执行的方法
64 */
65 channel.basicConsume(QUEUE,true,defaultConsumer);
66
67 }
68 }
工作模式(P:生产者,也就是要发送消息的程序 C:消费者:消息的接受者,会一直等待消息到来 queue:消息队列,图中红色部分 X:交换机)
Exchange(交换机)只负责转发消息,不具备存储消息的能力,因此如果没有任何队列与Exchange绑定,或者没有符合路由规则的队列,那么消息会丢失!
1、Work queues 工作队列

work queues与入门程序相比,多了一个消费端,两个消费端共同消费同一个队列中的消息。
2、Publish/Subscribe 发布订阅

3、Routing 路由

1、每个消费者监听自己的队列,并且设置routingkey。 2、生产者将消息发给交换机,由交换机根据routingkey来转发消息到指定的队列。
案例:用户通知,当用户充值成功或转账完成系统通知用户,通知方式有短信、邮件多种方法。(更好的对代码进行测试)
生产者
声明exchange_routing_inform交换机。
声明两个队列并且绑定到此交换机,绑定时需要指定routingkey
发送消息时需要指定routingkey
1 public class Producer03_routing {
2 //队列名称
3 private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";//队列
4 private static final String QUEUE_INFORM_SMS = "queue_inform_sms";//队列
5 private static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";//交换机
6 private static final String ROUTINGKEY_EMAIL="inform_email";//routingKey 路由key
7 private static final String ROUTINGKEY_SMS="inform_sms";//routingKey 路由key
8 public static void main(String[] args) {
9 //通过连接工厂创建新的连接和mq建立连接
10 ConnectionFactory connectionFactory = new ConnectionFactory();
11 connectionFactory.setHost("127.0.0.1");
12 connectionFactory.setPort(5672);//端口
13 connectionFactory.setUsername("guest");
14 connectionFactory.setPassword("guest");
15 //设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
16 connectionFactory.setVirtualHost("/");
17
18 Connection connection = null;
19 Channel channel = null;
20 try {
21 //建立新连接
22 connection = connectionFactory.newConnection();
23 //创建会话通道,生产者和mq服务所有通信都在channel通道中完成
24 channel = connection.createChannel();
25 //声明队列,如果队列在mq 中没有则要创建
26 //参数:String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments
27 /**
28 * 参数明细
29 * 1、queue 队列名称
30 * 2、durable 是否持久化,如果持久化,mq重启后队列还在
31 * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
32 * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
33 * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
34 */
35 channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
36 channel.queueDeclare(QUEUE_INFORM_SMS,true,false,false,null);
37 //声明一个交换机
38 //参数:String exchange, String type
39 /**
40 * 参数明细:
41 * 1、交换机的名称
42 * 2、交换机的类型
43 * fanout:对应的rabbitmq的工作模式是 publish/subscribe
44 * direct:对应的Routing 工作模式
45 * topic:对应的Topics工作模式
46 * headers: 对应的headers工作模式
47 */
48 channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
49 //进行交换机和队列绑定
50 //参数:String queue, String exchange, String routingKey
51 /**
52 * 参数明细:
53 * 1、queue 队列名称
54 * 2、exchange 交换机名称
55 * 3、routingKey 路由key,作用是交换机根据路由key的值将消息转发到指定的队列中,在发布订阅模式中调协为空字符串
56 */
57 channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);
58 channel.queueBind(QUEUE_INFORM_EMAIL,EXCHANGE_ROUTING_INFORM,"inform");
59 channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,ROUTINGKEY_SMS);
60 channel.queueBind(QUEUE_INFORM_SMS,EXCHANGE_ROUTING_INFORM,"inform");
61 //发送消息
62 //参数:String exchange, String routingKey, BasicProperties props, byte[] body
63 /**
64 * 参数明细:
65 * 1、exchange,交换机,如果不指定将使用mq的默认交换机(设置为"")
66 * 2、routingKey,路由key,交换机根据路由key来将消息转发到指定的队列,如果使用默认交换机,routingKey设置为队列的名称
67 * 3、props,消息的属性
68 * 4、body,消息内容
69 */
70 /* for(int i=0;i<5;i++){
71 //发送消息的时候指定routingKey
72 String message = "send email inform message to user";
73 channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL,null,message.getBytes());
74 System.out.println("send to mq "+message);
75 }
76 for(int i=0;i<5;i++){
77 //发送消息的时候指定routingKey
78 String message = "send sms inform message to user";
79 channel.basicPublish(EXCHANGE_ROUTING_INFORM,ROUTINGKEY_SMS,null,message.getBytes());
80 System.out.println("send to mq "+message);
81 }*/
82 for(int i=0;i<5;i++){
83 //发送消息的时候指定routingKey
84 String message = "send inform message to user";
85 channel.basicPublish(EXCHANGE_ROUTING_INFORM,"inform",null,message.getBytes());
86 System.out.println("send to mq "+message);
87 }
88
89 } catch (Exception e) {
90 e.printStackTrace();
91 } finally {
92 //关闭连接
93 //先关闭通道
94 try {
95 channel.close();
96 } catch (IOException e) {
97 e.printStackTrace();
98 } catch (TimeoutException e) {
99 e.printStackTrace();
100 }
101 try {
102 connection.close();
103 } catch (IOException e) {
104 e.printStackTrace();
105 }
106 }
107
108
109 }
110 }
消费者1(邮件发送消费者)
1 public class Consumer03_routing_email {
2 //队列名称
3 private static final String QUEUE_INFORM_EMAIL = "queue_inform_email";//队列
4 private static final String EXCHANGE_ROUTING_INFORM="exchange_routing_inform";//交换机
5 private static final String ROUTINGKEY_EMAIL="inform_email";//routingKey 路由key 和生产者相匹配
6
7 public static void main(String[] args) throws IOException, TimeoutException {
8 //通过连接工厂创建新的连接和mq建立连接
9 ConnectionFactory connectionFactory = new ConnectionFactory();
10 connectionFactory.setHost("127.0.0.1");
11 connectionFactory.setPort(5672);//端口
12 connectionFactory.setUsername("guest");
13 connectionFactory.setPassword("guest");
14 //设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机就相当于一个独立的mq
15 connectionFactory.setVirtualHost("/");
16
17 //建立新连接
18 Connection connection = connectionFactory.newConnection();
19 //创建会话通道,生产者和mq服务所有通信都在channel通道中完成
20 Channel channel = connection.createChannel();
21
22 /**
23 * 参数明细
24 * 1、queue 队列名称
25 * 2、durable 是否持久化,如果持久化,mq重启后队列还在
26 * 3、exclusive 是否独占连接,队列只允许在该连接中访问,如果connection连接关闭队列则自动删除,如果将此参数设置true可用于临时队列的创建
27 * 4、autoDelete 自动删除,队列不再使用时是否自动删除此队列,如果将此参数和exclusive参数设置为true就可以实现临时队列(队列不用了就自动删除)
28 * 5、arguments 参数,可以设置一个队列的扩展参数,比如:可设置存活时间
29 */
30 channel.queueDeclare(QUEUE_INFORM_EMAIL,true,false,false,null);
31 //声明一个交换机
32 //参数:String exchange, String type
33 /**
34 * 参数明细:
35 * 1、交换机的名称
36 * 2、交换机的类型
37 * fanout:对应的rabbitmq的工作模式是 publish/subscribe
38 * direct:对应的Routing 工作模式
39 * topic:对应的Topics工作模式
40 * headers: 对应的headers工作模式
41 */
42 channel.exchangeDeclare(EXCHANGE_ROUTING_INFORM, BuiltinExchangeType.DIRECT);
43 //进行交换机和队列绑定
44 //参数:String queue, String exchange, String routingKey
45 /**
46 * 参数明细:
47 * 1、queue 队列名称
48 * 2、exchange 交换机名称
49 * 3、routingKey 路由key,作用是交换机根据路由key的值将消息转发到指定的队列中,在发布订阅模式中调协为空字符串
50 */
51 channel.queueBind(QUEUE_INFORM_EMAIL, EXCHANGE_ROUTING_INFORM,ROUTINGKEY_EMAIL);
52
53 //实现消费方法
54 DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
55
56 /**
57 * 当接收到消息后此方法将被调用
58 * @param consumerTag 消费者标签,用来标识消费者的,在监听队列时设置channel.basicConsume
59 * @param envelope 信封,通过envelope
60 * @param properties 消息属性
61 * @param body 消息内容
62 * @throws IOException
63 */
64 @Override
65 public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
66 //交换机
67 String exchange = envelope.getExchange();
68 //消息id,mq在channel中用来标识消息的id,可用于确认消息已接收
69 long deliveryTag = envelope.getDeliveryTag();
70 //消息内容
71 String message= new String(body,"utf-8");
72 System.out.println("receive message:"+message);
73 }
74 };
75
76 //监听队列
77 //参数:String queue, boolean autoAck, Consumer callback
78 /**
79 * 参数明细:
80 * 1、queue 队列名称
81 * 2、autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,如果将此参数设置为tru表示会自动回复mq,如果设置为false要通过编程实现回复
82 * 3、callback,消费方法,当消费者接收到消息要执行的方法
83 */
84 channel.basicConsume(QUEUE_INFORM_EMAIL,true,defaultConsumer);
85
86 }
87 }
消费者2(短信发送消费者(参考邮件发送消费者的代码流程))
4、Topics 通配符

Routingkey一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如:item.insert
通配符规则:#:匹配一个或多个词 *:匹配不多不少恰好1个词
举例:item.#:能够匹配item.insert.abc或者item.insert
item.*:只能匹配item.inser
5、Header Header 转发器
6、RPC 远程过程调用

Spring整合RibbitMQ
maven依赖
1 <dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-amqp</artifactId> 4 </dependency> 5 <dependency> 6 <groupId>org.springframework.boot</groupId> 7 <artifactId>spring-boot-starter-test</artifactId> 8 </dependency> 9 <dependency> 10 <groupId>org.springframework.boot</groupId> 11 <artifactId>spring-boot-starter-logging</artifactId> 12 </dependency>
配置
1、配置application.yml
配置连接rabbitmq的参数
1 server: 2 port: 44000 3 spring: 4 application: 5 name: test-rabbitmq-producer 6 rabbitmq: 7 host: 127.0.0.1 8 port: 5672 9 username: guest 10 password: guest 11 virtualHost: /
2、定义RabbitConfig类,配置Exchange、Queue、及绑定交换机。
本例配置Topic交换机
1 @Configuration
2 public class RabbitmqConfig {
3 public static final String QUEUE_INFORM_EMAIL = "queue_inform_email";//队列
4 public static final String QUEUE_INFORM_SMS = "queue_inform_sms";
5 public static final String EXCHANGE_TOPICS_INFORM="exchange_topics_inform";//交换机
6 public static final String ROUTINGKEY_EMAIL="inform.#.email.#";//routingKey
7 public static final String ROUTINGKEY_SMS="inform.#.sms.#";//routingKey
8
9 //声明交换机
10 @Bean(EXCHANGE_TOPICS_INFORM)
11 public Exchange EXCHANGE_TOPICS_INFORM(){
12 //durable(true) 持久化,mq重启之后交换机还在
13 return ExchangeBuilder.topicExchange(EXCHANGE_TOPICS_INFORM).durable(true).build();
14 }
15
16 //声明QUEUE_INFORM_EMAIL队列
17 @Bean(QUEUE_INFORM_EMAIL)
18 public Queue QUEUE_INFORM_EMAIL(){
19 return new Queue(QUEUE_INFORM_EMAIL);
20 }
21 //声明QUEUE_INFORM_SMS队列
22 @Bean(QUEUE_INFORM_SMS)
23 public Queue QUEUE_INFORM_SMS(){
24 return new Queue(QUEUE_INFORM_SMS);
25 }
26
27 //ROUTINGKEY_EMAIL队列绑定交换机,指定routingKey
28 @Bean
29 public Binding BINDING_QUEUE_INFORM_EMAIL(@Qualifier(QUEUE_INFORM_EMAIL) Queue queue,
30 @Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
31 return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_EMAIL).noargs();
32 }
33 //ROUTINGKEY_SMS队列绑定交换机,指定routingKey
34 @Bean
35 public Binding BINDING_ROUTINGKEY_SMS(@Qualifier(QUEUE_INFORM_SMS) Queue queue,
36 @Qualifier(EXCHANGE_TOPICS_INFORM) Exchange exchange){
37 return BindingBuilder.bind(queue).to(exchange).with(ROUTINGKEY_SMS).noargs();
38 }
39 }
生产者
1 public class Producer05_topics_springboot {
2 @Autowired
3 RabbitTemplate rabbitTemplate;
4
5 //使用rabbitTemplate发送消息
6 @Test
7 public void testSendEmail(){
8
9 String message = "send email message to user";
10 /**
11 * 参数:
12 * 1、交换机名称
13 * 2、routingKey
14 * 3、消息内容
15 */
16 rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_TOPICS_INFORM,"inform.email",message);
17
18 }
19
20 //使用rabbitTemplate发送消息
21 @Test
22 public void testSendPostPage(){
23
24 Map message = new HashMap<>();
25 message.put("pageId","5a795ac7dd573c04508f3a56");
26 //将消息对象转成json串
27 String messageString = JSON.toJSONString(message);
28 //路由key,就是站点ID
29 String routingKey = "5a751fab6abb5044e0d19ea1";
30 /**
31 * 参数:
32 * 1、交换机名称
33 * 2、routingKey
34 * 3、消息内容
35 */
36 rabbitTemplate.convertAndSend("ex_routing_cms_postpage",routingKey,messageString);
37
38 }
39
40 }
消费者
1 @Component
2 public class ReceiveHandler {
3
4 @RabbitListener(queues = {RabbitmqConfig.QUEUE_INFORM_EMAIL})
5 public void send_email(String msg,Message message,Channel channel){
6 System.out.println("receive message is:"+msg);
7 }
8
9 }