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