だらだらと思いついたこととか書くブログ

エンジニア的なネタとか備忘録とかを書いていく予定

API呼び出しをRestTemplateからWebClientに切り替えた話

どんな話?

spring-bootで作成されているマイクロサービス間のAPI通信のクライアントをRestTemplateからWebClientに切り替えてみましたという話。

なぜ切り替えたのか、どのように対応しているのかといった内容を書きます。

なぜ切り替えたのか?

ビジネスロジックの中でいくつか別サービスへの通信を行っているが、いくつか非同期で通信を行える箇所があった。

メッセージキューを利用したりと徐々に非同期化を図っていたが、今回のケースでは解決法としてWebClientを利用するのが適してそうなこともあり試してみた。

そもそもRestTemplateは2022年4月現在でメンテナンスモードとなっており公式でもWebClientの利用が推奨されていたため、近い将来切り替えたいという話も上がっていた。

切り替え前の状況

ビジネスロジック内の1処理としてhttp通信での別サービスの呼び出しを行っている。

この通信自体は即時性が求められるものではなく、非同期での連携で問題ないものではあったが同期的に通信処理を行っていた。

ただし連携開始してから、たびたび通信が不安定になるケースが発生しており、これに起因しての不具合が発生する事がわかった。

そのため非同期連携に切り替えようと思いどんな方法が良いかなと考えたところ、WebClientでノンブロッキングでの連携が適していると考え切り替えを実施した。

切り替えについて

実現したいこと

  • 非同期、ノンブロッキングでのAPI呼び出しを行いたい
  • タイムアウト発生時にはリトライを実施したい(通信状況が不安定なケースでもリトライで復旧するケースも多いため)

実装例

APIクライアントの定義例

@Component
public class ServiceApi {

  private final WebClient webClient;

  public ServiceApi() {
    this.webClient =
        WebClient.builder()
            .baseUrl("URL")
            .build();
  }

  public Mono<Void> create() {
    log.info("cacheUserPlan request={}", request);
    return this.webClient
        .post()
        .uri("URL")
        .retrieve()
        .bodyToMono(Void.class)
        .retryWhen(
            Retry.fixedDelay(2, Duration.ofSeconds(5))
                // タイムアウトエラーが発生した場合には5秒間隔でリトライを行う(2回までAPI呼び出しを試みる)
                .filter(
                    throwable ->
                        throwable instanceof WebClientRequestException
                            && (throwable.getCause() instanceof ConnectException
                                || throwable.getCause() instanceof TimeoutException)));
  }
}

API呼び出し側の実装例

今回はビジネスロジックからの該当のAPI呼び出し部分のみノンブロッキングにしたかったため呼び出し側でsubscriberを利用 (Monoのままプレゼンテーションでレスポンスはしない)

ServiceApi
    .create()
    .subscribe(
        response -> {
            // API呼び出しが成功した場合の処理
        },
        exception -> {
            // API呼び出しでエラーが発生した場合の処理
        });

参考にさせていただいた情報

リトライのテストについて

リトライ周りのテストをどう書くのが良いか分からず少しハマった。

調べてみた限りMockWebServerを利用してモックサーバにAPI接続して試す方法が良く紹介されていたのでそちらを利用してみることにした。

参考にさせていただいた情報

今後について

切り替え後はしばらく様子を見てエラーハンドリングやリトライ設定などを今後必要に応じて見直していきたい。

リトライについてはjitterを利用したexponential backoffの利用なども考えていきたい。