介绍
消息队列场景
- 任务异步处理
将不需要同步处理的且耗时长的操作由消息队列通知消息接收方进行异步处理,提高了应用程序的响应时间
- 应用程序解耦合
MQ相当于一个中介,生产方通过MQ与消费方交互,它将应用程序进行解耦和
AMQP协议
旨在从协议层定义消息通信数据的标准格式,为的就是解决MQ市场上协议不统一的问题
JMS
Java消息服务,是java提供的一套消息服务API标准
类似JDBC,JMS是java语言专属的消息服务标准,在api层定义标准
AMQP是协议层定义的标准,是跨语言的
基本工作原理
工作流程
- 发送消息
- 生产者和Broker建立TCP连接
- 生产者和Broker建立通道
- 生产者通过通道消息发送Broker,由Exchange将消息进行转发
- Exchange将消息转发到指定的Queue
- 接收消息
- 消费者和Broker建立TCP连接
- 消费者和Broker建立通道
- 消费者监听指定的Queue
- 当有消息到达Queue时Broker默认将消息推送给消费者
- 消费者接受到消息
安装
docker
- 命令
docker pull rabbitmq:management
docker run -d -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 --name rmq rabbitmq:management
- 管理窗口:15672
Helloworld
配置
- pom.xml
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>4.0.3</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
<version>2.2.6.RELEASE</version>
</dependency>
发送端流程
- 创建连接
- 创建通道
- 声明队列
- 发送消息
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
Channel channel = null;
try{
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
channel = connection.createChannel();
//监听队列
// 声明队列,交换机默认
//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
* String queue 队列名称
* boolean durable 是否持久化,即mq重启是否还在
* boolean exclusive 是否独占连接,即连接关闭时队列自动删除,true是删除
* boolean autoDelete 是否自动删除,队列不再使用时删除,与exclusive都设为true就可以实现临时队列
* Map<String, Object> arguments 参数,可以设置一个队列的扩展参数,如存活时间
*/
String queue = "Hello World !";
channel.queueDeclare(queue,true,false,false,null);
//发送消息,如果接收端没有接受会存在消息队列里,通过15672端口查看
//参数:basicPublish(String exchange, String routingKey, AMQP.BasicProperties props, byte[] body)
/**
* String exchange 交换机,不指定将使用默认的
* String routingKey 路由key,交换机根据路由key来将校级转发到指定的队列
* 如果使用默认交换机,routingKey设置为队列名称
* props 消息的属性
* body 消息内容
*/
String message = "hello RabbitMQ !";
channel.basicPublish("",queue,null,message.getBytes());
System.out.println("send");
}catch(Exception e){
e.printStackTrace();
}finally {
channel.close();
connection.close();
}
接收端流程
- 创建连接
- 创建通道
- 声明队列
- 监听队列
- 接收消息
- ack回复
//通过连接工厂创建新的连接和mq建立连接
ConnectionFactory connectionFactory = new ConnectionFactory();
connectionFactory.setHost("192.168.199.132");
connectionFactory.setPort(5672);
connectionFactory.setUsername("guest");
connectionFactory.setPassword("guest");
//设置虚拟机,一个mq服务可以设置多个虚拟机,每个虚拟机相当于一个独立的mq
connectionFactory.setVirtualHost("/");
Connection connection = null;
//建立新连接
connection = connectionFactory.newConnection();
//创建会话通道,生产者和mq服务所有通信都在channel通道中完成
Channel channel = connection.createChannel();
//监听队列
// 声明队列,交换机默认
//queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments)
/**
* String queue 队列名称
* boolean durable 是否持久化,即mq重启是否还在
* boolean exclusive 是否独占连接,即连接关闭时队列自动删除,true是删除
* boolean autoDelete 是否自动删除,队列不再使用时删除,与exclusive都设为true就可以实现临时队列
* Map<String, Object> arguments 参数,可以设置一个队列的扩展参数,如存活时间
*/
String queue = "Hello World !";
channel.queueDeclare(queue,true,false,false,null);
//开始不同
// 实现消费方法
DefaultConsumer defaultConsumer = new DefaultConsumer(channel){
/**
*
* @param consumerTag 消费者标签,用来标识消费者,可在监听队列时设置channel.basicConsume
* @param envelope 信封,通过envelope获取
* @param properties 消息属性
* @param body 消息内容
* @throws IOException
*/
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
//交换机
String exchange = envelope.getExchange();
//消息id,mq在channel中用来表示消息的id,可用于确认消息已接收
long deliveryTag = envelope.getDeliveryTag();
//消息内容
String message = new String(body, "utf-8");
System.out.println(message);
}
};
//basicConsume(String queue, boolean autoAck, Consumer callback)
/**
* String queue 队列名称
* boolean autoAck 自动回复,当消费者接收到消息后要告诉mq消息已接收,设置true表示自动回复mq,false要编程实现回复
* Consumer callback 消费方法,当消费者接受到消息要执行的方法
*/
//监听队列
channel.basicConsume(queue,true,defaultConsumer);