Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

【仕様を読み解く】第1回 Advanced Message Queuing Protocol (1) ~Types~

初めまして、プロダクト開発部 Data Hub プロダクトグループの秋田です。20卒で入社して半年が過ぎ、月日があっという間に過ぎ去る恐ろしさを肌で実感する今日この頃です。

本連載では様々なプロトコルやシステムの仕様を読み解いていくことで、それらに込められた意思と意図を考えていこうと思います。
第1回は Data Hub で使用している Azure Service Bus で採用されている Advanced Message Queuing Protocol 1.0 (AMQP 1.0) の仕様を読み解いていきたいと思います。


Advanced Message Queuing Protocol (AMQP) について

近年のマイクロサービスアーキテクチャにおいては、アプリケーションとサービスを分離し非同期に動作させるために、メッセージキューを使うことがあります。メッセージキューの一般的な説明はここでは割愛しますが、AWSのページが簡潔で分かりやすいです。

メッセージキューを使う際に、どんなメッセージをどのようにやり取りするかを定めた代表的なメッセージングプロトコルの一つが Advanced Message Queuing Protocol (AMQP) です。

AMQP は金融サービス大手の JP Morgan Chase で生み出され、2006年に version 0.8~0.9、2008年に version 0.9.1 が発表されたのち、構造化情報標準促進協会 (OASIS) によって、2012年10月に現状の最新版である version 1.0 が発表されています。

AMQP が生まれた経緯と技術的特徴は、Microsoftのページに記載されています。ここではかいつまんで説明したいと思います。

かつて、アプリケーションとメッセージをやり取りするブローカーとの通信プロトコルは、ブローカーのベンダーごとに独自に作られていました。しかし、そのような状況は他のベンダーのシステムやライブラリと組み合わせようとしたときに、しばしば不都合を引き起こします。組み合わせようとしているシステムが別のメッセージングプロトコルを採用していたならば、いちいちゲートウェイなどを挟む必要が出てしまいます。そこで、AMQP はそういった課題を解決し、言語やフレームワークなどによらず使えるオープンな共通仕様として生まれたのです。

AMQP 1.0 の主な技術的特徴は、

  • 効率性
  • 信頼性
  • 柔軟性
  • ブローカーモデルに依存しない *1

の4つです。

このようなプロトコルは使われてなんぼですので、特に後者の二つは大切であると思います。実は AMQP 0.9.1 の時点ではブローカーモデルも言及していたものの、AMQP 1.0 で削除されたことからも、実装に必要な要件を極力減らし、プロトコルをサポートしてもらいやすくしたいという意思と意図が感じられます。

なお、AMQP に関する情報は下記から確認できます。
www.amqp.org

概要

AMQP 1.0 の仕様書は以下のような構成になっています。

  1. Types
  2. Transport
  3. Messaging
  4. Transactions
  5. Security

1は AMQP の型システムとエンコーディングについて記述されています。2以降は各レイヤーについての記述になります。
今回の記事では、最初の Types について扱います。

Types

AMQP の型は大きく分けて、基本的な型である Primitive Types とそれに注釈を付けて作る Described Types があります。ここではそれらの型とその応用である Composite Types について紹介します。

Primitive Types

もっともよく使われるような型は Primitive Types として用意されています。スカラ型に加え、list、map、arrayといったコレクション型が用意されています。JP Morgan Chase で生み出されたこともあり、decimal も decimal128 までサポートされているので、金融サービスなどでも問題なく利用できるでしょう。過不足なく型が用意されている印象を受けます。

Primitive Type として用意されている型は以下の通りです。なお、array は1つの型のみで構成されますが、list と map は複数の型の値で構成することも可能です。

null、boolean、ubyte、ushort、uint、ulong、byte、short、int、long、float、double、decimal32、decimal64、decimal128、char、timestamp、uuid、binary、string、symbol、list、map、array

Primitive Type は下図のようにエンコーディングされます。初めに Type Encoding を表す constructor があり、その後に値が入ります。Type Encoding 0xA1 はサイズ長 1octet の UTF-8 エンコードされた string 型を表しています。そして、 untyped bytes の最初の 1octet で文字列全体のサイズを表しています(下図の例では30文字 = 30 octet なので、0x1E が入ります)。

f:id:s_akita:20201020073928p:plain
Primitive Format Code (String) (仕様書より引用)

Described Types

AMQP では Primitive Types に注釈を付けることで、より幅広い型を表現できます。このような注釈付きの型を Described Types と呼びます。例えば URL のような制約のある文字列は、string 型に URL であることを示す descriptor を足すことで表現できます。
クライアントが descriptor の内容を解釈できる場合は Described Type としてデコードでき、descriptor の内容を解釈できないクライアントでも Primitive Type としてデコードすることが可能です。このように Described Types は柔軟な解釈が可能です。

Described Type は下図のようにエンコーディングされます。この Described Type の constructor は、初めに 0x00 が入り、その後に descriptor (0xA1 0x03 "URL") と constructor (0xA1) が続きます。この例では descriptor は Type Encoding 0xA1 、すなわち string 型で定義されています。
この例では descriptor より後は string 型のエンコーディングと同じ形になっています。descriptor の内容が解釈できない場合は、通常の string 型としてデコードすることが可能です。

f:id:s_akita:20201020080343p:plain
Described Format Code (URL) (仕様書より引用)

Composite Types

複合型、すなわち複数のフィールドを持つ型です。一瞬複雑そうに見えますが、これは単なる described list です。 constructor の最後の 0xc0 が list8 (最大長 2^8-1 の list )を示しています。

f:id:s_akita:20201021082659p:plain
Example Composite Value (仕様書より引用)

このように Primitive Types の組み合わせによって様々な型を表現でき、 Described Types も Primitive Types に注釈を付けただけなので、クライアント / サーバーとも実装としては、とりあえず Primitive Types にある型だけ最低限カバーできればよく、最小要件をできるだけコンパクトに抑えているように思います。

まとめ

今回の記事では AMQP 1.0 の Types についてまとめました。一口に型と言っても、様々な言語や利用環境を想定しようとすると、シンプルかつ必要十分に定義するのは難しいものだと思います。その点 AMQP 1.0 は扱いやすい形でうまくまとまっているように見えますね。
次回の記事では AMQP 1.0 の仕様書の続きについて触れていきたいと思います。お楽しみに!

*1:これは、ブローカーレスなアーキテクチャにおいても AMQP 1.0 を適用できることを意味します。

© Sansan, Inc.