佇列的操作方式為先進先出(FIFO),這表示訊息通常會加入佇列的「尾部」,並從「頭部」移除。「環形」佇列是一種特殊的佇列,其大小是固定的。當佇列中的訊息數量達到設定的大小時,會移除佇列頭部的訊息,以此來維持固定的大小。

舉例來說,考慮一個設定環形大小為 3 的佇列,以及一個依序傳送訊息 ABCD 的生產者。一旦傳送 C,佇列中的訊息數量將會是 3,與設定的環形大小相同。我們可以這樣視覺化佇列的成長…​

傳送 A 之後

             |---|
head/tail -> | A |
             |---|

傳送 B 之後

        |---|
head -> | A |
        |---|
tail -> | B |
        |---|

傳送 C 之後

        |---|
head -> | A |
        |---|
        | B |
        |---|
tail -> | C |
        |---|

當傳送 D 時,它會加入佇列的尾部,而佇列頭部的訊息(即 A)將會被移除,所以佇列會像這樣

        |---|
head -> | B |
        |---|
        | C |
        |---|
tail -> | D |
        |---|

這個範例涵蓋了最基本的使用情況,也就是將訊息加入佇列的尾部。然而,還有其他一些重要的使用情況涉及

  • 傳送中的訊息與回滾

  • 排程訊息

  • 分頁

但是,在我們討論這些使用情況之前,讓我們先看看環形佇列的基本設定。

1. 設定

有 2 個與環形佇列設定相關的參數。

ring-size 參數可以直接在 queue 元素上設定。預設值來自 default-ring-size address-setting(見下方)。

<addresses>
   <address name="myRing">
      <anycast>
         <queue name="myRing" ring-size="3" />
      </anycast>
   </address>
</addresses>

default-ring-size 是一個 address-setting,它適用於沒有明確設定 ring-size 的匹配位址上的佇列。這對於自動建立的佇列特別有用。預設值為 -1(即沒有限制)。

<address-settings>
   <address-setting match="ring.#">
      <default-ring-size>3</default-ring-size>
   </address-setting>
</address-settings>

ring-size 可以在執行階段更新。如果新的 ring-size 設定低於先前的 ring-size,代理程式不會立即從佇列頭部刪除足夠的訊息來強制執行新的大小。傳送到佇列的新訊息將會強制刪除舊訊息(即佇列不會變得更大),但是佇列要等到透過用戶端正常消費訊息自然達到其新大小為止。

2. 傳送中的訊息與回滾

當訊息「傳送中」時,它們處於一種中間狀態,技術上它們不在佇列中,但也尚未被確認。代理程式取決於消費者是否要確認這些訊息。在環形佇列的內容中,傳送中的訊息無法從佇列中移除。

這會產生一些難題。

由於傳送中訊息的性質,用戶端實際上可以傳送比環形佇列允許的更多的訊息。這可能會讓人覺得環形大小沒有被正確執行。考慮這個簡單的場景

  • 佇列 fooring-size="3"

  • 佇列 foo 上有 1 個消費者

  • 訊息 A 傳送到 foo 並派送給消費者

  • messageCount=1,deliveringCount=1

  • 訊息 B 傳送到 foo 並派送給消費者

  • messageCount=2,deliveringCount=2

  • 訊息 C 傳送到 foo 並派送給消費者

  • messageCount=3,deliveringCount=3

  • 訊息 D 傳送到 foo 並派送給消費者

  • messageCount=4,deliveringCount=4

foomessageCount 現在是 4,比 ring-size 3 1!但是,代理程式別無選擇,只能允許這樣做,因為它無法從傳送中的佇列中移除訊息。

現在考慮在沒有實際確認任何這 4 個訊息的情況下關閉消費者。這 4 個傳送中、未確認的訊息將會取消回代理程式,並以與它們被消費的相反順序加入佇列的頭部。當然,這會使佇列超過設定的 ring-size。因此,由於環形佇列偏好佇列尾部的訊息勝過佇列頭部的訊息,它將會保留 BCD 並刪除 A(因為 A 是最後一個加入佇列頭部的訊息)。

事務或核心階段回滾的處理方式相同。

如果您希望避免這種情況,並且您直接使用核心用戶端或核心 JMS 用戶端,您可以透過縮小 consumerWindowSize 的大小來減少傳送中的訊息(預設為 1024 * 1024 位元組)。

3. 排程訊息

當排程訊息傳送到佇列時,它不會像一般訊息一樣立即加入佇列的尾部。它會被保存在一個中間緩衝區中,並根據訊息的詳細資訊排程傳送到佇列的頭部。然而,排程訊息仍然會反映在佇列的訊息計數中。與傳送中的訊息一樣,這可能會讓人覺得環形佇列的大小沒有被執行。考慮這個簡單的場景

  • 佇列 fooring-size="3"

  • 在 12:00,訊息 A 傳送到 foo,排程在 12:05

  • messageCount=1,scheduledCount=1

  • 在 12:01,訊息 B 傳送到 foo

  • messageCount=2,scheduledCount=1

  • 在 12:02,訊息 C 傳送到 foo

  • messageCount=3,scheduledCount=1

  • 在 12:03,訊息 D 傳送到 foo

  • messageCount=4,scheduledCount=1

foomessageCount 現在是 4,比 ring-size 3 1!但是,排程訊息在技術上尚未在佇列上(即它在代理程式上並排程要放入佇列)。當 12:05 的排程傳送時間到來時,訊息將會放入佇列的頭部,但是由於環形佇列的大小已經達到,排程訊息 A 將會被移除。

4. 分頁

與排程訊息和傳送中的訊息類似,分頁訊息不會計入環形佇列的大小,因為訊息實際上是在位址層級而不是佇列層級進行分頁的。分頁訊息在技術上不在佇列上,儘管它會反映在佇列的 messageCount 中。

建議不要將分頁用於具有環形佇列的位址。換句話說,請確保整個位址能夠放入記憶體,或使用 DROPBLOCKFAIL address-full-policy