《RabbitMQ 消息中间件》RabbitMQ详解
前言
安装完RabbitMQ之后,我们就来了解一下RabbitMQ的管理命令。
详情
第一条命令:帮助命令
rabbitmqctl help
第二条命令:插件管理
rabbitmq-plugins
list 插件列表,enable启用插件,disable禁用插件,set重置插件。
管理界面
RabbitMQ使用的是AMQP的协议。
RabbitMQ的7中消息模型:
第一种消息模型-直连(SpringBoot演练)
加入依赖Jar包(Gradle)
compile('org.springframework.boot:spring-boot-starter-amqp')
封装类:
import com.rabbitmq.client.Connection; import com.rabbitmq.client.ConnectionFactory; import java.io.IOException; import java.util.concurrent.TimeoutException; public class MqUtil { public static Connection getConnection() throws IOException, TimeoutException { // 创建rabbitmq的连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); // 设置主机IP connectionFactory.setHost("47.105.72.224"); // 设置端口 connectionFactory.setPort(5672); // 设置主机名 connectionFactory.setVirtualHost("/my"); // 设置访问虚拟主机的IP和端口 connectionFactory.setUsername("rabbitmq"); connectionFactory.setPassword("rabbitmq"); return connectionFactory.newConnection(); } }
发送消息代码:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.MessageProperties; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class Provoder { public void sendMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("hello",true,false,false,null); /** * 发布消息 * 参数1:交互机 * 参数2:队列 * 参数3:消息额外设置(MessageProperties.MINIMAL_PERSISTENT_BASIC) * 参数4:具体内容 */ channel.basicPublish("","hello",MessageProperties.MINIMAL_PERSISTENT_BASIC,"Hello World!".getBytes()); channel.close(); } }
测试Controller
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.concurrent.TimeoutException; @RestController public class TestController3 { @Autowired SendMqProvoder sendMqProvoder; @RequestMapping("/rabbitmq.do") public String event() throws IOException, TimeoutException { sendMqProvoder.sendMessage(); return "success"; } }
效果展示:
网页访问后的RabbitMq消息情况,rabbitmq的管理界面中就出现了Hello队列,并且有两条未消费的消息。
消费者:
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class Customer { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("hello",true,false,false,null); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ channel.basicConsume("hello",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)); } }); //消费者需要一直监听消息。 //channel.close(); //connection.close(); } }
测试类:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.io.IOException; import java.util.concurrent.TimeoutException; @RestController public class TestController3 { @Autowired Provoder provoder; @Autowired Customer customer; @RequestMapping("/rabbitmq.do") public String event() throws IOException, TimeoutException { provoder.sendMessage(); return "success"; } @RequestMapping("/receive.do") public String receive() throws IOException, TimeoutException { customer.receiveMessage(); return "success"; } }
接收消息的结果:
第二种消息模型-work queue(SpringBoot演练)
1. 支持多个消费者,一个消息只能被消费一次。
2. 消息被多个消费者平均分配消费。
生产者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.MessageProperties; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class WorkProvoder { public void sendMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("work",true,false,false,null); /** * 发布消息 * 参数1:交互机 * 参数2:队列 * 参数3:消息额外设置(MessageProperties.MINIMAL_PERSISTENT_BASIC) * 参数4:具体内容 */ for (int i = 0; i < 100; i++) { channel.basicPublish("","work",MessageProperties.MINIMAL_PERSISTENT_BASIC,(i+"Hello Work Queue!").getBytes()); } channel.close(); connection.close(); } }
消费者1
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class WorkCustomer { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("work",true,false,false,null); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ channel.basicConsume("work",true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{ System.out.println("消费者-1:"+new String(body)); } }); } }
消费者2
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class WorkCustomer2 { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("work",true,false,false,null); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ channel.basicConsume("work",true,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{ System.out.println("消费者-2:"+new String(body)); } }); } }
测试类:
import java.io.IOException; import java.util.concurrent.TimeoutException; @RestController public class TestController4 { @Autowired WorkProvoder workProvoder; @Autowired WorkCustomer customer1; @Autowired WorkCustomer2 customer2; @RequestMapping("/workSend.do") public String workSend() throws IOException, TimeoutException { workProvoder.sendMessage(); return "success"; } @RequestMapping("/workReceive1.do") public String workReceive1() throws IOException, TimeoutException { customer1.receiveMessage(); return "success"; } @RequestMapping("/workReceive2.do") public String workReceive2() throws IOException, TimeoutException { customer2.receiveMessage(); return "success"; } }
运行结果:
现在的模式会出现消息丢失,平均分配的问题。
修改一下代码,加入手动确认和每次消费一条消息,来保证消息的完整。
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class WorkCustomer2 { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 参数1:队列名称,不存在自动创建 * 参数2:是否持久化队列 * 参数3:exclusive 是否独占队列(当前队列只能被当前连接使用) * 参数4:autoDelete 是否需要消费完之后,删除参数。 * 参数5:额外信息 */ channel.queueDeclare("work",true,false,false,null); // 一次处理一个 channel.basicQos(1); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ channel.basicConsume("work",false,new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException{ System.out.println("消费者-2:"+new String(body)); // 传一个标记确认消息。 channel.basicAck(envelope.getDeliveryTag(),false); } }); } }
第三种消息模型-广播(fanout)
生产者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.MessageProperties; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class FanProvider { public void sendMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 设置交换机 * 参数1:交换机的名字(自取)。 * 参数2:交换机类型:fanout:就是广播。 */ channel.exchangeDeclare("exchange1","fanout"); /** * 发布消息 * 参数1:交互机 * 参数2:队列 * 参数3:消息额外设置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化) * 参数4:具体内容 */ for (int i = 0; i < 100; i++) { channel.basicPublish("exchange1","",MessageProperties.MINIMAL_PERSISTENT_BASIC,(i+"Hello Fan Queue!").getBytes()); } channel.close(); connection.close(); } }
消费者:
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class FanCustomer { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 绑定交换机 * 参数1:交换机名 * 参数2:交换机类型 */ channel.exchangeDeclare("exchange1","fanout"); /********* 创建一个临时队列 *********/ String queue = channel.queueDeclare().getQueue(); /********* 绑定交换机和队列 *********/ channel.queueBind(queue,"exchange1",""); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ 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("消费者-1:"+new String(body)); } }); } }
运行结果:
两个消费者都能消费到消息。
第四种消息模型-订阅(Routing):实现不同的人去订阅不同的消息。
生产者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.MessageProperties; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class DirectProvider { public void sendMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 设置交换机 * 参数1:交换机的名字(自取)。 * 参数2:交换机类型:direct:路由模式。 */ channel.exchangeDeclare("logs","direct"); /******* 设置路由key ******/ String routingKey = "info"; String routingKey1 = "warn"; /** * 发布消息 * 参数1:交互机 * 参数2:队列 * 参数3:消息额外设置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化) * 参数4:具体内容 */ channel.basicPublish("logs",routingKey,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey+":Hello Fan Queue!").getBytes()); channel.basicPublish("logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Fan Queue!").getBytes()); channel.basicPublish("logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Fan Queue!").getBytes()); channel.close(); connection.close(); } }
消费者:
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class DirectCustomer { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 绑定交换机 * 参数1:交换机名 * 参数2:交换机类型 */ channel.exchangeDeclare("logs","direct"); /********* 创建一个临时队列 *********/ String queue = channel.queueDeclare().getQueue(); /********* 绑定交换机和队列和路由key *********/ channel.queueBind(queue,"logs","info"); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ 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("消费者-1:"+new String(body)); } }); } }
运行结果:
第五种消息模型-动态订阅(Topic):实现不同的人去订阅不同的消息,支持设置通配符。
* 匹配任意一个单词。 *.rabbitmq
# 匹配多个任意单词。 user.#
生产者:
import com.rabbitmq.client.Channel; import com.rabbitmq.client.Connection; import com.rabbitmq.client.MessageProperties; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class TopicProvider { public void sendMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 设置交换机 * 参数1:交换机的名字(自取)。 * 参数2:交换机类型:topic:动态路由模式。 */ channel.exchangeDeclare("topic_logs","topic"); /******* 设置路由key ******/ String routingKey = "user.info"; String routingKey1 = "user.warn"; /** * 发布消息 * 参数1:交互机 * 参数2:队列 * 参数3:消息额外设置(MessageProperties.MINIMAL_PERSISTENT_BASIC:消息持久化) * 参数4:具体内容 */ channel.basicPublish("topic_logs",routingKey,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey+":Hello Topic Queue!").getBytes()); channel.basicPublish("topic_logs",routingKey1,MessageProperties.MINIMAL_PERSISTENT_BASIC,(routingKey1+":Hello Topic Queue!").getBytes()); channel.close(); connection.close(); } }
消费者1:
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class TopicCustomer { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 绑定交换机 * 参数1:交换机名 * 参数2:交换机类型 */ channel.exchangeDeclare("topic_logs","topic"); /********* 创建一个临时队列 *********/ String queue = channel.queueDeclare().getQueue(); /********* 绑定交换机和队列和路由key *********/ channel.queueBind(queue,"topic_logs","*.info"); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ 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("消费者-1:"+new String(body)); } }); } }
消费者2:
import com.rabbitmq.client.*; import org.springframework.stereotype.Component; import java.io.IOException; import java.util.concurrent.TimeoutException; @Component public class TopicCustomer1 { public void receiveMessage() throws IOException, TimeoutException { Connection connection = MqUtil.getConnection(); // 创建MQ渠道 Channel channel = connection.createChannel(); /** * 绑定交换机 * 参数1:交换机名 * 参数2:交换机类型 */ channel.exchangeDeclare("topic_logs","topic"); /********* 创建一个临时队列 *********/ String queue = channel.queueDeclare().getQueue(); /********* 绑定交换机和队列和路由key *********/ channel.queueBind(queue,"topic_logs","user.*"); /** * queue 队列名称 * autoAck 开启消息自动确认机制 * callback 消费消息的回调函数 */ 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("消费者-2:"+new String(body)); } }); } }
运行结果:
第六种通道协议(RPC)略。
第7种消息模型-发布确认模型 略。
总结
1. rabbitmq 目前支持6中消息模型,一种通道协议。
2. rabbitmq 消息支持队列和消息持久化
3. 结构:生产者,消费者,队列,交换机,虚拟主机。
4. 消息确认机制,默认平均分配给每一个消费者。