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

2021.12.3 - 決済システムで学ぶレジリエントなサービスのいろは

hainet50b
December 06, 2021

2021.12.3 - 決済システムで学ぶレジリエントなサービスのいろは

▼イベント▼
Spring Fest 2021
https://springfest2021.springframework.jp/

▼配信アーカイブ▼
https://www.youtube.com/watch?v=9-yDaFlGTxE

hainet50b

December 06, 2021
Tweet

More Decks by hainet50b

Other Decks in Programming

Transcript

  1. ソフトバンク携帯ユーザー向けの 「ソフトバンクカード」のカード発行・ 運営をしています。 ソフトバンクカードは、 Visa加盟店 で利用できるプリペイドカードです。 ご利用金額に応じて Tポイントが貯 まります。 カード発行業務

    決済代行 EC運営事業者さま向けにオンライン決済 事業を運営しています。豊富な決済手段 をまとめてご提供しています。 カード加盟店業務 Visa、Mastercard、UnionPay(銀聯)のメン バーシップライセンスを保有しており、各ブラ ンドのアクワイアラ(クレジットカード加盟店 契約会社)としての加盟店審査や管理事 業、端末決済サービスを提供しています。 ソフトバンクと共同で、ソフトバンク 携帯ユーザー向けの通話料合算 請求「ソフトバンクまとめて支払い」 の開発・運営をしています。 キャリア決済 EC/ネット店舗 実店舗/訪問販売 SBペイメントサービスの事業内容 3
  2. ソフトバンク携帯ユーザー向けの 「ソフトバンクカード」のカード発行・ 運営をしています。 ソフトバンクカードは、 Visa加盟店 で利用できるプリペイドカードです。 ご利用金額に応じて Tポイントが貯 まります。 カード発行業務

    決済代行 EC運営事業者さま向けにオンライン決済 事業を運営しています。豊富な決済手段 をまとめてご提供しています。 カード加盟店業務 Visa、Mastercard、UnionPay(銀聯)のメン バーシップライセンスを保有しており、各ブラ ンドのアクワイアラー(クレジットカード加盟 店契約会社)としての加盟店審査や管理事 業、端末決済サービスを提供しています。 ソフトバンクと共同で、ソフトバンク 携帯ユーザー向けの通話料合算 請求「ソフトバンクまとめて支払い」 の開発・運営をしています。 キャリア決済 EC/ネット店舗 実店舗/訪問販売 SBペイメントサービスの事業内容 4
  3. 加盟店 決済機関 通販サイト ゲーム 教育 不動産 その他 電子書籍/動画 チケット クレジット

    携帯キャリア決済 コンビニ支払い プリペイドカード 口座振替 ポイント支払い アカウント連携決済 Webシステム 決済代行サービス 5
  4. 加盟店 決済機関 通販サイト ゲーム 教育 不動産 その他 電子書籍/動画 チケット クレジット

    携帯キャリア決済 コンビニ支払い プリペイドカード 口座振替 ポイント支払い アカウント連携決済 決済代行サービス Tanzu Application Service ・・・ 6
  5. 加盟店 決済機関 通販サイト ゲーム 教育 不動産 その他 電子書籍/動画 チケット クレジット

    携帯キャリア決済 コンビニ支払い プリペイドカード 口座振替 ポイント支払い アカウント連携決済 決済代行サービス Tanzu Application Service ・・・ 7 参考:https://www.slideshare.net/JunyaSuzuki1/cloudnative2-sfa4
  6. 加盟店 決済機関 通販サイト ゲーム 教育 不動産 その他 電子書籍/動画 チケット クレジット

    携帯キャリア決済 コンビニ支払い プリペイドカード 口座振替 ポイント支払い アカウント連携決済 決済代行サービス Tanzu Application Service ・・・ 8 プラットフォームや開発体制ではなく 運用や実装に着目してお話いたします。
  7. お品書き No 手法 対応するライブラリ 想定される効果 1 タイムアウト Spring Web -

    滞留による全断を防ぐ 2 リトライ Spring Retry - 1件でも多く決済を成立させる 3 冪等 - - リトライ可能にする 4 分散トレーシング Spring Cloud Sleuth / Zipkin - 処理を追跡する - 処理時間の傾向を把握する 5 非同期 Spring Cloud Stream - パフォーマンスを向上させる 6 キャッシュ Spring Cache - 障害ポイントを減らす 7 サーキットブレーカー Resilience4j - 障害の拡大を防ぐ 8 バルクヘッド Resilience4j - 対向先の障害を防ぐ この資料が見返して役に立つ「いろは」になりましたら幸いです 🙇 15
  8. 決済機関 17 1-1. タイムアウトを設定する ECサイト 決済システム 1500 ms 1000 ms

    通常時はスムーズにリクエストが流れていますが...
  9. 決済機関 18 1-1. タイムアウトを設定する ECサイト 決済システム 60500 ms 60000 ms

    決済機関が障害で遅延することはよくあります。
  10. 1-2. タイムアウトを実施したその後に全力を注ぐ このような状況には大きく2つの対策方法があります。 1. 取消を使う 決済機関にはエンドユーザーに明細が残らない 障害取消という機能があり、こちらをよく使います。 2. 参照を使う 不整合疑いのトランザクションを一旦キューに保持します。

    時間をおいてから定期的に状態を参照して、 取引が存在しないか失敗していた場合はクローズ、 取引が成功してしまっていた場合は取消を行います。 こちらに全力を注ぐイメージを 次のスライドで説明します。
  11. 決済機関 34 1-2. タイムアウトを実施したその後に全力を注ぐ 決済システム キューに格納し直して、n回やり直します!!! ①タイムアウト ②取消 ③キューに格納 ④取消(2回目)

    決済 サブシステム ⑤キューに格納(以下繰り返し) ※さらに一定回数を超えた取引はアラートで検知して手運用に回ります。  タイムアウトは自社の判断なので責任を持ちます。
  12. 36 2. リトライで1件でも多く救う 外部決済機関との通信では リトライできるエラーとそうでないエラーがあります。 エラーの種類 リトライの可否 Connection Reset /

    Refused ◦ Connect Timeout ◦ Unknown Host ◦ Read Timeout × HTTPステータスコード400系 △(多くの場合で必要ありません) HTTPステータスコード500系 × 通信に関するエラーについては救うことができます。 その他は2重課金の恐れがあるためリトライできません。 いずれもTCP接続に関するエラーで リクエストが到達していないことが 保証されているためリトライできる クラウド上にアプリケーションを 配置するようになってから 発生することが増えた
  13. 37 2. リトライで1件でも多く救う RestTemplateではタイムアウト時にResourceAccessExceptionが発生します。 この例外が持つメッセージでタイムアウトの種別を判別することができます。 public String lookup() { try

    { return restTemplate.getForObject("/lookup", String.class); } catch (ResourceAccessException e) { final String message = e.getMessage(); if (message != null && message.contains("Read timed out")) { // Handle read timeout. throw new RuntimeException(e); } else if (message != null && message.contains("connect timed out")) { // Let's retry! throw new RetryableException(e); } else { // Handle other timeout. throw new RuntimeException(e); } } } Java 17では “connect timed out”の”c”が大文字になって “Connect timed out”となっていました。 文字列の検査は危険な実装ですので注意が必要です。
  14. 39 2. リトライで1件でも多く救う @SpringBootApplication @EnableRetry public class SpringFest2021Application { //

    omit } @EnableRetryを付与することで リトライ機能が有効になります。 @Retryable( // リトライ対象とする例外を指定します。 // 抽象化した例外を作成すると見通しが良くなります。 value = RetryableException.class, // 処理が実行される最大数です。 // 3の場合は最低1回の処理と最大2回のリトライが実施されます。 maxAttempts = 3, // リトライの間隔を指定します。 // サンプルの場合は1回目のリトライは1秒後に、 // 2回目のリトライは2秒後に実施されます。 backoff = @Backoff(delay = 1_000, multiplier = 2.0) ) public String lookup() { // omit } @Retryableで設定を指定します。
  15. 43 3. 冪等を導入してリトライしやすいシステムにする 参照がリトライできるのは冪等という性質を持っているためです。 冪等とは何回やっても状態が変わらない ような性質のことをいいます。          ※例えばHTTP RESTのGET, PUT,

    PATCH, DELETEは冪等の性質を持っています。 本当にリトライしたい(=冪等であって欲しい)のは課金リクエストです。 この課金リクエストに冪等の性質を持ち込む工夫をご紹介いたします。 【参考】 冪等についてはStripe社やSquare社など多数の採用実績があります。 Stripe: https://stripe.com/docs/api/idempotent_requests Square: https://developer.squareup.com/docs/working-with-apis/idempotency
  16. 45 App 3. 冪等を導入してリトライしやすいシステムにする ②冪等キーとAppへのリクエスト内容の組み合わせを保管します。 ②以下の組み合わせを保管する。 - 冪等キー - HTTPメソッド

    - エンドポイント - HTTPリクエストボディ idempotency_key: 9c4d5af5-4114-4fd6-ac94-160fee09de41 POST /authorize { "transaction_id": "spring-fest-2021-0001", "amount": 1980 } 保管する組み合わせの例
  17. 46 App 3. 冪等を導入してリトライしやすいシステムにする ③冪等キーとAppからのレスポンス内容の組み合わせを保管します。 ③以下の組み合わせを保管する。 - 冪等キー - HTTPステータスコード

    - HTTPレスポンスボディ idempotency_key: 9c4d5af5-4114-4fd6-ac94-160fee09de41 200 OK { "result": "ok" } 保管する組み合わせの例 ①~③の情報を用いると冪等な振る舞いを持つ APIを作ることができます。
  18. App A 47 App B 3. 冪等を導入してリトライしやすいシステムにする 適用パターン1:2回リクエストをしてしまった ①1回目のリクエスト ②1回目のリクエストに

    対応する処理結果を返却する ③2回目のリクエスト ④②とまったく同じ レスポンスを返却する App Bは何度でも同じレスポンスを返却できます👍 処理は実行しない
  19. App A 48 App B 3. 冪等を導入してリトライしやすいシステムにする 適用パターン2:リクエストがタイムアウトしてしまった ①1回目のリクエストが タイムアウトした

    ②2回目のリクエスト ③①で返却するはずだった レスポンスを返却する App Aは結果が返ってくるまで何度でもリトライできます👍 バックエンドで 処理が実行されている 可能性がある 処理は実行しない
  20. App A 49 App B 3. 冪等を導入してリトライしやすいシステムにする 適用パターン3:「短時間で」2回リクエストをしてしまった ①1回目のリクエスト ②2回目のリクエスト

    ③1回目のリクエストが 処理中のためエラーを返却する ④1回目のリクエストに 対応する処理結果を返却する App Bは処理中は同一のリクエストを受け付けないようにできます👍 処理は実行しない ネットワークの問題による 意図しない再送など NGとするか 冪等に忠実に待たせるか 2択を考えたが 滞留を避けるため NGにすることにした
  21. 51 App A 4-1. 分散トレーシングで処理の流れを追う App B App C Request

    X 複数のアプリケーションで構成されるシステムにリクエストを送信すると...
  22. 52 App A 4-1. 分散トレーシングで処理の流れを追う App B App C Request

    X Log A Log B Log C それぞれのアプリケーションはリクエストに対応するログを出力します。 タイムスタンプを参考にして Log A -> B -> Cの順で見れば 処理の流れを追うことができる。
  23. 53 App A 4-1. 分散トレーシングで処理の流れを追う App B App C Request

    X Log A Log B Log C リクエストが複数になると対応関係を把握することは難しくなります。 Request Y Log A Log B Log C とあるLog Aから どのLog Bにつながっているのか分からない
  24. 54 App A 4-1. 分散トレーシングで処理の流れを追う App B App C Request

    X Log A-X Log B-X Log C-X そこでリクエストごとに固有の通し番号を付与すれば解決する、 という手法が分散トレーシングです。 Request Y Log A-Y Log B-Y Log C-Y
  25. 56 4-1. 分散トレーシングで処理の流れを追う App A App B Spring Cloud SleuthではB3という形式でTraceIDを伝播させています。

    Spring Cloud Sleuthを適切に設定することで、 RestTemplateやRabbitTemplate、KafkaTemplateなど 様々なプロトコルに対応したB3ヘッダを付与してくれます。 これによってSpring Cloud Sleuthを採用したアプリケーション間では 実装者が意識することなくTraceIDを引き継ぐことができます。 TraceID Spring Cloud Sleuth Spring Cloud Sleuth RestTemplate, RabbitTemplate, etc...
  26. 59 4-1. 分散トレーシングで処理の流れを追う Servlet FilterやRestTemplateで自動でSpanを切ってくれますが、 実装で任意のポイントでSpanを切ることもできます。 @NewSpan("span-name") public void spanByAnnotation()

    { // do something } アノテーションで指定する場合 Tracerで指定する場合 private final Tracer tracer; public SpringFest2021Service(Tracer tracer) { this.tracer = tracer; } Span newSpan = tracer.nextSpan().name("span-name"); try (Tracer.SpanInScope ws = tracer.withSpan(newSpan.start())) { // do something } finally { newSpan.end(); } SQLの実行や、 ファイルの読み書きに付与することが多いです。
  27. 4-1. 分散トレーシングで処理の流れを追う App トレースデータのサンプリングレートを設定することができます。 spring: sleuth: sampler: probability: 1.0 #

    割合で設定する。 # rate: 10 # 秒間件数で設定する。 0 〜 100 %の割合で送信 or 秒間n件だけ送信 処理の傾向を掴めば十分である場合には 低めに設定します。 弊社では例外的な遅延も検知したいため probability 1.0 (100%)で運用しています。 開発環境ではZipkinに負荷をかけないために rate 10 (秒間10件まで)で運用しています。
  28. 63 業務 システム 通信 システム Request X 4-2. 分散トレーシングに頼り過ぎない 決済機関

    業務ログ ・トランザクションID ・TraceID 通信ログ ・TraceID 例:HTTPステータス500 決済機関からエラーレスポンスを受領したら、 そのTraceIDを元にトランザクションIDを特定すればよい...と思っていました。 社内の業務システムで使う トランザクションIDもTraceIDから 逆引きをすれば特定できる!
  29. 64 業務 システム 通信 システム Request X 4-2. 分散トレーシングに頼り過ぎない 決済機関

    通信ログ ・TraceID 例:HTTPステータス500 しかし複数のエラー原因が出てくると雲行きが怪しくなり ... Request Y 例:Connect Timeout 前提:通信システムは 抽象化したエラーレスポンスを返却する。 業務ログ ・トランザクションID ・TraceID
  30. 65 業務 システム 通信 システム Request X 4-2. 分散トレーシングに頼り過ぎない 決済機関

    ネットワーク障害などで 同時にエラーが100件を超えて出たあたりで調査が破綻しました。 Request Y Request Z ・・・ 通信システムのログだけで以下のような調査を実施しなければならない。 ・Read TimeoutとHTTPステータス500のトランザクションIDを一覧にしたい ・エンドポイント単位のエラーを業務単位のエラーにマッピングしたい
  31. 66 業務 システム 通信 システム Request 4-2. 分散トレーシングに頼り過ぎない たとえ分散トレーシングを導入したとしても、 トランザクションIDは各システムのログに出力するようにしました。

    トランザクションID 通信システムに自由項目を設けた。 自由項目で受け取った業務 IDをログに出力する。 業務ログ ・トランザクションID ・TraceID 通信ログ ・トランザクションID ・TraceID
  32. 67 業務 システム 通信 システム Request 4-2. 分散トレーシングに頼り過ぎない たとえ分散トレーシングを導入したとしても、 トランザクションIDは各システムのログに出力するようにしました。

    トランザクションID 通信システムに自由項目を設けた。 自由項目で受け取った業務 IDをログに出力する。 業務ログ ・トランザクションID ・TraceID 業務ログ ・トランザクションID ・TraceID 「この方法をシステムの数珠つなぎが100連になってもやりますか」 と聞かれると、「確かにそれは難しい」という回答になります。 これはTagやBaggageという機能で達成することができます。 開発時に「分散トレーシングに依存しすぎないようにしよう」と 2つのシステムだったこともあり採用しました。 (後から振り返ってみればTagで良かったとも感じています) これから開発する場合はTagとBaggageも検討してみてください。
  33. 69 5. 後回しでいいものは非同期にする 弊社では通信に関わるログをセキュリティ要件のため 標準出力やファイルではなくRDBに保管しています。 決済 システム 決済機関 RDB 決済機関

    リクエスト 決済機関 レスポンス 業務 リクエスト 業務 レスポンス INSERT 加盟店様のセールなどで 大量トランザクションが発生した場合に パフォーマンスに影響してしまう。
  34. 71 5. 後回しでいいものは非同期にする Spring Cloud Stream & RabbitMQ Spring Cloud

    Streamでメッセージブローカーに簡単に接続できます。 また弊社ではメッセージブローカーに OSSのRabbitMQを採用しています。 spring-cloud-stream-binder-rabbitをアプリケーションの依存に追加することで、 RabbitMQに簡単に接続する実装を行えるようになります。 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency>
  35. 72 5. 後回しでいいものは非同期にする application.ymlでRabbitMQの接続先とグループ名を定義します。 spring: rabbitmq: host: localhost port: 5672

    username: guest password: guest cloud: stream: bindings: log-in-0: group: log-queue グループ名の定義が RabbitMQのExchange名やQueue名に影響します。
  36. 74 5. 後回しでいいものは非同期にする StreamBridgeを使うことでProducerを実装できます。 Spring Webのハンドラメソッドなど好きなタイミングで起動させられます。 private final StreamBridge streamBridge;

    public SpringFest2021GymService(StreamBridge streamBridge) { this.streamBridge = streamBridge; } public void log() { streamBridge.send("log-in-0", "Hello Spring Fest 2021!"); } RabbitMQでのExchangeを指定する ※Spring Cloud Stream 2.x系であった宣言的な記述は3.x系で非推奨となりました。
  37. 75 5. 後回しでいいものは非同期にする 性能が安定しない場合にも非同期を活用できます。 決済機関 ECサイトA 通知システム 1000 ms ECサイトB

    ECサイトC 1500 ms 3000 ms 3500 ms 60000 ms 60500 ms 複数の加盟店様がいるため 決済機関に対して性能を保証できない エンドユーザーの処理完了が通知される。
  38. 通知受信 システム 76 5. 後回しでいいものは非同期にする MQに格納することで決済機関に素早くレスポンスを返却できます。 決済機関 ECサイトA 通知 システム

    1000 ms ECサイトB ECサイトC 50 ms 3000 ms 60000 ms 50 ms 50 ms 加盟店様によっては 流量や性能をコントロールできる 決済機関には常に一定の性能で レスポンスを返却できる
  39. 通知受信 システム 77 5. 後回しでいいものは非同期にする MQに格納することで決済機関に素早くレスポンスを返却できます。 決済機関 ECサイトA 通知 システム

    1000 ms ECサイトB ECサイトC 50 ms 3000 ms 60000 ms 50 ms 50 ms 加盟店様によっては 流量や性能をコントロールできる 決済機関には常に一定の性能で レスポンスを返却できる 決済機関には即時OKと返しています。 これはタイムアウトと同様に 自社で責任を持つ行為になりますので、 通知システムのリトライの仕組みは 徹底的に設計する必要があります。 「同一の通知が複数送信されることがあります」 などサービス仕様まで踏み込むことも考えられます。
  40. 80 6. 更新頻度が低いデータはキャッシュで持つ もしかしたら必要のないリクエストがあるかもしれません。 決済システム ECサイトA RDB 決済機関 課金リクエスト 加盟店情報

    読み取り 鍵交換 課金 マスタ情報の更新頻度は とても低かったりしないか 鍵の有効期限は 実は1週間ほどあったりしないか
  41. 82 6. 更新頻度が低いデータはキャッシュで持つ @EnableCaching public class SpringFest2021GymApplication { // omit

    } @Cacheable("people") public Person findById(String id) { // omit } @EnableCachingを付与することで キャッシュ機能が有効になる キャッシュしたい値を返却するメソッドに @Cacheableを付与することで2回目以降は キャッシュされた結果が返却される 「引数ごとに」キャッシュされるため それを前提としたメソッドを設計する必要がある
  42. 84 6. 更新頻度が低いデータはキャッシュで持つ @Service public class PeopleService { private final

    Cache<String, String> cache = Caffeine.newBuilder() .maximumSize(3) .expireAfterWrite(Duration.ofMinutes(60)) .build(); public Person findById(String id) { return cache.get(id, i -> { // omit }); } } この場合インメモリで持つ実装のため 必ずサイズ上限とTTLを設けておく。
  43. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 すべての通信は同期通信を前提とします。 サーキットブレーカーがなかった場合
  44. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 決済機関Bに問題が発生すると ... サーキットブレーカーがなかった場合
  45. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 決済機関Bシステムの Webスレッドが埋め尽くされて 滞留します。 サーキットブレーカーがなかった場合
  46. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 決済機関Bの障害に引きづられて 決済機関GW1も滞留します。 決済機関Bとは関係がないはずの 決済機関Aも利用できなくなります。 サーキットブレーカーがなかった場合
  47. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 決済手段Bにまったく関係ない ECサイトYも決済できなくなります。 サーキットブレーカーがなかった場合
  48. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 レスポンスタイムが早いため 決済はできないが滞留することはない サーキットブレーカーを入れた場合
  49. 7. サーキットブレーカーで被害の拡大を防ぐ resilience4j: circuitbreaker: instances: default: # CLOSE failureRateThreshold: 100

    # サーキットブレーカーが OPEN状態となるエラー率 slidingWindowType: TIME_BASED slidingWindowSize: 120 # エラー率を検査する時間幅(秒) minimumNumberOfCalls: 10 # サーキットブレーカーが OPEN状態となるために必要な最低処理数 # OPEN waitDurationInOpenState: 30_000 # OPEN状態からHALF_OPEN状態に移行するまでの待機時間 # HALF_OPEN permittedNumberOfCallsInHalfOpenState: 10 # HALF_OPEN状態での検査処理数 # Exceptions # サーキットブレーカーの検査対象外とする例外クラス ignoreExceptions: - jp.sbps.springfest2021.exception.BusinessException サーキットブレーカーの設定はapplication.ymlに記述します。
  50. 7. サーキットブレーカーで被害の拡大を防ぐ resilience4j: circuitbreaker: instances: default: # CLOSE failureRateThreshold: 100

    # サーキットブレーカーが OPEN状態となるエラー率 slidingWindowType: TIME_BASED slidingWindowSize: 120 # エラー率を検査する時間幅(秒) minimumNumberOfCalls: 10 # サーキットブレーカーが OPEN状態となるために必要な最低処理数 # OPEN waitDurationInOpenState: 30_000 # OPEN状態からHALF_OPEN状態に移行するまでの待機時間 # HALF_OPEN permittedNumberOfCallsInHalfOpenState: 10 # HALF_OPEN状態での検査処理数 # Exceptions # サーキットブレーカーの検査対象外とする例外クラス ignoreExceptions: - jp.sbps.springfest2021.exception.BusinessException 再掲 決済システムでは1件でも 正常な取引ができるものがあれば通したいので エラー率が100%になるまでOPEN状態にしません。 サーキットブレーカーの設定はapplication.ymlに記述します。
  51. 7. サーキットブレーカーで被害の拡大を防ぐ @CircuitBreaker(name = "default") public void exchange() { //

    do something } アノテーションで実装する場合 private final CircuitBreaker circuitBreaker; public SpringFest2021Service(CircuitBreakerRegistry registry) { this.circuitBreaker = registry.circuitBreaker("default"); } public void exchange() { Supplier<String> decoratedSupplier = CircuitBreaker.decorateSupplier( circuitBreaker, service::exchange ); // Use decoratedSupplier as you like. } 手続き的に実装する場合
  52. 7. サーキットブレーカーで被害の拡大を防ぐ 決済機関A ECサイトX ※決済手段 A, Bを利用 決済機関A システム 決済機関

    GW 1 決済機関B 決済機関B システム 決済機関 GW 2 決済機関C 決済機関C システム フロント システム ECサイトY ※決済手段 Cを利用 レスポンスタイムが早いため 決済はできないが滞留することはない サーキットブレーカーを入れた場合 決済機関に通信するネットワークの問題で レスポンスタイム遅延が発生し 全件タイムアウトになったことがありました。 このときはサーキットブレーカーが OPEN状態となり即時返却状態となりました。 15分ほどHALF_OPENと行き来した後に 問題が収まってCLOSE状態となりました。 OPEN CLOSE 取扱量 レスポンスタイム OPENとHALF_OPENを行き来している
  53. 105 8. バルクヘッドで対向システムを守る 決済機関 ECサイト 決済システム 1 TPS 1 TPS

    通常時は決済機関と1TPSで通信をしている状態を考えます。
  54. 109 8. バルクヘッドで対向システムを守る resilience4j: bulkhead: instances: default: maxConcurrentCalls: 20 #

    処理を同時に呼び出せる数 maxWaitDuration: 0 # バルクヘッドに到達したときに諦めるまでの時間 バルクヘッドの設定はapplication.ymlに記述します。 バルクヘッドに到達する状況では 待っても解消の見込みが薄いため即時エラー返却としている。
  55. 110 8. バルクヘッドで対向システムを守る @Bulkhead(name = "default") public void exchange() {

    // do something } アノテーションで実装する場合 手続き的に実装する場合 private final Bulkhead bulkhead; public SpringFest2021Service(BulkheadRegistry registry) { this.bulkhead = registry.bulkhead("default"); } public void exchange() { Supplier<String> decoratedSupplier = Bulkhead.decorateSupplier( bulkhead, service::exchange ); // Use decoratedSupplier as you like. }
  56. 111 まとめ No 手法 対応するライブラリ 想定される効果 1 タイムアウト Spring Web

    - 滞留による全断を防ぐ 2 リトライ Spring Retry - 1件でも多く決済を成立させる 3 冪等 - - リトライ可能にする 4 分散トレーシング Spring Cloud Sleuth / Zipkin - 処理を追跡する - 処理時間の傾向を把握する 5 非同期 Spring Cloud Stream - パフォーマンスを向上させる 6 キャッシュ Spring Cache - 障害ポイントを減らす 7 サーキットブレーカー Resilience4j - 障害の拡大を防ぐ 8 バルクヘッド Resilience4j - 対向先の障害を防ぐ