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

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