$30 off During Our Annual Pro Sale. View Details »

Java x Spring Boot Warm up

Java x Spring Boot Warm up

2024/10/27 Presentation for JJUG CCC 2024 Fall
登壇資料

イベント:
https://jjug.doorkeeper.jp/events/177443

Web版のスライド:
https://kazu-kichi-67.github.io/slidev-jjug-ccc-2024-fall/1

---------------------------------

■リンク集

ライフサイクルの図 出典元:
https://shipilev.net/talks/j1-Oct2011-21682-benchmarking.pdf

Liveness and Readiness Probes with Spring Boot:
https://spring.io/blog/2020/03/25/liveness-and-readiness-probes-with-spring-boot

Liveness, Readiness, and Startup Probes:
https://kubernetes.io/docs/concepts/configuration/liveness-readiness-startup-probes/

Interface BeanFactory:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html

Bean Scopes:
https://docs.spring.io/spring-framework/reference/core/beans/factory-scopes.html

Class AbstractRoutingDataSource:
https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/jdbc/datasource/lookup/AbstractRoutingDataSource.html

JEP 310: Application Class-Data Sharing:
https://openjdk.org/jeps/310

JEP 341: Default CDS Archives:
https://openjdk.org/jeps/341

JEP 350: Dynamic CDS Archives:
https://openjdk.org/jeps/350

Class Data Sharing:
https://docs.spring.io/spring-boot/reference/packaging/class-data-sharing.html

GraalVM:
https://www.graalvm.org/

Introducing GraalVM Native Images:
https://docs.spring.io/spring-boot/reference/packaging/native-image/introducing-graalvm-native-images.html

Libraries and Frameworks Tested with Native Image:
https://www.graalvm.org/native-image/libraries-and-frameworks/

CRaC Project:
https://wiki.openjdk.org/display/crac

CRaC/docs:
https://github.com/CRaC/docs

Checkpoint and Restore With the JVM:
https://docs.spring.io/spring-boot/reference/packaging/checkpoint-restore.html#packaging.checkpoint-restore

SnapStart で AWS Lambda 関数の Java コールドスタートを削減する:
https://aws.amazon.com/jp/blogs/news/reducing-java-cold-starts-on-aws-lambda-functions-with-snapstart/

Project Leyden:
https://openjdk.org/projects/leyden/

Leyden Early Access Release:
https://github.com/openjdk/leyden/blob/leyden-ea1-release-notes/README.md

kazu_kichi_67

October 27, 2024
Tweet

More Decks by kazu_kichi_67

Other Decks in Technology

Transcript

  1. 自己紹介 Name - @kazu_kichi_67 Company - 中堅独立系SIer Skills - Java

    / Spring Boot / Scrum Master / DDD / Clean Architecture / etc… Hobby - フットサル / 爬虫類 / キャンプ / サウナ / ウイスキー / クラフトビール / etc…
  2. 背景 ECサイトにおけるカート決済システムのリプレイス案件 Java 21 Spring Boot 3系 Amazon EKS(Elastic Kubernetes

    Service) Domain-Driven Design Onion Architecture バックエンドのAPIアプリケーション
  3. 特殊ルートの実装 コードサンプル 1 @Repository 2 public class OrderRepositoryImpl implements OrderRepository

    { 3 public OrderId create(Order order) { 4 if (order.userId().isWarmup()) { 5 // 暖機運転用の特殊ルート 6 return OrderId.ofWarmup(); 7 } else { 8 // 注文作成 9 } 10 } 11 }
  4. 特殊ルートの実装 コードサンプル 4 if (order.userId().isWarmup()) { 5 // 暖機運転用の特殊ルート 6

    return OrderId.ofWarmup(); 1 @Repository 2 public class OrderRepositoryImpl implements OrderRepository { 3 public OrderId create(Order order) { 7 } else { 8 // 注文作成 9 } 10 } 11 }
  5. Dynamic Dependency Injection コードサンプル Interface BeanFactory Bean Scopes 1 @Configuration

    2 @RequiredArgsConstructor 3 public class WarmupConfiguration { 4 private final BeanFactory beanFactory; 5 private final WarmupService warmupService; 6 7 @Bean 8 @Primary 9 @Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES) 10 public OrderRepository orderRepository() { 11 return beanFactory.getBean(warmupService.getMode() + "OrderRepository", OrderRepository.class); 12 } 13 }
  6. Dynamic Dependency Injection コードサンプル Interface BeanFactory Bean Scopes 4 private

    final BeanFactory beanFactory; 7 @Bean 8 @Primary 9 @Scope(value = "prototype", proxyMode = ScopedProxyMode.INTERFACES) 10 public OrderRepository orderRepository() { 11 return beanFactory.getBean(warmupService.getMode() + "OrderRepository", OrderRepository.class); 12 } 1 @Configuration 2 @RequiredArgsConstructor 3 public class WarmupConfiguration { 5 private final WarmupService warmupService; 6 13 }
  7. Dynamic Data Source コードサンプル ① Class AbstractRoutingDataSource 1 @Component 2

    @RequiredArgsConstructor 3 public class DynamicRoutingDataSource extends AbstractRoutingDataSource { 4 private final WarmupService warmupService; 5 6 @Override 7 protected Object determineCurrentLookupKey() { 8 return warmupService.getMode(); 9 } 10 }
  8. Dynamic Data Source コードサンプル ① Class AbstractRoutingDataSource 3 public class

    DynamicRoutingDataSource extends AbstractRoutingDataSource { 6 @Override 7 protected Object determineCurrentLookupKey() { 1 @Component 2 @RequiredArgsConstructor 4 private final WarmupService warmupService; 5 8 return warmupService.getMode(); 9 } 10 }
  9. Dynamic Data Source コードサンプル ② 1 @Configuration 2 @RequiredArgsConstructor 3

    public class DynamicDataSourceConfiguration { 4 private final DynamicRoutingDataSource dynamicRoutingDataSource; 5 6 ・・・ 7 8 @Bean 9 @Primary 10 public DataSource dataSource() { 11 Map<Object, DataSource> dataSources = new LinkedHashMap<>(); 12 dataSources.put(WarmupMode.PROD, prodDataSource()); 13 dataSources.put(WarmupMode.WARMUP, warmupDataSource()); 14 15 dynamicRoutingDataSource.setTargetDataSources(dataSources); 16 dynamicRoutingDataSource.setDefaultTargetDataSource(prodDataSource()); 17 18 return dynamicRoutingDataSource; 19 } 20 }
  10. Dynamic Data Source コードサンプル ② 12 dataSources.put(WarmupMode.PROD, prodDataSource()); 13 dataSources.put(WarmupMode.WARMUP,

    warmupDataSource()); 14 15 dynamicRoutingDataSource.setTargetDataSources(dataSources); 1 @Configuration 2 @RequiredArgsConstructor 3 public class DynamicDataSourceConfiguration { 4 private final DynamicRoutingDataSource dynamicRoutingDataSource; 5 6 ・・・ 7 8 @Bean 9 @Primary 10 public DataSource dataSource() { 11 Map<Object, DataSource> dataSources = new LinkedHashMap<>(); 16 dynamicRoutingDataSource.setDefaultTargetDataSource(prodDataSource()); 17 18 return dynamicRoutingDataSource; 19 } 20 }
  11. Dynamic Data Source メリ/デメ ⭕️ データを自由に準備できるため、暖機の自由度が高い ⭕️ 暖機効果が大きい ⭕️ 実装・メンテナンスコスト小

    ❌ インフラの整備が必要 一言メモ ややトリッキーではあるが、インフラ構築さえできれば色々なユースケースに対応でき そう
  12. 前半のまとめ 特殊ルート Dynamic Dependency Injection Dynamic Data Source 暖機効果 🔺

    中 🔺 中 ⭕️ 大 実装・メンテナンスコスト ❌ 大 🔺 中 ⭕️ 小 その他 ⭕️ 手軽 ⭕️ クラス毎に切り替え可能 ⭕️ 暖機の自由度が高い ❌ 都度DIあり ❌ インフラ整備の必要あり
  13. 前半のまとめ 特殊ルート Dynamic Dependency Injection Dynamic Data Source 暖機効果 🔺

    中 🔺 中 ⭕️ 大 実装・メンテナンスコスト ❌ 大 🔺 中 ⭕️ 小 その他 ⭕️ 手軽 ⭕️ クラス毎に切り替え可能 ⭕️ 暖機の自由度が高い ❌ 都度DIあり ❌ インフラ整備の必要あり 結論 どのアプローチもデメリット(痛み)を伴う
  14. Class Data Sharing(CDS) Java JEP 310: Application Class-Data Sharing Java

    10で導入された、手動でアーカイブを作成する仕組み JEP 341: Default CDS Archives Java 12で導入された、ユーザの操作なしにデフォルトでCDSが有効になる機能 JEP 350: Dynamic CDS Archives Java 13で導入された、アプリケーション終了時にアーカイブを作成してくれる仕組み Spring Boot Class Data Sharing spring.context.exit=onRefresh は3.2以降、 jarmode=tools extract は3.3以降で利用可能
  15. Class Data Sharing(CDS) メリ/デメ ⭕️ 起動時間の削減 ⭕️ 初回リクエストの遅延緩和 ⭕️ 制限事項が少なく、導入コストは低め

    🔺 ピークパフォーマンスに達するまでの時間へのアプローチではない 🔺 後述のアプローチに比べると効果は控えめ 実体験で約30%ほどの削減効果がありました
  16. Class Data Sharing(CDS) メリ/デメ ⭕️ 起動時間の削減 ⭕️ 初回リクエストの遅延緩和 ⭕️ 制限事項が少なく、導入コストは低め

    🔺 ピークパフォーマンスに達するまでの時間へのアプローチではない 🔺 後述のアプローチに比べると効果は控えめ 実体験で約30%ほどの削減効果がありました
  17. Native化(GraalVM) メリ/デメ ⭕️ 起動時間の大幅な削減 ⭕️ 起動直後からピークパフォーマンス 🔺 利用しているライブラリがちゃんと動くかは要検証 Libraries and

    Frameworks Tested with Native Image 🔺 コンパイルに時間がかかり、開発体験が変わる ❌ アプリケーションの規模などにもよるが、移行のハードルは高め リフレクションのように実行時に決まる要素については、コンパイル時に明示的に指 定する必要がある
  18. Native化(GraalVM) メリ/デメ ⭕️ 起動時間の大幅な削減 ⭕️ 起動直後からピークパフォーマンス 🔺 利用しているライブラリがちゃんと動くかは要検証 Libraries and

    Frameworks Tested with Native Image 🔺 コンパイルに時間がかかり、開発体験が変わる ❌ アプリケーションの規模などにもよるが、移行のハードルは高め リフレクションのように実行時に決まる要素については、コンパイル時に明示的に指 定する必要がある
  19. CRaC(Coordinated Restore at Checkpoint) Java CRaC Project、CRaC/docs アプリケーションのチェックポイントを作成し、復元する形で起動できる 現時点だと、Azul Zulu、Liberica

    JDKで利用可能 Spring Boot Checkpoint and Restore With the JVM spring.context.checkpoint=onRefresh は3.2以降で利用可能 活用事例 SnapStart で AWS Lambda 関数の Java コールドスタートを削減する
  20. CRaC(Coordinated Restore at Checkpoint) Java CRaC Project、CRaC/docs アプリケーションのチェックポイントを作成し、復元する形で起動できる 現時点だと、Azul Zulu、Liberica

    JDKで利用可能 Spring Boot Checkpoint and Restore With the JVM spring.context.checkpoint=onRefresh は3.2以降で利用可能 活用事例 SnapStart で AWS Lambda 関数の Java コールドスタートを削減する
  21. CRaC(Coordinated Restore at Checkpoint) メリ/デメ ⭕️ 起動時間の大幅な削減 ⭕️ ピークパフォーマンスに達するまでの時間 チェックポイントの作成タイミングに依存する

    🔺 チェックポイント作成時にDB接続やファイルハンドルを閉じる必要がある 🔺 シークレットな情報がスナップショットに含まれるリスクがある ❌ Linux KernelのCheckpoint/Restore in Userspace(CRIU)を利用するため、実行環境 に依存する 特権操作が必要になるため、プラットフォームによっては利用できない
  22. CRaC(Coordinated Restore at Checkpoint) メリ/デメ ⭕️ 起動時間の大幅な削減 ⭕️ ピークパフォーマンスに達するまでの時間 チェックポイントの作成タイミングに依存する

    🔺 チェックポイント作成時にDB接続やファイルハンドルを閉じる必要がある 🔺 シークレットな情報がスナップショットに含まれるリスクがある ❌ Linux KernelのCheckpoint/Restore in Userspace(CRIU)を利用するため、実行環境 に依存する 特権操作が必要になるため、プラットフォームによっては利用できない