RabbitMQ(一):入门
-
RabbitMQ是一个消息中间件,相当于一个中转站;用于接收、存储、转发消息数据
-
RabbitMQ的作用:
- 流量消峰:当服务器处理不了过多的请求时,在进入服务器之前先进入MQ,MQ会对请求做排序,防止服务器宕机
- 应用解耦:当系统的某个子系统出现故障无法正常工作时,该子系统要处理的内存会被缓存到消息队列中,当该子系统正常后再继续处理;提高用户体验,避免用户再次请求
- 异步处理:有些服务间的调用是异步的,当某个服务在工作时,其他服务不能调用该服务,可使用消息队列来进行监听管理调用
-
MQ的核心:
- 生产者:产生数据发送消息的模块
- 交换机:用于接收生产者的消息、将消息推送到队列中、处理消息
- 队列:相当于消息缓存区
- 消费者:用于接收消息的模块
- 一个应用程序既可以是生产者,又可以是消费者;比如一个通讯工具,既可以发送信息,也可以接收信息
-
CentOS安装RabbitMQ:
vi /etc/yum.repos.d/rabbitmq_erlang.repo # 配置 centos8 Erlang的下载源 yum install erlang # 安装Erlang # 下载rabbitmq的rpm,传输centos yum install socat # 安装socat依赖 rpm -ivh rabbitmq-server-3.8.1-1.el8.noarch.rpm # 安装rabbitmq rabbitmq-plugins enable rabbitmq_management # 安装web插件 vi /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.1/ebin/rabbit.app # 设置可远程访问 firewall-cmd --add-port=15672/tcp --permanent # 开放端口 firewall-cmd --add-port=5672/tcp --permanent # 开放端口 firewall-cmd --reload # 使防火墙生效 rabbitmq-server -detached # 后台启动 http://ip:15672/ # 浏览器访问rabbitmq页面 sudo rabbitmq-server # 控制台启动
详情查看:https://www.jianshu.com/p/8aece9517533
RabbitMQ启动时报错:error: node with name "rabbit" already running on "bogon"
错误原因:后台进程被占用
解决方案:
ps -ef|grep rabbitmq # 查看进程 kill <进程id> # 杀死进程 # 再次启动
启动时报错:Warning: PID file not written; -detached was passed.
错误原因:警告信息,不影响
- 创建账号:
rabbitmqctl add_user <用户名> <密码> rabbitmqctl set_user_tags <用户名> administrator # 为当前账号设置超级管理员角色权限 rabbitmqctl set_permissions -p "/" admin ".*" ".*" ".*" # 表示admin用户在"/"这个主机中具有配置、读、写权限 # 设置格式:set_permissions [-p <vhostpath>] <user> <conf> <write> <read> "/"表示主机路径,也可自定义其他路径 rabbitmqctl list_users # 查看所有账户和对应的角色 # 此时可在window系统的浏览器中访问Linux的rabbitmq,输入创建的账号 rabbitmqctl stop_app # 关闭 rabbitmqctl reset # 重启 rabbitmqctl start_app # 启动
- window10上RabbitMQ环境配置:
1. 安装erlang,配置环境变量 i. ERLANG_HOME = 安装路径 ii. path中添加:%ERLANG_HOME%\bin iii. cmd输入erl查看版本验证是否安装成功 2. 安装RabbitMQ i. 进入rabbitMQ安装路径的sbin文件夹,打开cmd ii. rabbitmq-plugins enable rabbitmq_management # 安装可视化插件 iii. 浏览器输入:http://localhost:15672/ # 访问RabbitMQ页面,用户名、密码为guest 3. RabbitMQ常用命令: i. net start RabbitMQ # 启动 ii. net stop RabbitMQ # 停止 iii. rabbitmqctl status # 查看状态
详情查看:https://blog.csdn.net/qq_39915083/article/details/107034747
-
消息传递入门案例:
- 导入所需依赖,新建一个类作为消息生产者,定义一个队列,创建连接工厂,创建连接,生成队列,发送消息
- 运行后发现massage发送到了rabbitmq,可在rabbitmq中查看
- 新建一个类作为消息消费者,创建连接工厂,创建连接,接收rabbitmq中的消息,可指定回调方法用于处理成功和失败的情况
-
工作队列是为了避免立即执行大量任务,生产者发送大量消息到rabbitmq的队列,队列将消息缓存、排序、分发到不同的消费者
-
轮训分发消息案例:
- 抽取连接的步骤抽取到一个工具类,新建生产者类和消费者类,消费者类启动多个线程
- 当生产者发送消息时,消息被多个线程(消费者类)平均接收
-
消息应答:消费者接收消息并处理完成后,会告诉rabbitmq,这时rabbitmq会把消息删除了
-
自动应答:建议不要使用,若消费者不能即使处理消息,会导致消息丢失
-
手动应答:自定义应答方法
Channel.basicAck:用于肯定确认,RabbitMQ 已知道该消息并且成功的处理消息,可以将其丢弃了 Channel.basicNack:用于否定确认 Channel.basicReject:用于否定确认,与 Channel.basicNack 相比少一个参数Multiple Multiple为true表示批量应答,即消费者同时处理多个消息,其中一个消息处理完成后,则告诉rabbitmq全处理完了 Multiple为false表示不批量应答,即消费者同时处理多个消息,其中一个消息处理完成后,则告诉rabbitmq处理完的那个消息
-
消息自动重新入队,消息会保存在rabbitmq中,直到被消费者处理后、应答后才会删除,当消费者出现异常或者宕机时导致处理失败,rabbitmq会将消息分发给另一个消费者处理
-
手动应答 + 自动重新入队案例:
- 新建一个类作为消息生产者
- 新建一个类作为消息消费者:让该线程睡眠1秒后打印出接收到消息,再新建一个消息消费者类:让该线程睡眠30秒后打印出接收的消息,是为了模拟处理复杂的消息;在两个消费者类中设置为手动应答
- 测试:生产者发送消息,两个消费者轮训接收消息,当第二个消费者在接收消息时突然关闭服务,这时消息没处理完,又被自动分配到第一个消费者处理
-
队列持久化:
在生产者类中设置
消息应答机制确保了rabbitmq正常的情况下消息不会丢失
队列设置持久化后,rabbitmq即使宕机或重启后,该队列中的消息依然存在于rabbitmq
若将一个未持久化的队列设置成持久化后,重启会报错,这时由于该队列已经在rabbitmq中存在,在rabbitmq页面删除该队列,再重启线程,
这时该队列在rabbitmq显示的持久化
在rabbitmq控制台可查看设置了队列持久化的消息带有一个大写的D -
消息持久化:
rabbitmq中的队列持久化了,发送的消息也需设置持久化,否则rabbitmq重启后,队列存在,队列中的消息丢失了
在生产者类中发送消息时添加:MessageProperties.PERSISTENT_TEXT_PLAIN -
不公平分发:
即处理速度快的消费者多分配消息,确保消费者不会出现空闲状态
在消费者类添加:channel.basicQos(1) # 默认为0表示轮训,1表示不公平分发 -
预取值:
即指定消费者处理多少消息
在消费者类添加:channel.basicQos(5) # 这就表示分发5条,表示该消费者类会一直堆着5条,当该消费者类处理了1条,只剩4条时,又会接收1条消息 -
参考代码
// 连接rabbitmq工具类 public class RabbitMqUtils { //得到一个连接的 channel public static Channel getChannel() throws Exception{ //创建一个连接工厂 ConnectionFactory factory = new ConnectionFactory(); factory.setHost("182.92.234.71"); // rabbitmq的ip和账户密码 factory.setUsername("admin"); factory.setPassword("123"); Connection connection = factory.newConnection(); // 创建连接 Channel channel = connection.createChannel(); return channel; // 返回信道 } } // 生产者 public class Task02 { private static final String TASK_QUEUE_NAME = "ack_queue"; // 创建队列 public static void main(String[] argv) throws Exception { try (Channel channel = RabbitMqUtils.getChannel()){ // 获取连接rabbitmq /** * 生成一个队列 * 1.队列名称 * 2.队列持久化 * 3.该队列是否只供一个消费者进行消费 是否进行共享 true 可以多个消费者消费 * 4.是否自动删除 最后一个消费者端开连接以后 该队列是否自动删除 true 自动删除 * 5.其他参数 */ channel.queueDeclare(TASK_QUEUE_NAME, false, false, false, null); Scanner sc = new Scanner(System.in); System.out.println("请输入信息"); while (sc.hasNext()) { String message = sc.nextLine(); // 从控制台获取消息 /** * 发送一个消息 * 1.发送到那个交换机 * 2.路由的 key 是哪个 * 3.消息持久化 * 4.发送消息的消息体 */ channel.basicPublish("", TASK_QUEUE_NAME, MessageProperties.PERSISTENT_TEXT_PLAIN, message.getBytes("UTF-8")); // 发送消息到rabbitmq System.out.println("生产者发出消息" + message); } } } } // 消费者 public class Work03 { private static final String ACK_QUEUE_NAME="ack_queue"; public static void main(String[] args) throws Exception{ Channel channel = RabbitMqUtils.getChannel(); System.out.println("C1 等待接收消息处理时间较短"); //消息消费的时候如何处理消息 DeliverCallback deliverCallback=(consumerTag,delivery)->{ String message= new String(delivery.getBody()); SleepUtils.sleep(1); System.out.println("接收到消息:"+message); /** * 消息应答的具体处理: * 1.消息标记 tag * 2.是否批量应答未应答消息 */ channel.basicAck(delivery.getEnvelope().getDeliveryTag(),false); }; // 不公平分发 //channel.basicQos(1) // 预取值 channel.basicQos(5) //采用手动应答 boolean autoAck=false; /** * 1.队列名称 * 2.是否手动应答 * 3.接收成功的处理 * 4.接收消息失败的回调 */ channel.basicConsume(ACK_QUEUE_NAME,autoAck,deliverCallback,(consumerTag)->{ System.out.println(consumerTag+"消费者取消消费接口回调逻辑"); }); } } // 模拟处理时间的睡眠工具类 public class SleepUtils { public static void sleep(int second){ try { Thread.sleep(1000*second); } catch (InterruptedException _ignored){ Thread.currentThread().interrupt(); } } }
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术