Docurain Labo

Docurainサービス開発日記

TomcatをVirtualThreadで動作させる

前回の投稿から大分空いてしまいました、、、

本日は技術的なトピックを投稿してみます。

Java19になって仮想スレッド(かつてグリーンスレッドと呼ばれていたもの)が使えるようになりました。とはいえまだExperimentalですが。 JVMの起動パラメータに --enable-preview と付ければ関連APIを実行できるようになります。

(※23-01-10追記) グリーンスレッドとvirtual threadは大きく異なるものでした。 M:Nモデルというモデルです。詳しくはこちら。 https://stackoverflow.com/questions/74639116/what-is-the-difference-between-green-threads-and-virtual-threads

そこで、Tomcatを仮想スレッドで動作させられないか調べてみたら案外簡単に動きました。

まず、このようなクラスを用意します。

package jp.docurain.tomcat;

import java.util.concurrent.Executors;

import org.apache.catalina.LifecycleException;
import org.apache.catalina.LifecycleState;
import org.apache.catalina.util.LifecycleMBeanBase;

public final class VirtualThreadExecutor
    extends LifecycleMBeanBase
    implements org.apache.catalina.Executor {

    private java.util.concurrent.Executor es;

    private String name = "tomcat-vt";

    private String namePrefix = "tomcat-vt-";

    public void setName(final String x) { this.name = x; }

    @Override
    public String getName() { return this.name; }

    public void setNamePrefix(final String x) { this.namePrefix = x; }

    public String getNamePrefix() { return this.namePrefix; }

    @Override
    public void execute(final Runnable command) { this.es.execute(command); }

    @Override
    protected String getDomainInternal() { return null; }

    @Override
    protected String getObjectNameKeyProperties() { return "type=Executor,name=" + this.getName(); }

    @Override
    protected void startInternal() throws LifecycleException {
        this.es = Executors.newThreadPerTaskExecutor(Thread.ofVirtual().name(this.namePrefix, 0).factory());
        this.setState(LifecycleState.STARTING);
    }

    @Override
    protected void stopInternal() throws LifecycleException {
        this.setState(LifecycleState.STOPPING);
    }
}

次に、このクラスをコンパイルしてjarにして、Tomcatのインストールディレクトリのlibへ放り込みます。

つぎにserver.xmlを以下のようにするだけ。簡単ですね。

<Server port="8005" shutdown="SHUTDOWN">

  ...(略)

  <Service name="Catalina">

    <!-- 上記でコンパイルしたクラス -->
    <Executor name="vthread" className="jp.docurain.tomcat.VirtualThreadExecutor" />

    <!-- Executorのnameをexecutorへ指定 -->
    <Connector executor="vthread" port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />

  </Service>

  ...(略)

</Server>

で、起動して何かリクエストを投げてみると、これまでは

2022-11-27 16:47:32,099 INFO  [...] http-nio-8080-exec-1 何かのログ

のようなログだったのが、

2022-11-27 16:47:32,099 INFO  [...] tomcat-vt-1 何かのログ

のように変わっています。 起動中のスレッドを見てみても、TomcatのNioコネクター関連のスレッドが一つもありません。というわけで成功したようです。

実際に商用利用可能なレベルなのか、引き続き調査してみます。本日はここまで。