LINEヤフー Tech Blog

LINEヤフー株式会社のサービスを支える、技術・開発文化を発信しています。

出前館クーポンサービスでのサーバーアプリケーションのSpring Boot 3系対応

LINEヤフー Advent Calendar 2023の8日目の記事です。

LINEヤフー Tech blogをご覧の皆さん、こんにちは。京都開発部所属でデリバリーサービス 出前館のサーバーサイド開発を担当している本多康久です。

弊社では、グループ会社である株式会社 出前館のプロダクトを共同で開発しています。今回のブログでは、出前館のサーバーサイドで使用されているフレームワークであるSpring Boot の3系対応の事例について紹介します。

Spring Bootとは

Spring Bootは、Springのプロジェクト群を使用してアプリケーションを簡単に作成するためのOSSのフレームワークです。Spring Bootアプリケーションは、スタンドアローンの独立したJavaアプリケーションとして実行できます。Springのプロジェクト群(Spring Security, Spring Data, Spring Batch, etc.)との相互依存性の管理や自動設定により開発にかかるコストを大幅に減らせる強みがあります。

この強みにより、Spring Bootは出前館をはじめ、LINEヤフーの多くのプロダクトにも採用されています。

2系のサポート期間の終了

メジャーバージョンの2系は2023年11月24日でサポートが終了する(cf. サポート期間)ため、3系へのメジャーバージョンのアップグレードへの対応が必要です。

出前館でのマイクロサービスアーキテクチャ

デリバリーサービスである出前館は、近年、マイクロサービスアーキテクチャを採用し、数十のコンポーネントが稼働しています。出前館のマイクロサービスアーキテクチャの取り組みについては、"20年以上続くフードデリバリーサービス「出前館」におけるマイクロサービス化の取り組み(漆原, Tech-Verse 2022)", "出前館 マイクロサービスにおける加盟店管理画面のBFFアーキテクチャ(古田, LINE KYOTO交流会 2022)"などがありますので、詳細はそちらを参照ください。

本ブログでは、マイクロサービスアーキテクチャとして稼働するサービスの一つである、クーポンドメインを担っているクーポンサービスのSpring Boot 3系対応について紹介します。

バージョンアップグレードにおける具体的な対応内容

バージョンアップ前後の環境の情報は以下の通りです。

Software stacksversion (as-is)version (to-be)
Java1717
Spring Boot2.6.33.1.3
Gradle7.3.38.2.1

作業手順

まず、バージョンアップグレードの際の作業手順と、それぞれの工程にかかった時間を順番に説明します。

Release Notes, Migration Guidesの確認 (1日間)

Spring Bootは、GitHubのRepositoryのWikiページにRelease NotesMigration Guidesがドキュメント化されています。Release Notesには各マイナーバージョンのリリース内容が、Migration Guidesにはメジャーバージョンアップグレードの手順と変更点が記載されています。

アップデートにおけるプロダクトの対象部分の洗い出し(2日間)

Release NotesやMigration Guidesから、サービスの対象になっている部分を洗い出しました。Spring Bootは前述でも記載した通り、Release NotesやMigration Guidesが詳細にドキュメント化されており、開発者が対応すべきポイントを容易に理解できます。

細かな対応部分は省きます。大きなトピックは以下の通りです。

  • バージョンを2系の最新に上げる
  • Spring Securityの対応
  • Spring Batchの対応
  • Jakarta EEへの移行
  • Spring Cloud Sleuthのサポート終了、Micrometer Tracingへの移行

Migration作業(2週間)

洗い出した対象部分をそれぞれ対応しました。

チームメンバーからのレビュー(1週間)

バージョンアップグレードに際して、対応部分の改修、既存コードのリファクタリングを含め、7つのプルリクエスト、総ファイル差分264と大幅な改修になりました。対応箇所の漏れがないか、対応内容が正しいかなどのチェックのために複数人でのレビューを行いました。

総ファイル差分が264と書きましたが、2.7から3.0へ上げる際の差分が最も多く222となりました。Spring Bootのメジャーバージョンをアップデートすると、依存関係を管理するすべてのライブラリのバージョンが上がるので、一つのプルリクエストでかなり多くの差分が入ります。

テスト(1週間)

既存のユニットテスト、インテグレーションテストだけでなく、改修対象の機能を中心にE2Eのリグレッションテストを行い、既存機能の動作確認を行いました。E2Eのリグレッションテストとは、開発環境でユーザーが使うクライアントの画面から、特定の機能を操作し、APIの処理とデータベースの更新状態が正常に行えているか確認するテストを指します。

QA: Quality Assurance(2週間)

出前館でのリリースでは、QAを専用チームがリリースごとに実施しています。今回の対応でも、QAチームがさまざまな観点でテスト計画をたて、既存機能のテストを行いました。

具体的な対応内容

Spring Boot 3系への対応にあたって、Spring Bootが依存性を管理するプロジェクト(cf. Spring | Projects)もバージョンが上がります。下記では、大きなトピック(Spring Security, Spring Batch, Spring Cloud Sleuth)をさらに深掘りして紹介します。

Spring Security

Spring Boot 3系ではSpring Securityのバージョンは6系に上がっています。Spring Securityの5系から6系への対応には、Spring SecurityのMigration Guidesがあります。

以下に、アプリケーションのHTTPリクエストの認可を設定をする部分の対応の抜粋を示します。healthcheckエンドポイントに対する認可設定部分のコード(Java)です。

対応前
public SecurityFilterChain filterChain(HttpSecurity http, PreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter) throws Exception {
    http.authorizeRequests()
        .mvcMatchers(HttpMethod.GET, "/healthcheck").permitAll()
        .anyRequest().authenticated()
        .and()
        :
        (略)
    return http.build();
}
対応後
public SecurityFilterChain filterChain(HttpSecurity http, PreAuthenticatedProcessingFilter preAuthenticatedProcessingFilter) throws Exception {
    http.authorizeHttpRequests(authorize -> authorize
        .requestMatchers(HttpMethod.GET, "/healthcheck").permitAll()
        .anyRequest().authenticated()
    )
          :
        (略)
     return http.build();
}

まず、Lambda DSLへの対応があります。Lambda DSLはSpring Securityを設定するために推奨される記法です。Lambda DSL自体はバージョン5.2からリリースされていて、Lambda DSL を使わない既存の記述方法は6では非推奨、7では無効です。

Lambda DSLを推奨する理由は、可読性の高さ、.and()を使った設定の連鎖が必要なくなる、他Springライブラリ(Spring Integration, Spring Cloud Gateway)と同様の構成スタイルになると”Spring Security - Lambda SDL(Spring blog 2019)“で言及されています。

次に、リクエストのマッチングがmvcMatchersからrequestMatchersに変更されます。

また、authorizeRequestsからの移行があります。authorizeRequestsから、authorizeHttpRequestsにした場合、認可処理において、FilterSecurityInterceptorからAuthorizationFilterが使用されます。AuthorizationFilterはSpring SecurityのSecurityFilterChainの一部として機能するのはFilterSecurityInterceptorと同じです。詳細なアクセス制御、効率的なアクセス制御の評価が可能です。

Spring Batch

Spring Boot 3系ではSpring Batchのバージョンは5系に上がり、後方互換のない破壊的な変更があり、かなり多くの部分の修正が必要です。Spring BatchについてもMigration Guidesが用意されています。対応した部分は、

  • Batch metadataのスキーマ変更
  • JobBuilderFactory, StepBuilderFactoryが非推奨なるため、JobやStepの設定の方法の修正
  • @EnableBatchProcessingの挙動が変更
  • 複数Jobの起動のサポート停止による起動時のプロパティ名の変更
    があります。

今までJobBuilderFactory, StepBuilderFactoryをDIしてJobやStepの設定を行っていました。5.2からはJobBuilderFactory, StepBuilderFactoryがともに非推奨になり、Bean化されなくなりました。そのためJobRepositoryをDIし、設定を行う対応を行いました。以下にJobとStepの設定部分の対応を示します。

対応前
:
@Bean
public Job xxxxxJob(
        Step xxxxxStep
) {
    return jobBuilderFactory.get("xxxxxJob")
                            .listener(jobListener)
                            .start(xxxxxStep)
                            .build();
}

@Bean
public Step xxxxxStep(
        @Qualifier(DatasourceConfig.TRANSACTION_MANAGER)
                PlatformTransactionManager platformTransactionManager
) {
    return stepBuilderFactory.get("xxxxxStep")
                             .transactionManager(platformTransactionManager)
                             .tasklet(xxxxxTasklet)
                             .exceptionHandler(customExceptionHandler)
                             .build();
}
:
対応後
:
@Bean
public Job xxxxxJob(
        Step xxxxxStep
) {
    return new JobBuilder("xxxxxJob", jobRepository).listener(jobListener)
                                                    .start(xxxxxStep)
                                                    .build();
}

@Bean
public Step xxxxxStep(
        @Qualifier(DatasourceConfig.TRANSACTION_MANAGER)
                PlatformTransactionManager platformTransactionManager
) {
    return new StepBuilder("xxxxxStep", jobRepository).tasklet(xxxxxTasklet, platformTransactionManager)
                                                      .exceptionHandler(batchCustomExceptionHandler)
                                                      .build();
}
:

DIしていたjobBuilderFactory, stepBuilderFactoryではなく、それぞれ、JobBuilder, StepBuilderを使って、引数にjobRepositoryを指定するように変更しています。(各変数はフィールドインジェクションしています。)

さらに、Spring BatchのConfigurationクラスに付ける@EnableBatchProcessingアノテーションの挙動変更については、特に注意を要するポイントです。SpringBatchは、Job自体の状態を保持するメタデータやそれぞれのビジネスロジックにおいてデータベースにCRUDを行います。そのCRUD自体の管理は、TransactionManagerというものが行います。バージョン4系まではこのアノテーションを付加すると、TransactionManagerがBean化され、DIコンテナに載っていました。

しかし、バージョン5系からはユーザー定義のTransactionManagerと干渉してしまうという理由でTransactionManagerをBean化しなくなりました。そのため、今まで通りこのアノテーションを付加していてもJobが起動しない状況が生じます。対応は、@EnableBatchProcessingを削除し、@EnableAutoConfigurationアノテーションで自動設定を行います。

@EnableAutoConfigurationは、Spring Bootのアノテーションで、Springが持つエコシステム(Spring Batch, Spring Security, Spring Cloud, …)のBean定義をSpring Bootがあらかじめ用意している設定で行ってくれます。このアノテーションを付加するとTransactionManagerがBean化され、適切にJobが起動できます。

Spring Cloud Sleuth

私たちのプロジェクトでは、コンポーネント内部の処理の分散トレーシングを行うために、相関ID(TraceId, SpanId)を発行すべく、Spring Cloud Sleuthを用いていました。Spring Cloud Sleuthを導入すると、一連の処理の流れで別々に出てくるログを関連づけられ、可観測性が向上します。

Spring Boot 3系では、Spring Cloud Sleuthはサポートされなくなり、動作しません。コアプロジェクトはMicrometer Tracingへ移行されたのでこちらのライブラリへの移行が必要です。移行においてMigration Guidesが用意されています。

対応は、相関ID(TraceId, SpanId)を発行するTracerの自動設定が含まれるspring-boot-starter-actuatorの依存関係の追加と、Tracer(brave)の実装が含まれるmicrometer-tracing-bridge-brave依存関係の追加です。(braveはSpring Sleuthでのデフォルト)また、デフォルトではテストコンテキストでトレーシングがオフになっているのでConfigクラスに@AutoConfigureObservabilityを追加してトレーシングを行うよう設定しました。

まとめ

デリバリーサービス出前館のマイクロサービスアーキテクチャのコンポーネントとして稼働するクーポンサービスのSpring Boot 3系対応について具体的な対応内容の一部を抜粋して紹介しました。

今回、紹介した以外にも、Spring Boot 3系では、GraalVM Nativeイメージのサポートで高速な動作が可能になっていたり、3.1ではDocker Composeのモジュールが追加され、Spring Bootでのコンテナ管理が可能になるなど、さまざまな要素が追加されています。

また、Spring Boot 3.0.xは2.7.xと同じく2023年11月24日にサポートが終了するので、3.1.xまでの対応が必要です。この冬にしっかりと対応して、良い春(Spring)を迎えられるようにしましょう。