訊息編碼的考慮
許多雲端應用程式會使用異步訊息在系統元件之間交換資訊。 傳訊的重要層面是用來編碼承載數據的格式。 選擇傳訊技術之後,下一個步驟是定義訊息的編碼方式。 有許多可用的選項,但正確的選擇取決於您的使用案例。
本文說明一些考慮。
訊息交換需求
生產者與消費者之間的訊息交換需要:
- 定義訊息承載的圖形或結構。
- 表示承載的編碼格式。
- 用於讀取和寫入編碼負載的序列化庫。
訊息的產生者會根據商業規則和想要傳送給取用者的資訊,定義訊息圖形。 若要建構圖形,請將資訊分成離散或相關的主題(字段)。 決定這些欄位的值特性。 請考慮:最有效率的數據類型為何? 資料載荷會一直有特定欄位嗎? 承載內容是包含單一記錄還是重複的數值集合?
然後,根據您的需求選擇編碼格式。 某些因素包括在必要時建立高度結構化數據的能力、編碼和傳輸訊息所需的時間,以及剖析承載的能力。 根據編碼格式,選擇支援良好的串行化連結庫。
訊息的取用者必須知道這些決策,以便知道如何讀取傳入的訊息。
若要傳送訊息,產生者會將訊息串行化為編碼格式。 在接收端,消費者會反序列化訊息負載以使用資料。 如此一來,實體就會共用模型,只要圖形不會變更,訊息就會繼續而不會發生問題。 當合約變更時,編碼格式應該能夠處理變更,而不會影響使用者端。
JSON 等一些編碼格式是自我描述的,這表示不需要參考架構即可剖析它們。 不過,這類格式通常會產生較大的訊息。 使用其他格式時,數據可能無法輕易剖析,但訊息會精簡。 本文強調一些可協助您選擇格式的因素。
編碼格式的考量
編碼格式會定義一組結構化數據如何表示為位元組。 訊息類型可能會影響格式選擇。 與商務交易相關的訊息最有可能包含高度結構化的數據。 此外,您可能想要稍後擷取以供稽核之用。 針對事件的數據流,您可能想要儘快讀取一連串的記錄,並加以儲存以進行統計分析。
以下是選擇編碼格式時要考慮的一些點。
人類可讀性
訊息編碼可以廣泛分為文字型和二進位格式。
使用以文字為基礎的編碼方式,訊息承載是純文本,因此,使用者不需要使用任何程式代碼連結庫即可檢查。 人類可讀取的格式適用於封存數據。 此外,由於人類可以讀取資料負載,因此文字格式更容易偵錯,並傳送至日誌用於錯誤排除。
缺點是承載往往較大。 常見的文字格式為 JSON。
加密
如果訊息中有敏感數據,請考慮是否應遵循本指引中的說明對這些訊息進行完整加密,以加密 Azure 服務總線的靜態數據。 或者,如果只需要加密特定欄位,而且您想要降低雲端成本,請考慮針對此使用類似 NServiceBus 的連結庫。
編碼大小
訊息大小會影響網路上的網路 I/O 效能。 二進位格式比文字格式更精簡。 二進位格式需要序列化/反序列化庫。 除非載荷已解碼,否則無法讀取。
如果您想要降低網路使用量並更快傳輸訊息,請使用二進位格式。 在記憶體或網路頻寬是值得關注的案例中,建議使用這種格式類別。 二進位格式的選項包括 Apache Avro、Google 通訊協定緩衝區(protobuf)、MessagePack 和精簡二進位物件表示法(CBOR)。 這些格式的優缺點會在 本節中說明。
缺點是資料載荷不是人類可讀的。 大部分的二進位格式都會使用成本高昂的複雜系統來維護。 此外,他們需要專用的程式庫來解碼,如果您想要擷取存檔資料,則可能無法支援。
了解承載
訊息承載會以位元組序列的形式送達。 若要剖析此序列,取用者必須能夠存取描述承載中數據欄位的元數據。 儲存和散發元數據有兩種主要方法:
標記元資料。 在某些編碼方式中,尤其是 JSON,欄位會在訊息主體中以數據類型和標識符標記。 這些格式 自描述,因為它們可以在不參考架構的情況下剖析成值的字典。 使用者瞭解欄位的一種方法是查詢預期的值。 例如,產生者會以 JSON 發送承載。 消費者會將 JSON 剖析為字典格式,並檢查欄位是否存在,來了解負載。 另一種方式是讓取用者套用生產者共享的數據模型。 例如,如果您使用靜態類型語言,許多 JSON 序列化程式庫可以將 JSON 字串剖析成具類型的類別。
架構。 架構會正式定義訊息的結構和數據欄位。 在此模型中,生產者和取用者會透過定義完善的架構來取得合約。 架構可以定義數據類型、必要/選擇性欄位、版本資訊,以及承載的結構。 生成者會根據編寫器架構傳送有效載荷。 消費者會藉由套用讀取器範本來接收負載。 訊息會使用特定編碼的函式庫進行序列化/反序列化。 有兩種方式分發架構:
將架構儲存為訊息中的前置詞或標頭,但與承載不同。
將架構儲存在外部。
某些編碼格式會定義架構,並使用從架構產生類別的工具。 產生者和使用者會使用這些類別和函式庫來序列化和反序列化承載資料。 資料庫也會提供寫入架構與讀取架構之間的相容性檢查。 protobuf 和 Apache Avro 都遵循該方法。 主要差異在於 protobuf 具有語言無關的架構定義,但 Avro 會使用精簡的 JSON。 另一個差異在於這兩種格式在讀取器和寫入器架構之間提供相容性檢查的方式。
將架構儲存在架構登錄外部的另一種方式。 訊息包含架構和負載的參考。 產生者會在訊息中傳送架構標識碼,取用者會從外部存放區指定該標識符來擷取架構。 雙方都使用針對特定格式的庫來讀取和寫入訊息。 除了儲存架構之外,登錄還可以提供相容性檢查,以確保生產者與取用者之間的合約不會隨著架構的發展而中斷。
選擇方法之前,請先決定更重要的內容:傳輸數據大小或稍後剖析封存數據的能力。
將結構與有效負載一起儲存會產生更大的編碼尺寸,這種方法適用於間歇性訊息。 如果傳輸較小的位元組區塊非常重要,或您預期會有一連串的記錄,請選擇此方法。 維護外部架構存放區的成本可能很高。
不過,如果隨選解碼比資料大小更重要,則包括承載的架構或標記的元數據方法可保證之後的解碼。 訊息大小可能會大幅增加,並可能會影響記憶體的成本。
架構版本控制
隨著業務需求變更,圖形預期會變更,而且架構將會發展。 版本控制可讓產生者指出可能包含新功能的架構更新。 版本控制有兩個方面:
消費者應該注意變更。
其中一種方式是讓取用者檢查所有欄位,以判斷架構是否已變更。 另一種方式是生產者隨訊息一起發佈架構版本號碼。 當架構發展時,產生者會遞增版本。
變更不得影響或破壞消費者的商業邏輯。
假設欄位已新增至現有的架構。 如果使用者在新版本中收到與舊版本相同的資料負載,那麼如果無法忽視缺少的新欄位,他們的邏輯可能會中斷。 請考慮反向案例,假設新架構中已移除欄位。 使用舊架構的取用者可能無法讀取數據。
Avro 之類的編碼格式可讓您定義預設值。 在上述範例中,如果字段以預設值新增,則會將遺漏的字段填入預設值。 Protobuf 等其他格式透過必要和選擇性字段提供類似的功能。
承載結構
請考慮數據在負載中排列的方式。 這是一連串的記錄或離散單一承載嗎? 承載結構可以分類為下列其中一個模型:
陣列/字典/值:定義在一維或多維陣列中儲存值的項目。 條目具有唯一的鍵值對。 它可以延伸來表示複雜結構。 一些範例包括 JSON、Apache Avro 和 MessagePack。
如果訊息是以不同的架構進行個別編碼,則此配置很適合。 如果您有多個記錄,資料負載可能會過於重複,導致資料負載膨脹。
表格式數據:資訊分成數據列和數據行。 每個數據行都會指出欄位,或資訊的主旨,而每個數據列都包含這些欄位的值。 此配置對於重複的資訊集有效率,例如時間序列數據。
CSV 是最簡單的文字格式之一。 它會將數據呈現為具有一般標頭的記錄序列。 針對二進位編碼,Apache Avro 的前置詞類似於 CSV 標頭,但會產生精簡的編碼大小。
函式庫支援
請考慮使用眾所周知的格式,而非專屬模型。
廣為人知的格式可以透過社群廣泛支援的程式庫來實現支援。 使用特殊格式時,您需要特定的程式庫。 您的業務邏輯可能必須調整以適應程式庫所提供的一些 API 設計選擇。
針對架構型格式,請選擇能在讀取器和寫入器架構之間進行相容性檢查的編碼庫。 某些編碼庫,例如 Apache Avro,預期使用者需要在還原序列化訊息之前先指定寫入和讀取架構。 這項檢查可確保取用者知道架構版本。
互操作性
您選擇的格式可能取決於特定的工作負載或技術生態系統。
例如:
Azure 串流分析具有 JSON、CSV 和 Avro 的原生支援。 使用串流分析時,盡可能選擇其中一種格式是合理的。 如果沒有,您可以提供 自定義反序列化器,但這會增加一些額外的複雜度至您的解決方案。
JSON 是 HTTP REST API 的標準交換格式。 如果您的應用程式收到來自用戶端的 JSON 承載,然後將這些承載放在訊息佇列以進行異步處理,則使用 JSON 進行傳訊,而不是將 JSON 重新編碼為不同的格式可能很合理。
這隻是互操作性考慮的兩個範例。 一般而言,標準化格式會比自定義格式更具互操作性。 在文字型選項中,JSON 是最互通的其中一個。
編碼格式的選擇
以下是一些常用的編碼格式。 選擇格式之前,請先考慮考慮事項。
JSON(JavaScript物件標記法)
JSON 是開放式標準 (IETF RFC8259)。 它是文字格式,遵循陣列/字典/值模型。
JSON 可用於標記元數據,而且您可以剖析沒有架構的承載。 JSON 支援指定選擇性欄位的選項,有助於向前和回溯相容性。
最大的優勢是其通用。 這是許多傳訊服務最互通的預設編碼格式。
它是以文字為基礎的格式,在網路傳輸過程中效率不高,而且在儲存空間有限的情況下也不是理想的選擇。 如果您要透過 HTTP 直接將快取的項目傳回給客戶端,儲存 JSON 可以節省從其他格式反序列化然後序列化為 JSON 的成本。
使用 JSON 處理單一記錄訊息,或處理每個訊息具有不同架構的訊息序列。 避免針對一連串記錄使用 JSON,例如時間序列數據。
JSON 還有其他變化,例如 二進位 JSON (BSON),這是與 MongoDB 搭配運作的二進位編碼。
Comma-Separated 值 (CSV)
CSV 是以文字為基礎的表格式格式。 數據表的標頭表示欄位。 這是訊息包含一組記錄的慣用選擇。
缺點是缺乏標準化。 有許多方式可以表示分隔符、標頭和空白欄位。
Protocol Buffers(protobuf)
Protocol Buffers(或稱 protobuf)是一種序列化格式,會使用強類型定義檔案在鍵/值對中定義結構。 然後,這些定義檔案會編譯為特定語言的類別,以用於序列化和反序列化訊息。
訊息包含壓縮的二進位小型承載,因此傳送速率較快。 缺點是這個負載無法被人類直接閱讀。 此外,因為架構是外部的,因此對於您必須擷取封存數據的情況,不建議這麼做。
Apache Avro
Apache Avro 是二進位串行化格式,使用類似於 protobuf 的定義檔,但沒有編譯步驟。 相反地,序列化的數據一律包含架構前序信息。
前文可以包含標頭或架構標識符。 由於編碼大小較小,因此建議使用Avro進行串流處理數據。 此外,因為它有一個適用於一組記錄的標頭,所以它是表格式數據的絕佳選擇。
MessagePack
MessagePack 是一種二進位序列化格式,其設計目的是為了在網路傳輸時保持緊湊。 沒有訊息架構或訊息類型檢查。 不建議針對大量記憶體使用此格式。
CBOR
精簡二進位物件表示法 (CBOR) (Specification) 是提供小型編碼大小的二進位格式。 CBOR 優於 MessagePack 的優點是其符合 IETF RFC7049。