看本篇文章之前, 务必先看之前的文章Ruby使用RabbitMQ(基础)里面说到了一些基础内容.
下面, 来讲一下更加复杂且常用的方法和注意事项;
amqp-concepts
在rabbitmq 中生产者会把消息发送给 exchange
,
然后 exchange
再发送给queue, queue再发送给消费者;
如果 exchange 没有 queue,那么消息会被丢弃;
当然, 一般情况下, 都会绑定一个queue, 让它持久化.
这样, 消息就不会丢失了
exchange 新加入一个 queue-01 , 这个 queue-01 不会收到之前发送过的消息;
通过上面的图, 我们可以知道消息都是先经由exchange, 才分发给各个queue的;
那么, 到底要怎么分发消息呢?
这就涉及到 exchange
的类型, 它有四种类型;
具体的分析, 在下面的 Exchanges 一节.
其中要注意的是, 消费者在同一个queue中属于竞争关系;
如果queue中有6条消息, 3个消费者;
那么, 3个消费者会每个人收到2条消息;
在上文, 我们提到了Exchange.
它会根据不同的类型来分发消息;
注意: exchange 并不会保存消息, queue 保存消息
类型
fanout
是最简单的exchange类型;
它把接受到的消息, 转发给全部的queue;
# 生产者
exchange = channel.fanout('fanout-test-02', durable: true)
30.times do |n|
exchange.publish("fanout-message: #{n}")
end
# 消费者
exchange = channel.fanout('fanout-test-02', durable: true)
# 这里, 如果是不同的 queue, 会收到同样的消息
queue = channel.queue('fanout-queue-01', durable: true)
queue.bind(exchange)
queue.subscribe(block: true, manual_ack: true) do |_delivery_info, _properties, body|
# 模拟延时任务, 延时1s
sleep 1
puts _delivery_info
puts _properties
puts body
channel.ack(_delivery_info.delivery_tag)
end
direct
类型; 它是rabbitmq 的默认类型;
它根据不同的 routing_key
来分发消息
routing_key中可包含任意数量单词,最多达255个字节。
在Ruby使用RabbitMQ(基础), 我们没有设置 exchange, 仿佛没有exchange也能够正常工作;
但是, 在这背后, 其实是代码上的简写而已;
# 生产者
# 实际上, 并不是 queue来发布消息; 而是exchange; 这只是一种简写
queue = channel.queue('hello')
queue.publish('message')
# 消费者
queue = channel.queue('hello')
上面的代码, 实际上是下面的结果
# 生产者
exchange = channel.direct('')
exchange.publish('message', routing_key: 'hello')
# 消费者
exchange = channel.direct('')
queue = channel.queue('hello', exclusive: true)
# 根据不同的 routing_key 来分发消息
queue.bind(exchange, routing_key: 'hello')
如果, 生产者生产了 10条消息(2条warn, 3条info, 5条debug)
那么, 不同routing_key的queue 就会收到不同的条数
topic
类型的 Exchange
是最为灵活的.
和上面的 direct
类似;
topic
也是根据 routing_key
来分发消息的;
但是, 更为灵活;
routing_key 的命名 单词用 .
连接的字符串
例如
可以使用 #
, *
来匹配不同的key
#
代表多个单词*
代表一个单词所以在 exchange 分发消息时, 我们可以根据消费者设置的不同key, 具有针对性的获取消息;
例如:
# 发送的消息
['movies.action','movies.action.tom','movies.tragedy',
'movies.comedy', 'fruit.red.apple', 'fruit.yellow.banana']
movies.#
会收到 [‘movies.action’,‘movies.action.tom’,‘movies.tragedy’, ‘movies.comedy’]movies.action.*
会收到 [ ‘movies.action.tom’ ]# 生产者
exchange = channel.topic('topic-test', durable: true, auto_delete: false)
key = ['movies.action','movies.action.tom','movies.tragedy',
'movies.comedy', 'fruit.red.apple', 'fruit.yellow.banana']
key.each_with_index do |key, index|
exchange.publish("topic: #{key}", routing_key: key, persistent: true)
end
# 消费者
exchange = channel.topic('topic-test', durable: true, auto_delete: false)
# queue = channel.queue('movies-all', durable: true)
queue = channel.queue('movies-action', durable: true)
# queue.bind(exchange, routing_key: 'movies.#')
queue.bind(exchange, routing_key: 'movies.action.*')
queue.subscribe(block: true, manual_ack: true) do |_delivery_info, _properties, body|
puts body
channel.ack(_delivery_info.delivery_tag)
end
我们一直在使用 channel
来创建 exchange
, queue
那么, channel
是什么呢?
在上面的代码中, 我们可以看到, channel 是由 connection生成的
channel = connection.create_channel
其实, 这个和 AMQP
的设定有关;
一个连接代表了一个真实的 TCP 连接.
然而, 为了性能, 我们把 channel
处理为一个虚拟连接;
我们可以在一个应用中使用多个channel, 而仅仅使用一个 TCP连接.
rabbitmq-B站上的视频
queues
rabbitmq-connection-and-channel
amqp-concepts