Ruby使用RabbitMQ(进阶)

Ruby使用RabbitMQ(进阶)

看本篇文章之前, 务必先看之前的文章Ruby使用RabbitMQ(基础)里面说到了一些基础内容.

下面, 来讲一下更加复杂且常用的方法和注意事项;

概念

amqp-concepts

在rabbitmq 中生产者会把消息发送给 exchange,
然后 exchange 再发送给queue, queue再发送给消费者;

在这里插入图片描述

exchange没有queue

如果 exchange 没有 queue,那么消息会被丢弃;

当然, 一般情况下, 都会绑定一个queue, 让它持久化.
这样, 消息就不会丢失了

exchange 新加入一个 queue-01 , 这个 queue-01 不会收到之前发送过的消息;

一个exchange多个queue

通过上面的图, 我们可以知道消息都是先经由exchange, 才分发给各个queue的;
那么, 到底要怎么分发消息呢?

这就涉及到 exchange 的类型, 它有四种类型;

  • direct (默认类型)
  • topic
  • headers
  • fanout

具体的分析, 在下面的 Exchanges 一节.

一个queue多个消费者

其中要注意的是, 消费者在同一个queue中属于竞争关系;
如果queue中有6条消息, 3个消费者;
那么, 3个消费者会每个人收到2条消息;

Exchanges

在上文, 我们提到了Exchange.
它会根据不同的类型来分发消息;

注意: exchange 并不会保存消息, queue 保存消息

类型

  • fanout
  • direct (默认类型)
  • topic
  • headers

fanout

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

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

topic 类型的 Exchange 是最为灵活的.
和上面的 direct 类似;

topic 也是根据 routing_key 来分发消息的;
但是, 更为灵活;

routing_key 的命名 单词用 . 连接的字符串

例如

  • movies.action
  • movies.action.tom
  • movies.tragedy

可以使用 # , * 来匹配不同的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

我们一直在使用 channel 来创建 exchange, queue
那么, channel 是什么呢?

在上面的代码中, 我们可以看到, channel 是由 connection生成的

channel = connection.create_channel

其实, 这个和 AMQP 的设定有关;

一个连接代表了一个真实的 TCP 连接.
然而, 为了性能, 我们把 channel 处理为一个虚拟连接;

我们可以在一个应用中使用多个channel, 而仅仅使用一个 TCP连接.

参考文章

rabbitmq-B站上的视频

queues

rabbitmq-connection-and-channel

amqp-concepts

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章