(-> % read write unlearn)

My writings on this area are my own delusion

マイクロサービスアーキテクチャ読書会::第4章前半の自分なりのまとめ

MSA読書会の第三回に行ってきました。 扱ったのは第4章でした。 とても面白い内容で、しかも発表者と発表資料がとても分かりやすく最高でした。 自分なりに振り返りと整理をしてみます。

6章くらいまではかなり前に読んでいたのだけど、勉強会に参加して自分がいかに何も理解せずに読み進めていたか分かって楽しかったです。

長くなったので、このエントリでは前半(〜4.11)までです。


用語のまとめをちらっと

  • 統合: (マイクロ)サービス間を繋ぐこと。

  • 統合技術: (マイクロ)サービス間を繋ぐための通信技術、通信規格、通信手段のこと。

統合技術の選定基準

4つ。

  1. サーバ側の変更が、コンシューマ側の変更を引き起こすことが少ないこと。
  2. 特定の言語やフレームワーク、実行環境、に依存しないこと。
    • 例えば、Java RMIJavaに依存する。
  3. コンシューマにとって使いやすいこと。
    • 例えば、クライアントライブラリを自動生成できると、コンシューマは使いやすい*1
  4. 内部の実装詳細が隠蔽されること。
    • 内部の実装詳細が見えてしまっているとそれをコンシューマが利用してしまう。→ 結合度が高くなってしまう。

実例

以降、MusicCorpの「顧客サービス」を例に考えていく。 「顧客サービス」は、一見「顧客エンティティ」のCRUDを提供すればそれだけで済みそうだが、実際にはもっと複雑になることが多い。 「ようこそメール送信」とか「支払の設定」とか。

統合方法のモデルと通信モードと通信形態

  1. オーケストレーション と コレオグラフィ
  2. 同期 と 非同期
  3. リクエスト/レスポンス と イベント駆動

統合方法のモデルとして、オーケストレーション と コレオグラフィの2つを考える。

  • オーケストレーション: 上流のワークフローが、派生するワークフローの起動を一元的に制御・管理する。
    「上流のワークフロー」を抱えるサービスが、肥大化(「神サービス」化)しやすい。
    「派生するワークフロー」の中身がスカスカ(「貧血症」)になりやすい。
  • コレオグラフィ: 派生するワークフローは、上流のワークフローのイベントを購読することで処理が起動される。
    変更・拡張に強い。
    欠点は、処理が問題なく完了したことを確認するのに、一工夫(監視システムなど)必要になること。

  • 同期: 単純で結果を追いやすい。
  • 非同期: コレオグラフィ・モデルによくフィットする。

  • リクエスト/レスポンス: 呼び出す処理に時間がかかる場合は適さない。
  • イベント駆動: コレオグラフィ・モデルによくフィットする。

注意点として、リクエスト/レスポンス方式 = 同期通信ではないし、イベント駆動方式 = 非同期通信というわけでもない。 ただし、『イベントベースのシステムは本質的に非同期』

リクエスト/レスポンス方式の統合技術その1 The Shared Database

The Shared Database(共有データベース/データベース統合)は、世の中で最も採用されている統合技術(技術というよりは形態)である。 そして、The Shared Database絶対に避けなければいけないアンチパターンである。

理由:

  1. あるデータ構造(RDBならカラム構成など)を変更する際に、そのデータを利用する全てのサービスを考慮しないといけない。
  2. 技術が1つに固定される。例えばRDBでスタートしたとしたら、あとあとカラムナーに最適なデータが顕在化してきてそのデータ量がどんどん膨れ上がったとしても、変更や切り離しは容易ではない。『疎結合よ、さようなら
  3. 同じデータに対する各種操作の実装箇所が散らばりやすい。*2凝集性よ、さようなら

The Shared Databaseのまとめ: The Shared Databaseはアンチパターンなので、極力避けること。

リクエスト/レスポンス方式の統合技術その2 RPC

  • RPCの基本的な考え方はリモート呼び出しの複雑さを隠蔽することにある。
  • しかしそれだけに留まらず、リモート呼び出しであることまで過度に隠蔽されてしまう実装が世の中にはまま存在する。
    1. リモート呼び出しのコスト*3の隠蔽。
    2. リモート呼び出しの(NWを経由することによる)信頼性の低さの隠蔽。
    3. 拡張に対して非常に脆い*4実装が存在する。
  • 「リモート呼び出しであることの過度な隠蔽」がもたらす問題として、文中ではJava RMIを用いて2つの例があげられている。

    1. サーバ側のリモート・メソッドの変更が、常にクライアント側スタブを変更することを要求する。サーバ側のリモート・メソッドの変更はありふれた拡張であり、現実的には管理しきれなくなる。
    2. フィールドの削除の際に、デシリアライズのコードの整合性を保つためには「サーバとクライアントの同時デプロイ」が必要になってしまう。サーバ側のみでフィールドの削除や構成変更を行うとクライアントのデシリアライズの処理は当然失敗するようになる。*5
  • Java RMI: Javaで、NW越しにメソッドを呼び出すための仕様、技術。Javaは何年も使ってたのに、RMIは使ったことない。著者の評価は上述のとおり。

  • Protocol Buffers: Googleが考案した、言語やプラットフォームに非依存のシリアライズの仕組み。拡張可能。RTBやってた頃に使った記憶がある。著者の評価では割と褒められている。
  • Thrift: Facebookが考案・開発したRPCのフレームワーク。使ったことない。こちらも、著者の評価では割と褒められている。
  • XML-RPC: Wikipediaによると、『エンコード(符号化)にXMLを採用し、転送機構に HTTP を採用している。非常に単純なプロトコルで、少数のデータ型やコマンドだけを定義しているだけであり、その仕様は2枚の紙にまとめられる。』とのこと。自分は存在自体全く知らなかった。文中で名前が挙がっているが、特に著者は良し悪しには言及していない。

RPCのまとめ。
最近ではRPCはあまり人気がなくなってきているが、RPCが特別悪い統合技術というわけではない。 しかし、もっと良い統合技術が存在するから、わざわざRPCを積極的に採用する理由が少ない。
もしRPCを採用する場合は、「リモート呼び出しであることを過度に隠蔽」する実装や使用方法を選択してしまわないように気を付けなければいけない。

リクエスト/レスポンス方式の統合技術その3 REST*6

  • 最も重要なのはリソースが設計のコア概念であること。
  • リソースの外部表現と内部表現が完全に分離されている。
  • これは例えばHTTPなら、ContentTypeJSONが指定されJSON形式の外部表現でコンシューマにレスポンスされるが、内部ではMySQLのレコードとしてデータが表現されている、というような感じ。
  • RESTと言っても色んなかたちがある。Richardson Maturity Model必読
  • ちなみに、REST=HTTPではないしRPC≠HTTPってわけでもないが、実際には次の2つの理由で、HTTPでRESTするのが最も都合がいい。
    1. HTTPのメソッド(GETやPOSTやDELETEなど)がRESTによくフィットしている。
    2. HTTPはメジャーだから関連するツールが多い。
  • HATEOAS*7 で、より強力なクライアントとサーバの疎結合化を実現可能。 しかし、クライアント側の呼び出し回数が増えてしまうというデメリットもある。
  • データの外部表現はXMLJSONが2強。 もちろんその他(バイナリ)も使用可能。
XML JSON
単純さ 単純
重さ 軽め
ハイパー
メディア
コントロール
標準で搭載。
<link rel="" href="">
標準にはない。
HAL*8というのがあるっちゃある。
サポートするツール 多い
↑の例 XPath JSONPath
しかし、広くはサポートされていない
世の中の人気
著者のおすすめ

著者は、ハイパー・メディア・コントロールを使うのが好きなので、それを標準でサポートしているXMLがどちらかと言えば好きらしい。しかし、ハイパー・メディア・コントロールがあまりはやってないことも認めている。

RESTのまとめ。
REST on HTTPがデフォルトの選択肢。 HTTPのメソッドを4つを綺麗に使ってたとしても、実はRESTにはその先(レベル4)があるよ。

非同期イベント駆動の統合技術その1 Message Broker

  • RabbitMQとか。
  • イベントの発行と購読の両方について問題解決してくれる。
  • デメリット:開発プロセスは1つ複雑になるし、運用するMessage Brokerの専門知識が必要になる。
  • 著者おすすめ。
  • ただし、余計な機能(=複雑さ)が詰め込まれがちなので、そのリスクを認識すべし。『ミドルウェアを愚かなままにし、エンドポイントに知性を保持してください』

非同期イベント駆動の統合技術その1 Atom on HTTP

  • Atomは、リソースのフィードを発行する仕様。REST準拠。
  • HTTPはスケーリングに強い。
  • でも低遅延は苦手。
  • 競合コンシューマパターン*10のような、複数のコンシューマによる購読の排他制御は自前で実装しなければいけない。その場合はMessage Brokerを使ったほうが楽。

非同期アーキテクチャ全般

  • より疎結合で、よりスケーラブルにできる。
  • でも、複雑さは増す。プログラミングは難しめ。
  • RxJavaとかのReactive Extensions系のライブラリは、この手の非同期・イベント駆動プログラミングを扱いやすくしてくれる。
  • しかし、Rxが解決するのとは全く別の難しさも存在するので注意。
    • 「非同期リクエスト/レスポンス」にするとしたら、レスポンスを受けた時にどう振る舞うか。 そして、レスポンスを受け取るノードが非決定的でも問題ないか、など。
    • 想定外の不正なリクエストを受け取ったときに適切に隔離する機構があるか。例えばリトライ上限の設定など。

*1:しかし、クライアントライブラリの提供は、サーバとコンシューマの結合度を高めてしまうという別の問題を孕んでいるので注意。

*2:コンテキスト境界が物理的に存在しないから、集約するインセンティブが低い

*3:NW通信コスト、マーシャリング/アンマーシャリングコストなど

*4:本の中では「脆弱性」と表現されているが、原著では brittleness という単語なので、いわゆるセキュリティホール的な意味での脆弱性とはあえて違う意味ととらえて、ここでは「脆さ」としました。

*5:Javaの標準のデシリアライズは、意図しないフィールドが存在する分には単に無視されるので、単純な「フィールドの追加」はサーバ側のみで実施しても問題ない。

*6:REpresentational State Transfer

*7:Hypermedia As The Engine Of Application State

*8:Hypertext Application Language これ自体はXML用もあり。

*9:ポステルの法則に準じた、ペイロード

*10:Competing Consumer Pattern