Rabbitmq 消息队列 基于AMQP协议的消息队列 就是将本来一次性完成的,耗时的,需要等待的操作分离出来 -形成一个单独的模块,就是消息队列 -例子: 普通方式: A要寄信给B->A要去邮局-要等待接待-要填写具体信息-要每天去邮局查看 消息队列: A把信和具体信息给C-C去寄信-A等待查看结果即可 -目的: 减少中间流程的操作时间,为了更加快捷方便
Rabbitmq 应用场景 1) 异步处理 串行方式: 一步一步执行流程,耗时点多,不便捷 并行方式: 同时执行多个流程,耗时点略多,非常不便捷 使用消息队列: 讲整个模块交给消息队列处理,没有耗时,非常便捷--类似于面向对象 2)应用解耦 将两个本来绑在一起的模块解绑,各自运行各自的,互不干扰 -举例: 订单系统和库存系统 一般来说,下订单然后查库存,根据库存结果来判断订单是否成功 -耦合性高 使用消息队列,将订单和库存分开,订单进来后通过消息队列发消息给库存系统,然后订单系统直接返回订单结果即可 通过消息队列,库存系统在完成是否有库存的结果 -完成两个系统的解耦 3)流量削峰 在流量比较大的场景中,例如秒杀,抢购中,因为流量过大,经常会造成应用崩溃,所以在秒杀前将所有信息全部进入消息队列,经过消息队列的缓冲,在进入应用执行,超出部分另作打算 4)日志处理 每次进行操作时都要记录日志,就会造成日志的大量写入,从而造成数据库压力,所以将日志写进消息队列,在做后续处理,减少压力,提高效率
Rabbitmq用法的一种: 一个生产者(发送消息)-一个队列-一个消费者(接收消息) 发送流程 1)建立连接--对消息队列rabbitmq的链接 创建连接工厂 设置连接的参数 url:主机地址 username:连接用户名 password:连接密码 port:链接端口号 从工厂中获取一个链接 2)建立信道 通过获取的连接建立信道 通过信道设置交换机和队列的信息--当没有设置的队列时,创建一个该设置的队列 队列的名称 是否持久化 是否独享/排外的 是否自动删除--消费者接收信息后消息队列自动去除消息 额外属性--最大存储量... 3)通过信道发送到交换机,队列 创建要发送的内容 发送参数设置 交换机名称 若交换机无,填写队列名称 若交换机有,填写路由键--约束条件 额外属性设置 消息 接收流程 1)2)相同 3)监听队列
package com.szr.producer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
/**
* 生产者发送消息
*/
public class Producer {
//声明消息队列名称-被final修饰
private static final String QUEUE_NAME = "hello-szr" ;
/**
* 利用main方法模拟发送消息
* @param args
*/
public static void main(String[] args) throws IOException, TimeoutException {
//1.建立连接-创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接参数
connectionFactory.setHost("10.35.164.33");//建立主机连接地址
connectionFactory.setUsername("guest");//连接队列用户名
connectionFactory.setPassword("guest");//连接队列密码
connectionFactory.setVirtualHost("/");//连接主机名称
connectionFactory.setPort(5672);//连接端口号
//2.建立信道-从工厂中获取一个连接
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//使用信道设置交换机和队列
channel.queueDeclare(
QUEUE_NAME,//队列名称
true,//是否持久化
false,//是否独享/排外
true,//是否自动删除
null//额外属性
);
//3)通过信道发送-创建信息
String message = "这是我的第一个消息队列" ;
channel.basicPublish(
"",//没有交换机名称
QUEUE_NAME,//没有交换机名称,所以填写队列名称
null,//没有交换机名称,所以没有约定
message.getBytes(StandardCharsets.UTF_8)//消息
);
}
}
package com.szr.consumer;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DeliverCallback;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeoutException;
/**
* 消费者接收消息
*/
public class Consumer {
//声明消息队列名称-被final修饰
private static final String QUEUE_NAME = "hello-szr" ;
/**
* 利用main方法模拟接收消息
* @param args
*/
public static void main(String[] args) throws IOException, TimeoutException {
//1.建立连接-创建连接工厂
ConnectionFactory connectionFactory = new ConnectionFactory();
//设置连接参数
connectionFactory.setHost("10.35.164.33");//建立主机连接地址
connectionFactory.setUsername("guest");//连接队列用户名
connectionFactory.setPassword("guest");//连接队列密码
connectionFactory.setVirtualHost("/");//连接主机名称
connectionFactory.setPort(5672);//连接端口号
//2.建立信道-从工厂中获取一个连接
Connection connection = connectionFactory.newConnection();
Channel channel = connection.createChannel();
//使用信道设置交换机和队列
channel.queueDeclare(
QUEUE_NAME,//队列名称
true,//是否持久化
false,//是否独享/排外
true,//是否自动删除
null//额外属性
);
//3.监听队列
DeliverCallback deliverCallback = (consumerTag, delivery) -> {
//1.consumerTag当前的消费的唯一标识。2.delivery 消息本身
String message = new String(delivery.getBody(), "UTF-8");
System.out.println(" [x] Received : " + message);
};
//信道接收
channel.basicConsume(
QUEUE_NAME,//监听的队列名称
true,//是否自动确认-没有报异常,自动确认接收,从消息队列中删除
deliverCallback,//回调方法
consumerTag -> { }
);
}
}
package com.szr.config;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 消息队列配置类
*/
@Configuration
public class RabbitConfig {
/**
* 将这个对象交给spring管理起来
* @return
*/
@Bean
public Queue queue(){
//返回得到的队列
return new Queue(
"hello-zhongli" //队列名称
);
}
}
package com.szr;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 测试类
*/
@SpringBootTest
public class sendTest {
//注入rabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate ;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(
"",//交换机为空
"hello-zhongli",//队列名称
"你好,钟离!"//发送内容
);
}
}
package com.szr.listen;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 监听队列类
*/
@Component //将这个类交给spring管理
public class ListenMessage {
@RabbitListener(queues = "hello-zhongli") //代表要监听哪一个队列
public void listenMessage(String message){
System.out.println("message = " + message);
}
}
WorkQueues 工作队列 一个生产者(发送消息)-一个队列-多个消费者(多个接收消息) 结论: 会将每条消息平均的发给每个消费者,并且遵循轮换规则
RabbitMQ-Publish-Subscribe 发布订阅模式 一个发送者-一个交换机-多个队列-多个消费者 没有规则,给全部的队列发送了内容
RabbitMQ-Routing 路由模式 一个发送者-一个交换机,拥有绑定规则-多个队列-多个消费者 好,但是太麻烦,于是有了通配符
RabbitMQ-Topics 通配符模式 一个发送者-一个交换机,规则加入通配符,更加多样性-多个队列-多个消费者
package com.szr.listen;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* 监听队列类
*/
@Component //将这个类交给spring管理
public class ListenMessage {
@RabbitListener(queues = "hello-zhongli") //代表要监听哪一个队列
public void listenMessage(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-zhongli")
public void listenliyueFanout(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-leishen")
public void listendaoqiFanout(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-Routing-zhongli")
public void listenliyueRouting(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-Routing-leishen")
public void listendaoqiRouting(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-Topic-zhongli")
public void listenliyueTopic(String message){
System.out.println("message = " + message);
}
@RabbitListener(queues = "Queue-Topic-leishen")
public void listendaoqiTopic(String message){
System.out.println("message = " + message);
}
}
package com.szr.config;
import org.springframework.amqp.core.*;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RabbitTopicConfig {
/**
* 第一个消息队列,交给spring管理起来,使用@Qualifier来注入
* @return
*/
@Bean(name = "Topic-liyue")
public Queue liyue(){
return new Queue("Queue-Topic-zhongli");
}
/**
* 第二个消息队列,交给spring管理
* @return
*/
@Bean(name = "Topic-daoqi")
public Queue daoqi(){
return new Queue("Queue-Topic-leishen");
}
/**
* 发布订阅模式专门的交换机
* @return 返回交换机对象
*/
@Bean
public TopicExchange topicExchange(){
return new TopicExchange("Topic-liyue-daoqi-fanout");
}
/**
* 绑定关系,绑定交换机和队列
* @param queue 通过名称注入的队列-liyue
* @param topicExchange 只有一个这种类型的交换机
* @return
*/
@Bean
Binding bindTopicLiyueExchange(@Qualifier("Topic-liyue")Queue queue, TopicExchange topicExchange){
//返回一个声明关系-将注入的队列绑定到交换机上
return BindingBuilder.bind(queue).to(topicExchange).with("#.liyue");
}
/**
* 绑定关系,绑定交换机和队列
* @param queue 通过名称注入的队列-daoqi
* @param topicExchange 只有一个这种类型的交换机
* @return
*/
@Bean
Binding bindTopicDaoqiExchange(@Qualifier("Topic-daoqi")Queue queue,TopicExchange topicExchange){
//返回一个声明关系-将注入的队列绑定到交换机上
return BindingBuilder.bind(queue).to(topicExchange).with("daoqi.#");
}
}
package com.szr;
import org.junit.jupiter.api.Test;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 测试类
*/
@SpringBootTest
public class sendTest {
//注入rabbitTemplate
@Autowired
private RabbitTemplate rabbitTemplate ;
@Test
public void testSend(){
rabbitTemplate.convertAndSend(
"",//交换机为空
"hello-zhongli",//队列名称
"你好,钟离!"//发送内容
);
}
@Test
public void testFanout(){
rabbitTemplate.convertAndSend(
"publish-liyue-daoqi-fanout",//交换机名称
"",//没有约定条件
"这是庆祝凌华和申鹤复刻"//要发送的消息
);
}
@Test
public void testRouting(){
rabbitTemplate.convertAndSend(
"Routing-liyue-daoqi-fanout",//交换机名称
"liyue",
"这是庆祝凌华和申鹤复刻"//要发送的消息
);
}
@Test
public void testRouting1(){
rabbitTemplate.convertAndSend(
"Routing-liyue-daoqi-fanout",//交换机名称
"daoqi",
"这是庆祝凌华和申鹤复刻"//要发送的消息
);
}
@Test
public void testTopic(){
rabbitTemplate.convertAndSend(
"Topic-liyue-daoqi-fanout",//交换机名称
"daoqi.liyue",
"这是庆祝凌华和申鹤复刻"//要发送的消息
);
}
}