用虚拟线程编写Http服务器 - piot


在本文中,您将学习如何使用 Java 创建 HTTP 服务器以及如何使用虚拟线程来处理传入的请求。我们将此解决方案与使用标准线程池的 HTTP 服务器进行比较。我们的测试将在大约 200 个并发请求的重负载下比较两种情况下的内存使用情况。

源码: GitHub 存储库


由于虚拟线程在 Java 19 中仍然是预览功能,我们需要在编译期间启用它。对于 Maven,我们需要启用预览功能maven-compiler-plugin,如下所示。

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <version>3.10.1</version>
      <configuration>
        <release>19</release>
        <compilerArgs>
          --enable-preview
        </compilerArgs>
      </configuration>
    </plugin>
  </plugins>
</build>

我们不需要太多的东西就可以用Java创建一个HTTP甚至HTTPS服务器。在Java API中,一个叫做HttpServer的对象可以让我们非常容易地实现它。一旦我们将创建服务器,我们可以用setExecutor方法覆盖一个默认的线程执行器。

无论我们选择哪种类型的执行器,有一个要求是我们的服务器必须满足的。它需要能够同时处理200个请求。因此,对于标准的Java线程,我们将创建一个最大尺寸为200的池。对于虚拟线程来说,创建任何池子都是没有意义的。它们不会消耗很多资源,因为它们直接与操作系统相关。

private static void runServer(boolean virtual, boolean withLock) 
      throws IOException {
   
   HttpServer httpServer = HttpServer
         .create(new InetSocketAddress(8080), 0); // (1)

   httpServer.createContext(
"/example"
      new SimpleDelayedHandler(withLock));
// (2)
   
   if (virtual) {
      httpServer.setExecutor(
            Executors.newVirtualThreadPerTaskExecutor()
      );
// (3)
   } else {
      httpServer.setExecutor(
            Executors.newFixedThreadPool(200)
      );
// (4)
   }

   httpServer.start();
// (5)
}

这就是我们创建一个HTTP服务器的方法。它将在/example上下文路径(2)下监听8080端口(1)。SimpleDelayedHandler对象处理所有进入的请求。根据withLock变量的值,它将模拟不加锁的延迟(false)或使用ReentrantLock(true)。为了简化练习,我们可以使用单个布尔参数在标准(4)和虚拟线程执行器(3)之间进行切换。设置完所有需要的参数后,我们可以启动服务器(5)。

然后,我们需要从主方法中调用runServer方法。我们将根据两个输入参数的值来测试4种情况。

public static void main(String[] args) throws IOException {
   runServer(true, false);
}

运行服务器后,你可以使用以下命令进行测试。

$ curl http://localhost:8080/example


虚拟线程可以减少编写工作量,维护和观察,特别是对于高吞吐量并发应用程序。我们可以像使用标准 Java 线程一样简单地使用它们。本文的目的是向您展示如何从虚拟线程开始构建您自己的解决方案,例如 HTTP 服务器。
然后,您可以轻松比较标准线程和虚拟线程之间的性能差异。