from:https://www.cnblogs.com/schips/p/12262587.html


背景

QoS 等級 與 通信的流程有關(guān),直接影響了整個通信。而且篇幅比較長,所以我覺得應(yīng)該單獨拎出來講一下。

概念

QoS 代表了 服務(wù)質(zhì)量等級。 設(shè)置上,由2 位 的二進制控制,且值不允許為 3(0x11)。

QoS值Bit 2Bit 1描述
000最多分發(fā)一次
101至少分發(fā)一次
210只分發(fā)一次
-11保留位

要注意的是,QoS 是 Sender 和 Receiver 之間達成的協(xié)議,不是 Publisher 和 Subscriber 之間達成的協(xié)議。

也就是說 Publisher 發(fā)布一條 QoS1 的消息,只能保證 Broker 能至少收到一次這個消息;至于對應(yīng)的 Subscriber 能否至少收到一次這個消息,還要取決于 Subscriber 在 Subscribe 的時候和 Broker 協(xié)商的 QoS 等級。

這里又牽扯出一個概念:"QoS 降級":在 MQTT 協(xié)議中,從 Broker 到 Subscriber 這段消息傳遞的實際 QoS 等于 "Publisher 發(fā)布消息時指定的 QoS 等級和 Subscriber 在訂閱時與 Broker 協(xié)商的 QoS 等級,這兩個 QoS 等級中的最小那一個。"

QoS 0 的通信時序圖

此時,整個過程中的 Sender 不關(guān)心 Receiver 是否收到消息,它"盡力"發(fā)完消息,至于是否有人收到,它不在乎。

發(fā)布者服務(wù)器訂閱者PUBLISH (QoS0,Msg-A)PUBLISH(QoS0,Msg-A)Delete Msg-A發(fā)布者服務(wù)器訂閱者QoS 0:At most one(Fire and forget)

QoS1 的通信時序圖

此時,Sender 發(fā)送的一條消息,Receiver 至少能收到一次,也就是說 Sender 向 Receiver 發(fā)送消息,如果發(fā)送失敗,會繼續(xù)重試,直到 Receiver 收到消息為止,但是因為重傳的原因,Receiver 有可能會收到重復(fù)的消息;

發(fā)布者服務(wù)器訂閱者Store (Msg-A)PUBLISH (QoS1,Msg-A)Store (Msg-A)PUBLISH (QoS1,Msg-A)PUBACK (QoS1)Delete (Msg-A)PUBACK (QoS1,Msg-A)Delete (Msg-A)發(fā)布者服務(wù)器訂閱者QoS 1:At least one

1)Sender 向 Receiver 發(fā)送一個帶有消息數(shù)據(jù)的 PUBLISH 包, 并在本地保存這個 PUBLISH 包。

2)Receiver 收到 PUBLISH 包以后,向 Sender 發(fā)送一個 PUBACK 數(shù)據(jù)包,PUBACK 數(shù)據(jù)包沒有消息體(Payload),在可變頭中(Variable header)中有一個包標識(Packet Identifier),和它收到的 PUBLISH 包中的 Packet Identifier 一致。

3)Sender 收到 PUBACK 之后,根據(jù) PUBACK 包中的 Packet Identifier 找到本地保存的 PUBLISH 包,然后丟棄掉,一次消息的發(fā)送完成。

4)如果 Sender 在一段時間內(nèi)沒有收到 PUBLISH 包對應(yīng)的 PUBACK,它將該 PUBLISH 包的 DUP 標識設(shè)為 1(代表是重新發(fā)送的 PUBLISH 包),然后重新發(fā)送該 PUBLISH 包。重復(fù)這個流程,直到收到 PUBACK,然后執(zhí)行第 3 步。

QoS 2 的通信時序圖

QoS2 不僅要確保 Receiver 能收到 Sender 發(fā)送的消息,還要保證消息不重復(fù)。它的重傳和應(yīng)答機制就要復(fù)雜一些,同時開銷也是最大的。

Sender 發(fā)送的一條消息,Receiver 確保能收到而且只收到一次,也就是說 Sender 盡力向 Receiver 發(fā)送消息,如果發(fā)送失敗,會繼續(xù)重試,直到 Receiver 收到消息為止,同時保證 Receiver 不會因為消息重傳而收到重復(fù)的消息。

發(fā)布者服務(wù)器訂閱者Store (Msg-A)PUBLISH (QoS2,Msg-A,DUP=0)Store (Msg-A)PUBREC (QoS2,Msg-A)PUBREL (QoS2,Msg-A)PUBLISH (QoS2,Msg-A,DUP=0)PUBCOMP (QoS2,Msg-A)Delete (Msg-A)Store (Msg-A)PUBREC (QoS2,Msg-A)PUBREL (QoS2,Msg-A)Notify (Msg-A)PUBCOMP (QoS2,Msg-A)Delete (Msg-A)Delete (Msg-A)發(fā)布者服務(wù)器訂閱者QoS 2:Exactly one

QoS 使用 2 套請求/應(yīng)答流程(一個 4 段的握手)來確保 Receiver 收到來自 Sender 的消息,且不重復(fù):

1)Sender 發(fā)送 QoS 為 2 的 PUBLISH 數(shù)據(jù)包,數(shù)據(jù)包 Packet Identifier 為 P,并在本地保存該 PUBLISH 包;

2)Receiver 收到 PUBLISH 數(shù)據(jù)包以后,在本地保存 PUBLISH 包的 Packet Identifier P,并回復(fù) Sender 一個 PUBREC 數(shù)據(jù)包,PUBREC 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);

3)當(dāng) Sender 收到 PUBREC,它就可以安全地丟棄掉初始的 Packet Identifier 為 P 的 PUBLISH 數(shù)據(jù)包,同時保存該 PUBREC 數(shù)據(jù)包,同時回復(fù) Receiver 一個 PUBREL 數(shù)據(jù)包,PUBREL 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體;如果 Sender 在一定時間內(nèi)沒有收到 PUBREC,它會把 PUBLISH 包的 DUP 標識設(shè)為 1,重新發(fā)送該 PUBLISH 數(shù)據(jù)包(Payload);

4)當(dāng) Receiver 收到 PUBREL 數(shù)據(jù)包,它可以丟棄掉保存的 PUBLISH 包的 Packet Identifier P,并回復(fù) Sender 一個 PUBCOMP 數(shù)據(jù)包,PUBCOMP 數(shù)據(jù)包可變頭中的 Packet Identifier 為 P,沒有消息體(Payload);

5)當(dāng) Sender 收到 PUBCOMP 包,那么它認為數(shù)據(jù)包傳輸已完成,它會丟棄掉對應(yīng)的 PUBREC 包。如果 Sender 在一定時間內(nèi)沒有收到 PUBCOMP 包,它會重新發(fā)送 PUBREL 數(shù)據(jù)包。

我們可以看到在 QoS2 中,完成一次消息的傳遞,Sender 和 Reciever 之間至少要發(fā)送四個數(shù)據(jù)包,QoS2 是最安全也是最慢的一種 QoS 等級了。

QoS 和會話(Session)

客戶端的會話狀態(tài)包括:

  • 已經(jīng)發(fā)送給服務(wù)端,但是還沒有完成確認的QoS 1和QoS 2級別的消息
  • 已從服務(wù)端接收,但是還沒有完成確認的QoS 2級別的消息。

服務(wù)端的會話狀態(tài)包括:

  • 會話是否存在,即使會話狀態(tài)的其它部分都是空。
  • 客戶端的訂閱信息。
  • 已經(jīng)發(fā)送給客戶端,但是還沒有完成確認的QoS 1和QoS 2級別的消息。
  • 即將傳輸給客戶端的QoS 1和QoS 2級別的消息。
  • 已從客戶端接收,但是還沒有完成確認的QoS 2級別的消息。
  • 可選,準備發(fā)送給客戶端的QoS 0級別的消息。

保留消息不是服務(wù)端會話狀態(tài)的一部分,會話終止時不能刪除保留消息。

如果 Client 想接收離線消息,必須使用持久化的會話(CONNECT報文中可變頭(byte8[1])Clean Session = 0)連接到 Broker,這樣 Broker 才會存儲 Client 在離線期間沒有確認接收的 QoS 大于 1 的消息。

QoS 等級的選擇

在以下情況下你可以選擇 QoS0

  • Client 和 Broker 之間的網(wǎng)絡(luò)連接非常穩(wěn)定,例如一個通過有線網(wǎng)絡(luò)連接到 Broker 的測試用 Client;
  • 可以接受丟失部分消息,比如你有一個傳感器以非常短的間隔發(fā)布狀態(tài)數(shù)據(jù),所以丟一些也可以接受;
  • 你不需要離線消息。

在以下情況下你應(yīng)該選擇 QoS1:

  • 你需要接收所有的消息,而且你的應(yīng)用可以接受并處理重復(fù)的消息;
  • 你無法接受 QoS2 帶來的額外開銷,QoS1 發(fā)送消息的速度比 QoS2 快很多。

在以下情況下你應(yīng)該選擇 QoS2:

  • 你的應(yīng)用必須接收到所有的消息,而且你的應(yīng)用在重復(fù)的消息下無法正常工作,同時你也能接受 QoS2 帶來的額外開銷。

實際上,QoS1 是應(yīng)用最廣泛的 QoS 等級,QoS1 發(fā)送消息的速度很快,而且能夠保證消息的可靠性。雖然使用 QoS1 可能會收到重復(fù)的消息,但是在應(yīng)用程序里面處理重復(fù)消息,通常并不是件難事。