Quarkus による Apache Camel のパフォーマンス向上

こんにちは、ソリューションアーキテクトの蒸野(ムシノ)です。

今回は、Red Hat Developerのブログ Apache Camel boosts Quarkus performance | Red Hat Developer の翻訳記事を紹介させて頂きたいと思います。 一部、記事の内容を分かりやすくするため、オリジナルにない補足事項を追加しています。ご承知おきいただけらと思います。


Camel Quarkusは、 Apache Camelコミュニティのサブプロジェクトであり、Quarkus上でCamelを実行できるようにするものです。Apache Camelは、こちらでも説明させていただいたように、あらゆるシステム統合を解決することを目的としたオープンソースコミュニティプロジェクトです。Quarkusは、OpenJDK、HotSpotとGraalVMで動作し、早い起動時間と低いメモリ使用率が特徴のJavaフレームワークです。

この記事では、 Camel が長い時間とともにどのように変化してきたか、そしてなぜ Quarkus を採用するのかについて説明します。 また、クイックガイドとして、Camel Quarkus プロジェクトを作成し、パフォーマンスの大きな利点を体験していただけるように簡単なサンプルを示したいと思います。

Apache Camel が特別な理由

Apache Camel はコンテナやKubernetesが登場するよりも先んじて存在していました。他のIntegration製品に対して、 Apache Camel の大きな利点は、非常に軽量に保たれている点です。誤解を恐れずに言うと、Camel はJavaプロジェクトに組み込む小さなライブラリです。 その軽量さ故に、新しいトレンドに素早く適応し、さまざまなランタイム(Webコンテナ、Java EEサーバー、OSGi、Spring Bootなど)に適合することができました。Apache Camel は進化と改良を続け、長年にわたって利用され、利用実績が豊富である機能を提供するという大きな長所があります。

他のIntegration製品が誕生し、初期の成功を収め停滞している間、 Apache Camel コミュニティは、風の変化をよく読み、必要に応じて新しい船体や帆を進化させながら航海し続けていると言えます。

コンテナにおけるJavaのメモリ使用量

Kubernetes とコンテナが登場する以前は、Javaプロセス上にサービスを束ねることが理にかなっていました。多くのIntegrationサーバ(エンタープライズ・サービス・バス)は、インフラとメモリ消費を節約するために、多くのIntegrationプロセスを一つに集約していました。

コンテナの出現により、Integrationのアプローチは大規模なIntegrationサーバから独立した実行アプリケーションに移行し始め、マイクロサービスとして知られるようになりました。しかし開発者は、この新しいアプローチが非常にメモリを消費することにすぐに気づきました。

新しい言語などはメモリフットプリントを最小化するのに役立つように見えましたが、Javaが長い時間をかけて成長してきた機能やライブラリの代替をもたらすものではありませんでした。しかし、Javaの利用を継続するとしても、メモリ使用量に問題があるのなら何か新たな手を打つの必要がありました。

Welcome to Camel Quarkus

Camel Quarkus は、Camel コアライブラリとその豊富なコネクターコンポーネントを Quarkus にもたらします。 Quarkus は、クラウドベースのアプリケーションにおけるJavaの使用を促進する一方で、 非Kubernetes ユーザーがベアメタル上で最大のパフォーマンスでアプリケーションを実行することを可能にします。

Quarkus のミッションは、最小限のメモリで超高速にJavaを実行し、コンテナベースのプラットフォームでの実行を最適化し、ランニングコストを削減することです。以下のサブセクションでは、これらの目標について説明します。

パフォーマンスの向上

Quarkus は、 Apache Camel のパフォーマンスを飛躍的に向上させ、 Camel を実行する他のJavaランタイムを凌駕しています。 Camel Quarkus の改善点として、以下が挙げられます。

  • 起動時間が数秒以下
  • 低メモリーフットプリント
  • ウォームアップの必要なし
  • サーバーレス特性(Camel-K)

下記は、基本的なRESTful API呼び出しとRESTful API呼び出しおよびデータベースアクセスの2つの操作について、さまざまなデプロイメントの起動時間を比較したものです。ネイティブコンパイルによる Quarkus は、実質的に瞬時に終了します。通常のJavaジャストインタイム(JIT)コンパイラーを使用した Quarkus は、従来のJavaよりもはるかに高速です。ネイティブモードの Quarkus は、RESTサービスの起動に数百分の1秒しかかからないなど、速さが際立っています。

下記は、同じ操作のメモリフットプリントを示しています。Quarkusをネイティブモードで使用した場合、消費量は最小限なレベルまで低下します。

ライブコーディング

効率的な開発の重要な側面として、コーディングとテスト中に快適さと生産性を感じることであり、これは「developer joy」としても知られています。 Quarkus で Camel との統合を行う主な利点の1つはライブコーディングモードです。このモードでは、トライアンドエラーを繰り返し、コンパイル、起動、再試行のサイクルを早めることができます。ライブコーディングモードでは、コードを変更すると、それが即座に検出されて適用されるため、失敗してもすぐに再試行することができます。例えば、ライブコードモードでの更新は、 Apache Camel を使い始めたばかりでまだ理解が深まっていないときに非常に役に立ち、事実上、学習プロセスを加速させます。

下記の図は、 Apache Camel ルートをコーディングおよび更新する際に、ライブコーディン グモードがどのように機能するかを示しています。

Quarkus のライブコーディングモードでは、コードを更新するたびに、新しいコードの再実行がトリガーされます。 Quarkus コミュニティは、コード作成を簡素化し、開発者の生産性を高めることにも役立っています。たとえば、Integrationサービスを実装する際に、DevやProd環境ごとに異なる設定を提供する必要がある場合、 Camel Quarkus では、1つの設定ファイルですべての環境に対応するプロパティを定義することができます。

例えば次のように Camel ルートでバックエンドサービスと統合する必要があるとします。次の XML 定義で Camel の定義行っています。 この XML は、括弧の間に定義されたパラメータによってターゲットホストを指定しています。

<to uri="http:{{api.backend1.host}}/camel/subscriber/details"/>

次に、エンドポイントを設定するために、設定ファイル内でプロパティを指定します。最初のプロパティはデフォルト(本番環境)の設定値を定義し、2番目のプロパティはライブコーディングモードを有効にしたときに有効になるdev環境用のプロパティを設定しています。

api.backend1.host = end1:8080
%dev.api.backend1.host = localhost:9000

Camel Quarkus on Camel 3

Camel Quarkus は、 Apache Camel の進化版である Camel 3 で生まれました。この時点で、 Camel Quarkus は Camel のプロジェクトファミリーとなりました。

  • Camel 3: コアフレームワークと統合ツール
  • Camel Quarkus:QuarkusのためのCamel拡張機能
  • Camel K: Kubernetesのための軽量なサーバーレス統合プラットフォーム

Camel のバージョン2からバージョン3になった主な理由の1つは、 Camel Quarkus と Camel K に誕生させることでした。これらは、皆さんも推測されているように、コンテナ上で Camel を実行することを示しています。 Camel-K は Kubernetes環境 でのみ利用可能ですが、Camel Quarkusはコンテナでもスタンドアロンでも実行可能です。

Camel 3 では、 Quarkus 上で動作させるために多くの内部改良が施されています。特に、Javaリフレクションの使用を排除するために多くの作業が行われ、 Camel はネイティブモードで実行できるようになりました。もう1つの大きな仕事は、Camelフレームワークのコアをモジュール化することでした。これにより、メモリのフットプリントを大幅に削減し、起動時間や実行速度を高速化することができました。

Camel Quarkus のサンプル実行

Camel Quarkus が何であるかを理解したところで、簡単なサンプルを実行してその利点を体験してみたいと思います。 ここでは、Camel Quarkusの最初の一歩を踏み出すために、簡単なサンプルで理解することを意図しています。

前提条件

このサンプルを再現するためには、以下の要件がシステムにインストールされていることを確認してください。

  • JDK 11+ (任意のディストリビューション)
  • Maven 3.6.2以上
  • オプションで ネイティブコンパイル用GraalVM 22.3

ジェネレーターの設定

下記の記事を参考にスケルトンを作成します rheb.hatenablog.com

ここではタイマーコンポーネントを使用して画面に簡単なメッセージをログとして出力するサンプルを作成します。

Quarkusのコードジェネレーターで、「camel」と入力しフィルタリングします。 コードジェネレーターインターフェースには、Quarkusで使用できるすべての Apache Camel 拡張機能が表示されます。

Java DSL と XML DSL を使用した 2 つの Camel ルートを作成します。

フィルタされたリストから、Camel Timer と Camel XML を選びます。選択済みのものがインターフェイス上に表示されます。 「Generate your application」 と書かれた赤いボタンをクリックし、プロジェクトを生成してダウンロードします。

Work on the Camel code

ZIPファイルをダウンロードし、適切なフォルダでプロジェクトを解凍してください。 次に、 Camel Java DSLルート を含む以下のJavaクラス定義を「src/main/java/org/acme/TimerRoute.java」ファイルに追加してください。

package org.acme;
import org.apache.camel.builder.RouteBuilder;

public class TimerRoute extends RouteBuilder {
    @Override
    public void configure() throws Exception {
        from("timer:java?period=1000").id("route-java")
        .log("Hello World from Java DSL");
    }
}

そしてターミナルに下記コマンドを入力して、開発モードのCamel Quarkusアプリケーションを起動します。

mvn clean quarkus:dev

Quarkusが起動すると、次のようなメッセージが何度も繰り返し表示されるはずです。

INFO  [route-java] (Camel (camel-1) thread #1 - timer://java) Hello World from Java DSL
INFO  [route-java] (Camel (camel-1) thread #1 - timer://java) Hello World from Java DSL
INFO  [route-java] (Camel (camel-1) thread #1 - timer://java) Hello World from Java DSL
・・・

コードの修正

Quarkus Camel アプリケーションを開発モードで実行すると、ライブコーディングモードが有効になっているので、コード修正してみます。 XML DSL を使用して2つ目のCamelルートを追加することで、その動作を確認してみましょう。 次の XML 定義を「src/main/resources/routes.xml」に追加します。

<routes xmlns="http://camel.apache.org/schema/spring">

    <route id="route-xml">
        <from uri="timer:xml?period=1000"/>
        <log message="Hello World from XML DSL"/>
    </route>

</routes>

「src/main/resources/application.properties」を編集して、Camel に XML ルート定義の場所を知らせる次のプロパティを追加します。

camel.main.routes-include-pattern = classpath:routes.xml

「保存」をクリックすると、 Quarkus が変更を即座に検出して自動的に再起動し、新しいCamel XML定義が追加されます。 ターミナルにJava DSLとXML DSLの両方のメッセージが表示されるはずです。

INFO  [route-java] (Camel (camel-1) thread #1 - timer://java) Hello World from Java DSL
INFO  [rou.xml:5] (Camel (camel-1) thread #2 - timer://xml) Hello World from XML DSL
INFO  [route-java] (Camel (camel-1) thread #1 - timer://java) Hello World from Java DSL
INFO  [rou.xml:5] (Camel (camel-1) thread #2 - timer://xml) Hello World from XML DSL
・・・

パフォーマンスの測定

ログの詳細を見ると、Apache CamelとQuarkusがいかに早く再起動したかが分かります。

... Apache Camel 3.18.3 (camel-1) started in 71ms
... code-with-quarkus 1.0.0-SNAPSHOT on JVM (powered by Quarkus 2.13.5.Final-redhat-00003) started in 1.632s.

しかし、 Quarkus の本来のパフォーマンスは、プロジェクトをネイティブモードでビルドしたときに発揮されます。 次のコマンドを実行します。

mvn clean package -Pnative

ビルドプロセスは数分で完了します。完了したら、ネイティブアプリケーションで実行します。

./target/*-runner

アプリケーションの出力を注意深く観察すると、起動時に消費される時間がわかります。 次のログは、ネイティブモードで動作するCamel Quarkusの超高速の起動時間を示しています。

... Apache Camel 3.18.3 (camel-1) started in 2ms
... code-with-quarkus 1.0.0-SNAPSHOT native (powered by Quarkus 2.13.5.Final-redhat-00003) started in 0.510s.

メモリ使用量についてはどうでしょうか。以下のコマンドを実行し、物理RAM上のプロセスへのメモリ割り当てを反映する常駐セットサイズ(RSS)を検査します。

ps -o rss,command

このコマンドの出力は、次のように表示されるはずです。

30408 ./target/code-with-quarkus-1.0.0-SNAPSHOT-runner

RSS使用量が30MBほどであることを示しています。

最後に

如何でしたでしょうか?
Camel Quarkusは、Apache Camel に新たな次元をもたらす重要な技術的な進化といえます。Quarkusは、非常に高速で反応性の高いシステム統合と、「パフォーマンスの向上」、「developer joy」、そして Quarkus が体現するコンテナファーストのスタートラインとなります。
ご意見をお待ちしております。

参照リンク:

* 各記事は著者の見解によるものでありその所属組織を代表する公式なものではありません。その内容については非公式見解を含みます。