加入收藏 | 设为首页 | 会员中心 | 我要投稿 甘南站长网 (https://www.0941zz.com/)- 科技、行业物联网、开发、云计算、云管理!
当前位置: 首页 > 运营中心 > Tomcat > 正文

分析在Tomcat中启用虚拟线程特性

发布时间:2023-02-20 10:01:08 所属栏目:Tomcat 来源:互联网
导读:安装OpenJDK-19或者Oracle JDK-19 准备好嵌入式Tomcat的依赖,需要引入三个依赖包,分别是tomcat-embed-core、tomcat-embed-el和tomcat-embed-websocket,版本选用10.1.0+ 查看Tomcat官方文档的CHANGELOG: 支持Loom项目的Tomcat最低版本为10.1.0-M16,对应
 
  安装OpenJDK-19或者Oracle JDK-19
  准备好嵌入式Tomcat的依赖,需要引入三个依赖包,分别是tomcat-embed-core、tomcat-embed-el和tomcat-embed-websocket,版本选用10.1.0+
  查看Tomcat官方文档的CHANGELOG:

 
  支持Loom项目的Tomcat最低版本为10.1.0-M16,对应的正式版是10.1.0(当前时间为2022-10-07前后),低于此版本因为大量API还没有适配虚拟线程,主要是没有改造监视器锁的引用导致虚拟线程pin到载体(平台)线程等问题,因此别无他选。另外,重要的提醒说三次:

  <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-core</artifactId>
      <version>10.1.0</version>
  </dependency>
  <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-el</artifactId>
      <version>10.1.0</version>
  </dependency>
  <dependency>
      <groupId>org.apache.tomcat.embed</groupId>
      <artifactId>tomcat-embed-websocket</artifactId>
      <version>10.1.0</version>
  </dependency>
 
  编程式初始化Tomcat
  为了使用反射调用一些java.base模块下没开放的依赖包和跟踪虚拟线程栈,程序运行时候加入下面的VM参数:
 
 
  --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/java.util.concurrent=ALL-UNNAMED -Djdk.tracePinnedThreads=full
  在IDEA的运行配置中是这个样子:
 
  public class VirtualThreadHandleServlet extends HttpServlet {
  
      private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
  
      @Override
      protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
          Thread thread = Thread.currentThread();
          System.out.printf("service by thread ==> %s, is virtual ==> %s, carrier thread ==> %sn",
                  thread.getName(), thread.isVirtual(), getCurrentCarrierThreadName(thread));
          resp.setStatus(HttpServletResponse.SC_OK);
          resp.setHeader("Content-Type", "application/json");
          String content = "{"time":" + """ + LocalDateTime.now().format(FORMATTER) + ""}";
          resp.getWriter().write(content);
      }
  
      private static String getCurrentCarrierThreadName(Thread currentThread) {
          if (currentThread.isVirtual()) {
              try {
                  MethodHandle methodHandle = MethodHandles.privateLookupIn(Thread.class, MethodHandles.lookup())
                          .findStatic(Thread.class, "currentCarrierThread", MethodType.methodType(Thread.class));
                  Thread carrierThread = (Thread) methodHandle.invoke();
                  return carrierThread.getName();
              } catch (Throwable e) {
                  e.printStackTrace();
              }
          }
          return "UNKNOWN";
      }
  }
  该Servlet实现比较简单,就是在控制台打印一些虚拟线程和载体线程的一些信息,然后返回HTTP状态码为200和一个JSON字符展示当前精确到毫秒的时间。接着编写一个main方法初始化Tomcat:
 
  public class EmbedTomcatVirtualThreadDemo {
  
      private static final String SERVLET_NAME = "VirtualThreadHandleServlet";
  
      private static final String SERVLET_PATH = "/*";
  
      /**
       * 设置VM参数:
       * --add-opens java.base/java.lang=ALL-UNNAMED
       * --add-opens java.base/java.lang.reflect=ALL-UNNAMED
       * --add-opens java.base/java.util.concurrent=ALL-UNNAMED
       * -Djdk.tracePinnedThreads=full
       *
       * @param args args
       * @throws Exception e
       */
      public static void main(String[] args) throws Throwable {
          String pinMode = System.getProperty("jdk.tracePinnedThreads");
          System.out.println("pin mode = " + pinMode);
          Tomcat tomcat = new Tomcat();
          Context context = tomcat.addContext("", (new File(".")).getAbsolutePath());
          Tomcat.addServlet(context, SERVLET_NAME, new VirtualThreadHandleServlet());
          context.addServletMappingDecoded(SERVLET_PATH, SERVLET_NAME);
          Connector connector = new Connector();
          ProtocolHandler protocolHandler = connector.getProtocolHandler();
          if (protocolHandler instanceof AbstractProtocol<?> protocol) {
              protocol.setAddress(InetAddress.getByName("127.0.0.1"));
              protocol.setPort(9091);
              ThreadFactory factory = Thread.ofVirtual().name("embed-tomcat-virtualWorker-", 0).factory();
              Class<?> klass = Class.forName("java.util.concurrent.ThreadPerTaskExecutor");
              MethodHandle methodHandle = MethodHandles.privateLookupIn(klass, MethodHandles.lookup())
                      .findStatic(klass, "create", MethodType.methodType(klass, new Class[]{ThreadFactory.class}));
              ExecutorService executor = (ExecutorService) methodHandle.invoke(factory);
              protocol.setExecutor(executor);

(编辑:甘南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读