Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Lagomに学ぶマイクロサービス / Learn Microservice with Lagom

yuiwai
August 27, 2021

Lagomに学ぶマイクロサービス / Learn Microservice with Lagom

yuiwai

August 27, 2021
Tweet

More Decks by yuiwai

Other Decks in Programming

Transcript

  1. LagomのEntity永続化(イベントソーシング) Service Entity 永続化層(Journal) (Cassandra, RDB, ...etc) Client Request Command

    Reply Response State Event 永続化層(Snapshot) (Cassandra, RDB, ...etc) イベントソーシングの全体像。個々の処理を順に見ていく
  2. LagomのEntity永続化(イベントソーシング) Service ClientからServiceへリクエストが送信される Entity 永続化層(Journal) (Cassandra, RDB, ...etc) Client Request

    Command Reply Response State Event 永続化層(Snapshot) (Cassandra, RDB, ...etc) RegisterUserRequest( Name: “Yuichiro” )
  3. LagomのEntity永続化(イベントソーシング) Service Entity 永続化層(Journal) (Cassandra, RDB, ...etc) Client Request Command

    Reply Response State Event 永続化層(Snapshot) (Cassandra, RDB, ...etc) RegisterUserRequest( Name: “Yuichiro” ) RegisterUserCommand( Name: “Yuichiro” ) UserRegistered( Name: “Yuichiro” ) State.User.Name = event.Name 識別子発行 EntityはCommandを受け取り、Eventを発行する。Eventの保存が成功したら、それを Stateに反映すること で状態を更新する。
  4. LagomのEntity永続化(イベントソーシング) Service Entity 永続化層(Journal) (Cassandra, RDB, ...etc) Client Request Command

    Reply Response State Event 永続化層(Snapshot) (Cassandra, RDB, ...etc) RegisterUserRequest( Name: “Yuichiro” ) RegisterUserCommand( Name: “Yuichiro” ) UserRegistered( Name: “Yuichiro” ) State.User.Name = event.Name 識別子発行 ServiceはEntityからの返答を非同期に受け取り、 Clientへレスポンスを返す。 上記の例では発行した識別子だけを返している。 Done 識別子
  5. LagomのEntity配置戦略(Cluster Sharding) Service EntityはEntityID(Entityの識別子)によってService内で一意に特定され、どのシャードに配置されるかが決 まる。 Node Request Response Node Shard

    Shard Shard Shard Shard Shard Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity API実装 (EntityIDから Shardを特定) 注)Cluster ShardingはSplit Brain問題の影響をモロに受けるが、それはまた別の機会に
  6. LagomのEntity配置戦略(Cluster Sharding) Service EntityはEntityID(Entityの識別子)によってService内で一意に特定され、どのシャードに配置されるかが決 まる。 Node Request Response Node Shard

    Shard Shard Shard Shard Shard Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity Entity API実装 (EntityIDから Shardを特定) 注)Cluster ShardingはSplit Brain問題の影響をモロに受けるが、それはまた別の機会に 図を見ると分かる通り、 Serviceはクラスタ(複数のノードか ら形成されている)である。 つまり、Serviceは単体でスケールイン /アウトする。 この点は非常に重要。
  7. LagomのCQRS戦略(Read-side) 永続化層(Journal) (Cassandra, RDB, ...etc) 永続化層(Read-side) (Cassandra, RDB, ...etc) EventProcessor

    (Read Model Updater) 永続化されたイベントを 順に取得 読み取り用のモデルを 構築し保存 Write Read ユーザ登録イベントに対して、 SQLを利用してRead-sideを更新する例
  8. LagomのCQRS戦略(Read-side) 永続化層(Journal) (Cassandra, RDB, ...etc) 永続化層(Read-side) (Cassandra, RDB, ...etc) EventProcessor

    (Read Model Updater) 永続化されたイベントを 順に取得 読み取り用のモデルを 構築し保存 Write Read UserRegistered ( Name: “Yuichiro” ) ユーザ登録イベントに対して、 SQLを利用してRead-sideを更新する例 イベントをJournalから読み取る。どこまで読み取ったかの offsetを保持しているので、 Journalのデータが破 損しない限り、いつかは追い付く。
  9. LagomのCQRS戦略(Read-side) 永続化層(Journal) (Cassandra, RDB, ...etc) 永続化層(Read-side) (Cassandra, RDB, ...etc) EventProcessor

    (Read Model Updater) 永続化されたイベントを 順に取得 読み取り用のモデルを 構築し保存 Write Read UserRegistered ( Name: “Yuichiro” ) INSERT INTO user (name) VALUES (‘Yuichiro’) ユーザ登録イベントに対して、 SQLを利用してRead-sideを更新する例 Read-side(読取専用:CQRSでいうところのQuery側に利用する永続化層へとイベントを同期する)。上記 の例ではSQLを利用してRDBに保持している想定。
  10. LagomのCQRS戦略(Read-side) Read-sideの構築は単純なCRUD操作になる。泥臭い書き方が求められる反面、どんな永続化層を選ぶ ことも出来る自由度があるので、データ構造に合わせてより良いものを選択すればいい。 永続化層(Journal) (Cassandra, RDB, ...etc) 永続化層(Read-side) (Cassandra, RDB,

    ...etc) EventProcessor (Read Model Updater) 永続化されたイベントを 順に取得 読み取り用のモデルを 構築し保存 Write Read UserRegistered ( Name: “Yuichiro” ) INSERT INTO user (name) VALUES (‘Yuichiro’) ユーザ登録イベントに対して、 SQLを利用してRead-sideを更新する例
  11. サービスの結合 Service A Service B Request Request ▪ 同期的なアプローチの問題点 Response

    Response もし、Service Bがレスポンスを返さなかったら? Error?
  12. サービスの結合 • Service Aはタイムアウトまで、待たされる(ブロックされる)かもしれない = パフォーマンスの劣化、障害の伝播 • Service Aは処理を「失敗」と判断してエラーを返すが、実は Service

    Bの処理は成功しているかもし れない = データ不整合 Service A Service B Request Request ▪ 同期的なアプローチの問題点 Response Response もし、Service Bがレスポンスを返さなかったら? Error?
  13. サービスの結合 Service A Service B Request Request ▪ 同期的なアプローチの問題点 Response

    Response もし、Service Bがレスポンスを返さなかったら? Error? - Service Aはタイムアウトまで、待たされる(ブロックされる)かもしれない = パフォーマンスの劣化、障害の伝播 - Service Aは処理を「失敗」と判断してエラーを返すが、実は Service Bの処理は成功しているかもし れない = データ不整合 【教訓】分散処理において、整合性を担保するのは非常に難しい
  14. Circuit Breaker ▪ Serviceは常にすべてがHealthyとは限らない Service A Service B Request Request

    Response Response 先ほど見た、同期的な呼び出しが失敗した例。 Service Bが連携先の外部サービスであったとして、そこで何か障害が発生してい る、という状況が考えられる。 (Service AはService Bのクライアントである) 障害発生中
  15. Circuit Breaker ▪ Serviceは常にすべてがHealthyとは限らない Service A Service B Request Request

    Response Response 先ほど見た、同期的な呼び出しが失敗した例。 Service Bが連携先の外部サービスであったとして、そこで何か障害が発生してい る、という状況が考えられる。 (Service AはService Bのクライアントである) 障害発生中 この状況下で、Service Bへのリクエストを投げ 続けるのは得策ではない。 (リクエストがより詰まり続けるだけ) 遅延はService Aのクライアントにも伝播してし まう。
  16. Circuit Breaker ▪ Serviceは常にすべてがHealthyとは限らない Service A Service B Request Response

    Service Bの状態を「接続不能」と見なして、早期に失敗させる。 こうすることで、Service Bに余計な負荷を掛けず、復旧を待つと同時に、 Service A 自体の応答速度を保つことで、障害の伝播を食い止める。 障害発生中 Fail-Fast
  17. Circuit Breakerの状態遷移 ▪ Circuit Breakerは段階的に状態遷移をしながら復旧を待つ https://www.lagomframework.com/documentation/1.4.x/scala/ServiceClients.html#Circuit-Breakers から拝借 Closed: 正常な状態。障害検知時に Openに遷移する。

    Open: 全てのリクエストを早期失敗させる。一定時間経過で Half-Openへ遷移する。 Half-Open: 最初の1リクエストだけを接続する。 => 成功したら、Closedへ遷移する。 => 失敗したら、Openへ遷移する。