Vert.x:ハイ・パフォーマンスI/O Toolkitを軽く触ってみた

はじめに

こんにちは、システムエンジニアのキュウです。今回は個人プロジェクトの開発によく使われているオープンソースライブラリ「Vert.x」を紹介します。

Vert.xとは

Vert.xはNettyをベースに構築したJVM上のノンブロッキングI/O処理ライブラリです。使いやすいハイ・パフォーマンスI/O Toolkitとして、WebやGUIやミドルウェアなどのアプリケーション構築によく使われています。

大きな特徴はEvent BusVerticleFutureの3つです。詳しい紹介と使い方はドキュメントが充実した公式サイトを参照してください。

パフォーマンスが良い理由

Vert.xはなぜハイ・パフォーマンスなのでしょうか?アーキテクチャやソース解析をすべて説明するのは難しいですが、効率的にI/Oイベントを処理するために重要なポイントについて、自分の理解を簡単に説明します。

VerticleとThread

VerticleインスタンスはJavaのThreadと連携して、Single thread風のコーディングスタイルを作っているのが一番面白い特性だと思います。Event Busに流されたイベントを消化するため、Vert.xはThread poolとVertical poolから、それぞれ1つのインスタンスを拾ってバインドします。処理する時、VerticleとThreadインスタンス間の関係は以下のようなイメージです。

VerticleとThread間のマッピング

ThreadインスタンスはVerticleインスタンスと1対多、VerticleインスタンスはThreadと1対1の関係です。いわゆるVert.xのスケジューリングアルゴリズムで、1つのVerticleインスタンスは処理開始から終了まで、必ず同じThreadインスタンスとバインドされます。

その結果、Verticleインスタンス内の共有資源(ステータスやカウンターなどのフィールド変数)に対して、同期処理(Java Locks、synchronizedなど)を考える必要は無くなり、実装者は、処理ロジックを考えることに集中できます。

Verticleインスタンス間の通信方式

複数ProcessやThread間の安全な通信方式の1つとして、ベーシックなActorモードを取り上げます。このモードでは、処理ユニット(Actor)間の情報交換はメッセージオブジェクトで行います。他Actorからのメッセージを受信したら処理を開始する仕組みとなり、以下のような通信ネットワークを作ります。

Actorモード

Vert.xの場合、Verticle間通信のメカニズムはEvent Bus経由で複雑な通信ネットワークを簡潔に構築しています。

Vert.xのActorモード

I/Oイベントの処理方式

例えば、Webアプリのリクエストからレスポンスを返すまでの流れは、「read -> decode -> compute -> encode -> send」の5つのステップとなります。

少数のThreadを利用して大量のリクエストを処理する方式の1つとして、ベーシックなReactorモードを取り上げます。このモードでは、下図のようにAcceptorがクライアントからのリクエストを拾ってReactorに渡します。そしてReactor内部のDispatcher経由で効率的に各処理のHandlerに割り当てます。

Reactorモード

Vert.xの場合、以下のイメージのような拡張実装となります。

Vert.xのReactorモード

Event Bus、Verticle、Actorモード、複数のポイントを合わせると、I/Oの各処理は以下のイメージとなります。

Actorモード、VerticleとEventBus

テストしてみる

メカニズム的な話のみだとピンとこないかもしれません。そこで、Vert.xはどのぐらいの性能になるか、Spring BootVert.x Webの2つのフレームワークを使って、簡単なテストで比較してみました。(Vert.x WebはVert.xをベースにしたWebフレームワークで、Spring Bootよりかなりシンプルです)

テストケース

上記は、ランダムな数値をDatabaseから抽出して、ヒットした回数を計上して、クライアント側へ結果を返却するケースです。

1.実験環境

Client:
Window 11 laptop, JMeter
Server:
1C2G Ubuntu 22.04 VM on NAS Server(自宅のNASではこのスペックのVMを作るのが、せいいっぱいでした、ごめんなさい~)
Web Framework:
Spring Boot 2.7.0
Vertx-Web 4.3.2
Database: PostgreSQL 14
JDK:OpenJDK-17

2.テストコード

簡単なテストなので、Spring Bootでの実装コードは割愛します。

すべてのコードではありませんが、Vert.x Webの方のみ、重要な部分を記載します。(両方ともコーディング上のチューニングはせずフレームワークのUnboxing状態のままで機能を実装しています。全ソースコードはこちら参照)

3.実験結果

下表はJMeterで5,000リクエストを並行してテストした結果です。

JMeter結果

Verticleインスタンス×5(1つのVerticleクラスを5つのインスタンスで並列実行)以外、すべて実行エラーが発生してしまいました。しかし、エラーとなったリクエストを除けば、Vert.x WebのTPSは明らかに高いことが分かります。

起動時間については、Standby状態になるまで(JVMの起動時間を含む)、Spring BootよりVert.x Webの方がかなり速いです。

起動時間

最後に、参考としてCPUとメモリの使用率について、以下の計測結果を参照すれば、結果がひと目で分かります。

Vert.x Web Verticle×1
CPUとメモリの使用率:Vert.x Web Verticle×1
Vert.x Web Verticle×5
CPUとメモリの使用率:Vert.x Web Verticle×5
Spring Boot
CPUとメモリの使用率:Spring Boot

感想

Vert.xは効率的なライブラリと言われていますが、実際の使用には注意すべきポイントがあります。Futrueは非同期処理の粒度を簡単にコントロールでき、性能面が優れています。ただし、複雑なロジックの場合、Java lambdaの大量使用によってコールバック地獄に陥りやすく、ソースの理解と今後のメンテナンスに悪影響を与えます。コーディングスタイル上の工夫も重要です。また、業務系開発に便利な内蔵機能やツールなどはほぼ提供されていません。

個人的な意見ですが、大規模な業務系アプリの開発だと、Spring系以外では、RedHat社がVert.xをベースに開発したQuarkusフレームワークがより良い選択肢かもしれません。