lasciva blog

開発して得た知見やwebビジネスのストック

「マイクロサービスパターン 実践的システムデザインのためのコード解説」を読んだ

目的、モチベーション

マイクロサービスのデザインパターンを知りたかった。

全体の感想

サーガのパターンを扱っていたのが特に参考になった。全体的には、具体的な仮想のプロジェクトをもとにコードの事例も記載されており、抽象的にも具体的にも説明されていて、バランスが良かった。 マイクロサービスアーキテクチャと重複している箇所も多かったので、読んだことのある人は数章だけ読むので十分だと感じた。

目次

概要

Chapter 1 モノリシック地獄からの脱出

1.4. マイクロサービスアーキテクチャで状況打開

1.4.1. スケールキューブとマイクロサービス

マイクロサービスはArt of Scalability, The: Scalable Web Architecture, Processes, and Organizations for the Modern Enterprise (Pear04)で紹介されているスケールの3次元モデルの軸の一つと言える。

Chapter 2 サービスへの分割

DDDとかに基づいてドメイン分割

Chapter 3 マイクロサービスアーキテクチャで使われるプロセス間通信

3.2 同期的なリモートプロシージャ呼び出しパターンを使った通信

3.3 非同期的メッセージングパターンを使った通信

3.3.4 メッセージブローカー

ブローカーレスメッセージング

  • メリット
    • 直接やり取りするので、ネットワークトラフィックが軽くなり、レイテンシが下がる
    • パフォーマンスのボトルネックや単一障害点になるリスクを抱えたメッセージブローカーがない
    • 運用の複雑度が下がる
  • デメリット
    • サービスディスカバリーが必要
    • センダーとレシーバの両方が利用可能でないといけないので、可用性が下がる
    • 配信保証などのメカニズムの実装が難しくなる

ブローカーベースメッセージング

技術選定の際には、以下のようなことを考慮しなければならない。

  • サポートされているプログラミング言語
  • サポートされているメッセージング標準
  • メッセージの順序
  • 永続記憶
  • 持続性
  • スケーラビリティ
  • レイテンシ
  • 競合するコンシューマ

  • メリット

    • 疎結合
    • メッセージのバッファリング
    • 柔軟性の高い通信
    • 明示的なIPC
  • デメリット
    • パフォーマンスのボトルネックになる危険性
    • 単一障害点になる危険性
    • 運用の複雑度の上昇
3.3.7 トランザクショナルメッセージング

DBの書き込みのトランザクション内で、メッセージをパブリッシュしたいケースがある。パフォーマンスと信頼性を担保するためのパターンがいくつかある。

Transactional outbox

DBにOUTBOXというテーブルを追加して、書き込みのトランザクション内でOUTBOXテーブルにメッセージを挿入する。メッセージリレーがOUTBOXテーブルを読み込み、メッセージブローカーにメッセージをパブリッシュする。
このようにすることで、トランザクションをローカルに留め、アトミック性を保証することができる。

microservices.io

Polling publisher
メッセージリレーが、OUTBOXテーブルをポーリングしてパブリッシュするパターン。

microservices.io

Transactional log tailing
Transaction Log MinerがOUTBOXテーブルのトランザクションログを読み込み、メッセージに変換してパブリッシュする。トランザクションをサポートしていないNoSQLでも使用できる。
以下のようなプロジェクトで採用されている。

microservices.io

Chapter 4 サーガによるトランザクションの管理

4.2 サーガのコーディネート

4.2.1 コレオグラフィベースのサーガ

中央で管理するサービスがなく、それぞれのサービスがメッセージをpublishやconsumeする。問題は大きく2点ある。

  1. DBのトランザクションとイベントのpublishをアトミックに行わなければならない
  2. 相関するデータに対応するために、関連するIDが必要になる

欠点

  1. サーガの定義が分散されているので、わかりにくい
  2. サービスが循環依存してしまうことがある
  3. 関連するイベントをサブスクライブしないといけないので、密結合になる
4.2.2 オーケストレーションベースのサーガ

ビジネスロジックを管理するサービスを中心にして、メッセージのやりとりを行う。参加している他のサービスは共通の1つのチャネルで返信を行うことで、関心を減らすことができる。ビジネスロジックを集約できるが、難しくなりすぎないように複数のサービスにまたがるステートを管理する部分のみに集中させることが重要。

4.3 分離性の欠如への対処方法

サーガを使うと、ACIDのうち分離性が失われてしまう。そのため、更新の消失や、ダーティリード、反復不能読み取りなどが発生してしまう。

4.3.2 分離性の欠如に対処するためのカウンターメジャー
  • Semantic lock: アプリケーションレベルのロック。stateに *_PENDINGのようなものを追加して、その場合にアプリケーション側で適切に処理を行う。
  • Commutative updates: どのように順序でも実行できるように更新処理を設計すること。例えば、決済を単純にキャンセルするのではなく、補償となる逆の処理を行う。
  • Pessimistic view: ビジネスリスクが最小になるようにサーガの順序を並べ直すこと。例えば、ダーティリードが起こりそうなサービスの処理を最後に行う。
  • Reread value: データを読み直して変更されていないことを確認してから上書きするようにしてダーティライトを防ぐこと
  • Version file: レコードに対する更新を記録し、順序を変えられるようにすること
  • By value: 個々のリクエストのビジネスリスクによりダイナミックに変更処理メカニズムを選択すること。例えば、決済額が大きいときは厳密な分散トランザクションを利用して、少額の際は以上で紹介したカウンターメジャーを使うなど。

6つぐらい紹介されてるので、まとめる

Chapter 5 マイクロサービスアーキテクチャにおけるビジネスロジックの設計

スナップショットを作って、効率化する versionを条件で絞って、楽観的なロックを実装 イベント形式は削除の操作が大変

Chapter 7 マイクロサービスアーキテクチャでのクエリーの実装

複数のサービスにまたがったデータを一度に取得するには大きく2種類方法がある。 一つはAPI compositionパターンで、BFFなどで複数のサービスを呼び出してアプリケーションで結合して返す方法。サービス間でデータの独立性が保てなかったり、可用性が下がるなどのデメリットはあるが、シンプルなものはこれで概ね対応できる。
もう一つはCQRS(Command Query Responsibility Segregation)パターンで、この方法では更新CUDの操作と読み込みRの操作を別々のサービスで行う。Commandのサービスがイベントをpublishして、それをQueryのサービスがsubscribeして独自のDBに保存する。Queryに最適なDBを選択できたり、関心を分離でき、複雑なQueryも扱えるようになる。一方で、同時更新やイベントを冪等に処理したり、レプリケーションの遅延を考慮した上で実装を行う必要がある。

Chapter 8 外部APIパターン

8.2 APIゲートウェイの実装

8.2.1 API Gateway パターンの概要

API Gatewayは以下のような役割を担う。

  • リクエストのルーティング
  • API合成: 複数のサービスのリクエストの結果を処理する
  • エッジ機能: 認証、認可、流量制限、キャッシュ、メトリクスの収集、ログ
  • プロトコル変換: 外部との通信に最適なプロトコルと、内部通信に最適なプロトコルを変換する

Chapter 11 本番環境に耐えられるサービスの開発

  • 認証・認可
  • 環境変数などによるconfigの設定
  • Observability
    • Health check
    • Log集約
    • 分散トレーシング
    • アプリケーションのメトリクス
    • Exceptionトラッキング
    • Audit logging
  • サービスメッシュ

次のアクション