ネイティブに向けて: Spring&Dubbo AOT テクノロジーの例と原理の説明

著者: リウ・ジュン

クラウド コンピューティングの時代において、Java アプリケーションは、遅い「コールド スタート」、高いメモリ使用量、長いウォームアップ時間などの問題に直面しており、サーバーレスなどのクラウド デプロイメント モードにうまく適応できません。GraalVM はこれらの問題を大幅に解決します。これらの問題と GraalVM の使用制限を解決するために、Spring や Dubbo などの主流フレームワークも対応する AOT ソリューションを提供しています。

この記事では、クラウド時代に Java アプリケーションが直面する課題、GraalVM Native Image がこれらの問題をどのように解決するか、GraalVM の基本概念と動作原理を詳細に分析し、最後に共通のマイクロサービスを Spring6 と統合する方法を示します。 + Dubbo3 マイクロサービス アプリケーションの例 サービス アプリケーションは静的にパッケージ化されます。

この記事は主に次の 4 つの部分に分かれています。

  1. まず、現在のクラウドコンピューティングの急速な発展においてクラウドアプリケーションが持つべき特性と、Javaアプリケーションがクラウド上で直面する課題について見ていきます。
  2. 次に、GraalVM、Native Image とは何か、そして GraalVM を介して Java アプリケーションを静的に印刷し、Native Image から実行可能なバイナリ プログラムを生成する方法について紹介します。
  3. 3 番目のパートでは、GraalVM の使用には特定の制限があることがわかりました。たとえば、Java リフレクションなどの動的機能はサポートされていないため、これらの制限を回避するには特別なメタデータ構成を提供する必要があります。このパートでは、その方法について説明します。 AOT 処理の追加 Spring6 フレームワークでの AOT 処理、Dubbo3 フレームワークでの AOT 処理など、メタデータの自動構成を実現するために導入されました。
  4. 最後に、Spring6+Dubbo3 アプリケーションの例を使用して、このような Java アプリケーションを静的にパッケージ化する方法を示します。

クラウド時代の Java アプリケーションが直面する課題

まず、クラウドコンピューティング時代のアプリケーションの特徴と、クラウド時代にJavaが直面する課題を見てみましょう。さまざまな統計機関が提供するデータから判断すると、Java 言語は現在でも開発者にとって最も人気のあるプログラミング言語の 1 つであり、一部のスクリプト開発言語に次いで 2 番目です。Java言語は業務アプリケーションを効率よく開発できる言語であり、その豊かな生態性から開発・運用効率が非常に高く、Java言語をベースとしたアプリケーションが数多く開発されています。

画像

しかし、クラウド コンピューティングの時代になると、Java アプリケーションの展開と運用は多くの問題に直面し始めました。サーバーレスを例に挙げてみましょう. サーバーレスは、クラウド上のますます主流の導入モデルです. これにより、開発者はビジネス ロジックにより集中し、高速な弾力性を通じてリソースの問題の解決に役立ちます. 最新のデータによると、Java はすべてのクラウド コンピューティングに占める割合ベンダーのサーバーレス ランタイムの割合は高くはなく、従来のアプリケーション開発におけるその割合に匹敵するほどではありません。

画像

この主な理由は、Java アプリケーションがサーバーレス シナリオのいくつかの重要な要件を満たせないことです。

  • 1つ目は起動速度の問題で、Javaのコールドスタートアップには時間がかかります。Java アプリケーションのプルアップ時間は数秒から数十秒程度であるため、これは高速ポップアップを必要とするサーバーレス シナリオにとって非常に大きな課題です。
  • 2 点目は、Java アプリケーションは最高のパフォーマンス状態を実現するために一定のウォームアップ時間を必要とする場合が多く、起動したばかりのアプリケーションに比較的大きなトラフィックを割り当てるのは適切ではなく、リクエストのタイムアウトや高リソースなどの問題が発生します。頻繁に使用されるため、Java アプリケーションの実効プルアップ時間がさらに長くなります。
  • 3 点目は、Java アプリケーションの動作環境に関する要件であり、多くの場合、大量のメモリとコンピューティング リソースが必要ですが、これらはビジネス自体に割り当てられず、一部の JVM ランタイムで消費されます。これはクラウドを使用するのとは異なります。コストを削減するため、効率向上の目標を組み合わせて一致させます。
  • 最後に、Java アプリケーションによって作成されるパッケージまたはイメージも非常に大きいため、全体としての保存と取得の効率にも影響します。

次に、パッケージ化およびランタイム テクノロジである GraalVM が、Java アプリケーションが直面するこれらの問題をどのように解決するかを見てみましょう。

GraaIVM の概要

GraalVM は、Java アプリケーションを事前にスタンドアロン バイナリにコンパイルします。このバイナリは即座に起動し、ウォームアップなしで最高のパフォーマンスを提供し、使用するリソースが少なくなります。

公式の紹介によると、GraalVM は Java アプリケーション向けの AOT コンパイル機能とバイナリ パッケージング機能を提供しており、GraalVM に基づくバイナリ パッケージは、高速起動、超高性能、ウォームアップ時間なし、リソース消費の非常に少ないことを実現できます。ここで言う AOT とはコンパイル時に発生する技術的な略語、つまり Ahead-of-time のことであり、これについては後で説明します。一般に、GraalVM は 2 つの部分に分割できます。

  • まず第一に、GraalVM は完全な JDK リリース バージョンであり、この時点では OpenJDK と同等であり、jvm 指向の言語で開発された任意のアプリケーションを実行できます。
  • 次に、 GraalVMは、独立して実行できるバイナリ パッケージにアプリケーションをパッケージ化できるネイティブ イメージパッケージング テクノロジを提供します。このパッケージは、JVM なしで実行できる自己完結型アプリケーションです。

画像.png

上の図に示すように、GraalVM コンパイラは、JIT と AOT の 2 つのモードを提供します。

  • JIT の場合、Java クラスが .class 形式のファイルにコンパイルされることは誰もが知っています。ここでコンパイルした後、それは jvm によって認識されるバイトコードです。Java アプリケーションの実行中に、JIT コンパイラはいくつかのバイト コードをコンパイルします。バイト コードはマシンにコンパイルされます。コードの実行速度が向上しました。
  • AOT モードの場合、コンパイル中にバイトコードをマシンコードに直接変換するため、実行時の jvm への依存が直接保存され、jvm のロードとバイトコード実行時のウォームアップの時間が節約されるため、AOT でコンパイルおよびパッケージ化されたプログラムの実行効率は非常に高くなります。

画像

一般に、JIT を使用すると、アプリケーションはより高度な処理能力を備え、リクエストの最大遅延を示す重要な指標を減らすことができます。一方、AOT は、バイナリ パッケージの言及が小さくなり、実行状態での記述が少なくなり、アプリケーションのコールド スタート速度をさらに向上させることができます。メモリなどのリソースが必要となります。

ネイティブイメージとは何ですか?

GraalVM のネイティブ イメージの概念については、上で何度も説明しました。ネイティブ イメージは、Java コードを実行可能なバイナリ プログラムにコンパイルおよびパッケージ化するテクノロジです。パッケージには、アプリケーション独自のコードや標準の依存関係など、ランタイムに必要なコードのみが含まれています。パッケージ、言語ランタイム、および JDK ライブラリに関連付けられた静的コード。このパッケージの操作には jvm 環境は必要ありませんが、もちろん特定のマシン環境にバインドされているため、異なるマシン環境に対して個別にパッケージ化する必要があります。Native Image には、次のような一連の機能があります。

  • JVMの実行に必要なリソースの一部のみが含まれており、ランニングコストが低くなります。

  • 起動時間 (ミリ秒)

  • 始動後はベストな状態となり、暖機運転は不要です。

  • より軽量なバイナリ パッケージとしてパッケージ化できるため、展開がより迅速かつ効率的になります。

  • より高いレベルのセキュリティ

画像

要約すると、重要な項目は次のとおりです。起動速度の高速化、リソース使用量の削減、セキュリティ脆弱性のリスクの軽減、バイナリ パッケージ サイズのコンパクト化です。Sererless などのクラウド コンピューティング アプリケーション シナリオで Java アプリケーションが直面する未解決の問題を解決します。

GraaIVM Native Imageの基本原理と使用法

次に、GraalVM の基本的な使用法を見てみましょう。まず、ネイティブ イメージに必要な関連する基本的な依存関係をインストールする必要があります。これは、オペレーティング システム環境によって異なります。次に、GraalVM JDK ダウンローダーを使用してネイティブ イメージをダウンロードできます。 -画像。すべてがインストールされたら、2 番目のステップで、native-image コマンドを使用して Java アプリケーションをコンパイルおよびパッケージ化できます。入力にはクラス ファイル、jar ファイル、Java モジュールなどを指定でき、最後に実行可能ファイルにパッケージ化されます。ここの Hello World のように、独立して実行されます。さらに、GraalVM は、パッケージ化プロセスを容易にする、対応する Maven および Gradle ビルド ツール プラグインも提供します。

画像.png

GraalVM は、「閉じた世界の仮定」と呼ばれる概念に基づいており、すべてのランタイム リソースとプログラムの動作がコンパイル中に完全に決定できることが必要です。この図は、特定の AOT のコンパイルとパッケージ化のプロセスを示しています。左側のアプリケーション コード、ウェアハウス、JDK などはすべて入力として使用されます。GraalVM は、アクセス可能なすべてのコードと実行パスをスキャンするためのエントリ ポイントとして main を使用します。処理中、いくつかの初期化前のアクション、最終的な AOT コンパイル済みマシン コード、および初期化リソースなどの一部の状態データは、実行可能なネイティブ パッケージにパッケージ化されます。

従来の JVM デプロイメント モードと比較すると、GraalVM ネイティブ イメージ モードは大きく異なります。

  • GraalVM は、コンパイルおよび構築中に main 関数をエントリ ポイントとして使用し、アプリケーション コードの静的分析を完了します。
  • 静的解析中に到達できないコードは削除され、最終的なバイナリ パッケージには含まれません。
  • GraalVM は、リフレクション、リソース リソースの読み込み、シリアル化、動的プロキシなど、コード内の一部の動的呼び出し動作を認識できません。すべての動的動作は制限されます。
  • クラスパスは構築段階で固定化され、変更できません
  • 遅延クラスロードはサポートされなくなり、利用可能なすべてのクラスとコードはプログラムの起動時に決定されます。
  • 使用が制限されている他の Java アプリケーション機能もいくつかあります (事前のクラス初期化など)。

GraalVM はリフレクションなどの動的機能をサポートしていませんが、アプリケーションやフレームワークの多くはリフレクションや動的プロキシなどの多くの機能を使用しています。これらのアプリケーションをネイティブ イメージとしてパッケージ化して静的を実現するにはどうすればよいでしょうか? GraalVM はメタデータ構成エントリを提供します。すべての動的機能の構成ファイルを提供することによって、「閉じた世界の仮定」モードが確立され、GraalVM がコンパイル時に予期されるすべての動作を認識できるようになります。ここでは 2 つの例を示します。

  1. ここに反映されているエンコード方法などのエンコード方法に関しては、GraalVM はコード分析を通じてメタデータを計算できます。

画像

画像

  1. もう 1 つの例は、追加の json 構成ファイルを提供し、指定されたディレクトリ META-INF/native-image// の下に配置することです。

画像

AOT処理

Java アプリケーションまたはフレームワークでのリフレクションなどの動的機能の使用は、GraalVM の使用に影響を与える障害であり、多くのフレームワークにはこの制限があります。アプリケーションまたは開発者がメタデータ構成を提供する必要がある場合、それは非常に困難になります。したがって、Spring や Dubbo などのフレームワークでは、AOT コンパイルまたは AOT コンパイルの前に、AOT 処理または AOT 前処理が導入されています。AOT 処理は、自動化されたメタデータ収集を完了し、メタデータを AOT コンパイラに提供するために使用されます。

画像

AOT コンパイルのメカニズムはすべての Java アプリケーションに共通ですが、AOT コンパイルと比較すると、AOT 処理によるメタデータの収集プロセスはフレームワークごとに異なります。これは、フレームワークごとにリフレクション、動的プロキシなどの独自の使用法があるためです。

典型的な Spring + Dubbo マイクロサービス アプリケーションを例に挙げると、このアプリケーションの静的パッケージ化を実現するには、Spring、Dubbo、および多数のサードパーティ依存関係のメタデータ処理プロセスが必要になります。

  • スプリング - スプリングAOT処理
  • Dubbo - Dubbo AOT 処理
  • サードパーティのライブラリ - 到達可能性メタデータ

Spring では、Spring アプリケーションの静的前処理をサポートするために Spring AOT メカニズムが Spring6 でリリースされました。Dubbo も最近バージョン 3.2 で Dubbo AOT メカニズムをリリースし、Dubbo 関連コンポーネントがネイティブ前処理を自動的に実装できるようにしました。これに加えて、2 つのフレームワークは緊密に連携しています。ビジネス開発に関連するものです。多くの場合、アプリケーションには多数のサードパーティの依存関係が存在します。これらの依存関係のメタデータも、静的依存関係に影響を与える鍵です。リフレクション、クラス読み込み、その他の動作が含まれる場合、メタデータは次のことを行う必要があります。これらのサードパーティ アプリケーションには現在 2 つのチャネルがあり、1 つは GraalVM によって公式に提供される共有スペースで、依存するメタデータ構成のかなりの部分が利用可能であり、もう 1 つは公式リリースを要求する方法です。コンポーネントのメタデータ設定を含めると、GraalVM はどちらの場合でもメタデータを自動的に読み取ることができます。

メタデータ構成: https://github.com/oracle/graalvm-reachability-metadata

春のAOT

次に、コンパイル前に Spring AOT によって実行される前処理作業を見てみましょう Spring フレームワークには、自動構成や条件付き Bean など、多くの動的機能があります。Spring AOT は、これらの動的な特性、構築フェーズでの前処理、および GraalVM で使用できる一連のメタデータ入力の生成を目的としています。ここで生成されるコンテンツには次のものが含まれます。

  • 次の図に示すように、Spring Bean は関連コードを事前に定義します。
  • ビルドフェーズ中に動的プロキシ関連のコードを生成する
  • リフレクションなどで使用される一部のJSONメタデータファイルについて

画像.png

ダボAOT

Dubbo AOT の機能は Spring AOT と非常によく似ていますが、Dubbo AOT は次のような Dubbo フレームワークの独自の使用法に合わせて特別に前処理される点が異なります。

  • SPI拡張関連のソースコード生成
  • 一部のリフレクションでは、JSON 構成ファイルを使用して生成されます。
  • RPC プロキシ クラスのコード生成

画像

画像

Spring6+Dubbo3 デモ

次に、Spring AOT、Dubbo AOT などを使用して、Spring6 + Dubbo3 のサンプル マイクロサービス アプリケーションを通じてアプリケーションのネイティブ イメージ パッケージ化を実現する方法を示します。

完全なコードサンプルはここからダウンロードできます: https://github.com/apache/dubbo-samples/tree/master/1-basic/dubbo-samples-native-image

ステップ 1: GraalVM をインストールする

  1. Graalvm 公式 Web サイトで、ご使用のシステムに応じて対応する Graalvm バージョンを選択します: https://www.graalvm.org/downloads/

  2. 公式ドキュメントに従ってネイティブ イメージをインストールします: https://www.graalvm.org/latest/reference-manual/native-image/#install-native-image

ステップ 2: プロジェクトを作成する

このサンプルアプリケーションは一般的なマイクロサービスアプリケーションであり、アプリケーション構成の開発にはSpringBoot3を、RPCサービスの定義と公開にはDubbo3を使用し、アプリケーション構築ツールにはMavenを使用しています。

画像.png

画像.png

ステップ 3: Maven プラグインを構成する

焦点は、spring-boot-maven-plugin、native-maven-plugin、および dubbo-maven-plugin の 3 つのプラグイン構成を追加し、AOT 処理を有効にし、dubbo-maven-plugin の mainClass をフルパスに変更することです。必要なスタートアップ クラスの。(API の使用方法では、spring-boot-maven-plugin の依存関係を追加する必要はありません。)

<profiles>
        <profile>
            <id>native</id>
            <build>
                <plugins>
                    <plugin>
                        <artifactId>maven-compiler-plugin</artifactId>
                        <configuration>
                            <release>17</release>
                            <fork>true</fork>
                            <verbose>true</verbose>
                        </configuration>
                    </plugin>
                    <plugin>
                        <groupId>org.springframework.boot</groupId>
                        <artifactId>spring-boot-maven-plugin</artifactId>
                        <executions>
                            <execution>
                                <id>process-aot</id>
                                <goals>
                                    <goal>process-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.graalvm.buildtools</groupId>
                        <artifactId>native-maven-plugin</artifactId>
                        <version>0.9.20</version>
                        <configuration>
                            <classesDirectory>${project.build.outputDirectory}</classesDirectory>
                            <metadataRepository>
                                <enabled>true</enabled>
                            </metadataRepository>
                            <requiredVersion>22.3</requiredVersion>
                        </configuration>
                        <executions>
                            <execution>
                                <id>add-reachability-metadata</id>
                                <goals>
                                    <goal>add-reachability-metadata</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <groupId>org.apache.dubbo</groupId>
                        <artifactId>dubbo-maven-plugin</artifactId>
                        <version>${dubbo.version}</version>
                        <configuration>
                            <mainClass>com.example.nativedemo.NativeDemoApplication</mainClass>
                        </configuration>
                        <executions>
                            <execution>
                                <phase>process-sources</phase>
                                <goals>
                                    <goal>dubbo-process-aot</goal>
                                </goals>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

ステップ 4: ネイティブ関連の依存関係を Pom 依存関係に追加する

さらに、Dubbo の場合、現在のネイティブ メカニズムの一部は JDK17 およびその他のバージョンに依存しているため、Dubbo はデフォルトで一部のパッケージをリリース バージョンにパッケージ化しないため、dubbo-spring6 アダプテーションと dubbo-native コンポーネントという 2 つの追加の依存関係を追加する必要があります。

<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-config-spring6</artifactId>
    <version>${dubbo.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.dubbo</groupId>
    <artifactId>dubbo-native</artifactId>
    <version>${dubbo.version}</version>
</dependency>

ステップ 5: コンパイラ、プロキシ、シリアル化、ロガーを調整する

同時に、この例のサードパーティ コンポーネントのサポートは、現在、主にサードパーティ コンポーネントの到達可能性メタデータに制限されています。たとえば、現在サポートされているネットワーク通信またはエンコード コンポーネントには Netty と Fastjson2 が含まれ、サポートされているログ コンポーネントには Logback が含まれ、マイクロサービス コンポーネントには Nacos、Zookeeper などが含まれます。

  • 現在サポートされているシリアル化メソッドは Fastjson2 です
  • コンパイラとプロキシは現在 jdk のみを選択できます
  • ロガーは現在 slf4j を設定する必要があり、現在はログバックのみをサポートしています

構成例は次のとおりです。

dubbo:
  application:
    name: ${spring.application.name}
    logger: slf4j
    compiler: jdk
  protocol:
    name: dubbo
    port: -1
    serialization: fastjson2
  registry:
    id: zk-registry
    address: zookeeper://127.0.0.1:2181
  config-center:
    address: zookeeper://127.0.0.1:2181
  metadata-report:
    address: zookeeper://127.0.0.1:2181
  provider:
    proxy: jdk
    serialization: fastjson2
  consumer:
    proxy: jdk
    serialization: fastjson2

ステップ 6: コンパイル

プロジェクトのルート パスで次のコンパイル コマンドを実行します。

  • APIで直接実行
mvn clean install -P native -Dmaven.test.skip=true
  • アノテーションとxmlメソッド(Springboot3統合メソッド)
mvn clean install -P native native:compile -Dmaven.test.skip=true

ステップ 7: バイナリ ファイルを実行する

バイナリ ファイルは target/ ディレクトリにあり、バイナリ パッケージの名前は通常、target/native-demo などのプロジェクト名です。

要約する

GraalVM テクノロジーは、クラウド コンピューティング時代の Java アプリケーションに新たな変化をもたらし、Java アプリケーションの起動の遅さやリソース占有の解決に役立ちましたが、同時に GraalVM の使用にはいくつかの制限も見えてきたため、Spring6 、SpringBoot3、および Dubbo3 はすべて、対応するネイティブ ソリューションが提供されます。Dubbo に加えて、Spring Cloud Alibaba も静的パッケージング ソリューションを推進しており、次に nacos、sentinel、seata など 2 つのフレームワークの周囲のエコロジー コンポーネントを中心に全体的なネイティブ静的検証を推進します。

関連リンク:

[1] Apache Dubbo ブログ

https://dubbo.apache.org/zh-cn/blog/

人民大学の卒業生らが全学生の情報を盗んで美人採点サイトを構築、刑事拘束された NTアーキテクチャをベースにしたWindows版QQが正式リリース 米国は中国の利用を制限トレーニング AI モデルを提供する Amazon、Microsoft などのクラウド サービスの オープンソース プロジェクトが機能開発を停止すると発表 2023 年に最も高給の技術職であるLeaferJS がリリース: オープンソースの強力な 2D グラフィックス ライブラリである Visual Studio Code 1.80 が サポート端末画像機能 . スレッド登録数3,000万突破 「変化」 deepin、7月のApple M1データベースランキングに合わせてAsahi Linux採用 :Oracle急上昇、再びスコア拡大
{{名前}}
{{名前}}

おすすめ

転載: my.oschina.net/u/3874284/blog/10087336