惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

I
InfoQ
Engineering at Meta
Engineering at Meta
GbyAI
GbyAI
A
About on SuperTechFans
aimingoo的专栏
aimingoo的专栏
P
Privacy & Cybersecurity Law Blog
Cloudbric
Cloudbric
云风的 BLOG
云风的 BLOG
S
Secure Thoughts
Stack Overflow Blog
Stack Overflow Blog
G
Google Developers Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
S
SegmentFault 最新的问题
博客园 - Franky
T
Tenable Blog
T
The Blog of Author Tim Ferriss
博客园 - 三生石上(FineUI控件)
V
V2EX
Recent Commits to openclaw:main
Recent Commits to openclaw:main
T
Troy Hunt's Blog
罗磊的独立博客
WordPress大学
WordPress大学
SecWiki News
SecWiki News
The Cloudflare Blog
S
Securelist
小众软件
小众软件
Schneier on Security
Schneier on Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
博客园 - 叶小钗
Google Online Security Blog
Google Online Security Blog
Forbes - Security
Forbes - Security
阮一峰的网络日志
阮一峰的网络日志
量子位
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
W
WeLiveSecurity
A
Arctic Wolf
大猫的无限游戏
大猫的无限游戏
The Last Watchdog
The Last Watchdog
C
Cybersecurity and Infrastructure Security Agency CISA
宝玉的分享
宝玉的分享
MyScale Blog
MyScale Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
博客园 - 司徒正美
Vercel News
Vercel News
H
Help Net Security
Y
Y Combinator Blog
PCI Perspectives
PCI Perspectives
L
LINUX DO - 最新话题
H
Hackread – Cybersecurity News, Data Breaches, AI and More

博客园 - cacard

Android中的内部类引起的内存泄露 Android的消息机制: Message/MessageQueue/Handler/Looper ArrayList/Vector的原理、线程安全和迭代Fail-Fast JVM中的Stack和Frame JVM中的垃圾收集算法和Heap分区简记 无锁编程以及CAS 简述Java内存模型的由来、概念及语义 MQTT协议简记 RabbitMQ 入门 [Java] LinkedHashMap 源码简要分析 [Java] HashMap 源码简要分析 [Java] Hashtable 源码简要分析 CentOS 安装 Hadoop 手记 树莓派(RespberryPi)安装手记 RPC简述 [C++] const与重载 [C++] 左值、右值、右值引用 [C++] 引用 Java线程池 / Executor / Callable / Future
RabbitMQ的工作队列和路由
cacard · 2014-03-14 · via 博客园 - cacard

工作队列:Working Queue

工作队列这个概念与简单的发送/接收消息的区别就是:接收方接收到消息后,可能需要花费更长的时间来处理消息,这个过程就叫一个Work/Task。

几个概念

分配:多个接收端接收同一个Queue时,如何分配?

消息确认:Server端如何确定接收方的Work已经对消息进行了完整的处理?

消息持久化:发送方、服务端Queue如何对未处理的消息进行磁盘持久化?

Round-robin分配

多个接收端接收同一个Queue时,采用了Round-robin分配算法,即轮叫调度——依次分配给各个接收方。

消息确认

默认开启了消息确认(接收方接收到消息后,立即向服务器发回确认)。消息接收方处理完消息后,向服务器发送消息确认,服务器再删除该消息。

对于耗时的work,可以先关闭自动消息确认,在work完成后,再手动发回确认。

channel.basicConsume("hello",false/*关闭自动消息确认*/,consumer);

// ...work完成后

channel.basicAck(delivery.getEnvelope().getDeliveryTag(), false);

持久化

1. Server端的Queue持久化

注意的是,如果已经声明了同名非持久化的Queue,则再次声明无效。

发送方和接收方都需要指定该参数。

boolean durable = true;

channel.queueDeclare("task_queue", durable, false, false, null); 

2. Message持久化

channel.basicPublish("", "task_queue", MessageProperties.PERSISTENT_TEXT_PLAIN,message.getBytes());

负载分配

为了解决各个接收端工作量相差太大的问题(有的一直busy,有的空闲比较多),突破Round-robin。

int prefetchCount = 1;

channel.basicQos(prefetchCount);

意思为,最多为当前接收方发送一条消息。如果接收方还未处理完毕消息,还没有回发确认,就不要再给他分配消息了,应该把当前消息分配给其它空闲接收方。

使用类型为direct的exchange,发送特定关键词(RoutingKey)的消息给订阅该关键词的Queue。

场景示例:消息发送方发送了类型为[error][info]的两种消息,写磁盘的消息接受者只接受error类型的消息,Console打印的接收两者。

(上图采用了不同颜色来作为routingKey)

发送方

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct"/*exchange类型为direct*/);
 
channel.basicPublish(EXCHANGE_NAME, "info"/*关键词=info*/, null, message.getBytes());
channel.close();
connection.close();

接收方

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "direct"/*exchange类型为direct*/);
// 创建匿名Queue
String queueName = channel.queueDeclare().getQueue();
// 订阅某个关键词,绑定到匿名Queue中
channel.queueBind(quueName,EXCHANGE_NAME,"error");
channel.queueBind(quueName,EXCHANGE_NAME,"info");
 
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
 
QueueingConsumer.Delivery delivery = consumer.nextDelivery(); // Blocking...
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey(); // 可获取路由关键词

这种模式可以看做对Routing的扩展。Routing只能使用固定关键词,而Topics模式可以订阅模糊关键词

关键词必须是一组word,由点号分割。例如"xxx.yyy.zzz",限定255bytes。

* 表示一个word;

# 表示0个或者多个word;

发送方

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic"/*exchange类型*/);
 
channel.basicPublish(EXCHANGE_NAME, "xxx.yyy"/*关键词routingKey*/, null, message.getBytes());
channel.close();
connection.close();

接收方

ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
channel.exchangeDeclare(EXCHANGE_NAME, "topic"/*exchange类型*/);
// 创建匿名Queue
String queueName = channel.queueDeclare().getQueue();
// 订阅某个关键词,绑定到匿名Queue中
channel.queueBind(quueName,EXCHANGE_NAME,"*.yyy");
 
QueueingConsumer consumer = new QueueingConsumer(channel);
channel.basicConsume(queueName, true, consumer);
 
QueueingConsumer.Delivery delivery = consumer.nextDelivery(); // Blocking...
String message = new String(delivery.getBody());
String routingKey = delivery.getEnvelope().getRoutingKey(); // 可获取路由关键词