当前位置: 代码网 > 服务器>服务器>Tomcat > 解析在Tomcat中启用虚拟线程特性

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

2024年05月19日 Tomcat 我要评论
前提趁着国庆前后阅读了虚拟线程相关的源码,写了一篇《虚拟线程 - virtualthread源码透视》,里面介绍了虚拟线程的实现原理和使用示例。需要准备做一下前期准备:安装openjdk-19或者or

前提

趁着国庆前后阅读了虚拟线程相关的源码,写了一篇《虚拟线程 - virtualthread源码透视》,里面介绍了虚拟线程的实现原理和使用示例。需要准备做一下前期准备:

  • 安装openjdk-19或者oracle jdk-19
  • 准备好嵌入式tomcat的依赖,需要引入三个依赖包,分别是tomcat-embed-coretomcat-embed-eltomcat-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的运行配置中是这个样子:

接着编写一个httpservlet实现:

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 ==> %s\n",
                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);
        }
        tomcat.getservice().addconnector(connector);
        tomcat.start();
    }
}

这里virtualthreadhandleservlet匹配所有格式的请求路径并且处理所有请求方法类型的请求。默认的虚拟线程调度器没有为虚拟线程设置名称,也就是如果使用executors.newvirtualthreadpertaskexecutor()作为tomcat的线程池是最终调用看到的控制台输出的虚拟线程名称是一个空字符串。所以笔者这里用methodhandle直接实例化了默认修饰符没有开放访问权限的threadpertaskexecutor类,基于一个自定义的threadfactory强制构造了一个自定义threadpertaskexecutor实例。调用main方法启动后见控制台输出:

这里确认了tomcat启动完成侦听127.0.0.1:9091,通过浏览器或者postman发送任意请求例如http://127.0.0.1:9091/foo就能看到响应结果和控制台输出:

这里的tomcat线程池甚至可以设计为一个完全自定义的虚拟线程调度器,可以参考前面一篇文章,这里不再赘述。

暂时无法在springboot体系中使用

由于servlet规范问题,tomcat的升级导致一些接口迁移到jakarta.servlet包中,例如jakarta.servlet.servlet,此时springboot体系即使是最新版本(当前时间为2022-10-07前后,此时最新版本为2.7.4)使用的是还是旧的规范,对应的类是javax.servlet.servlet,这只是其中一个接口,大部分和http协议或者servlet规范相关的接口都存在这个包升级不兼容的问题,需要等待springboot升级为embed-tomcat-*-10.1.0+才能适配虚拟线程。

小结

demo项目仓库:

githubhttps://github.com/zjcscut/framework-mesh/tree/master/tomcat-virtual-thread

到此这篇关于在tomcat中启用虚拟线程特性的文章就介绍到这了,更多相关tomcat启用虚拟线程内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

  • tomcat 启动时卡住问题排查及解决方法

    tomcat 启动时卡住问题排查及解决方法

    正常项目无法访问(linux 服务器),启动tomcat时卡在下图位置,项目无法启动。1、先检查tomcat日志、项目日志没有报错信息,且没再产生新的日志信息。... [阅读全文]
  • Tomcat架构设计及Servlet作用规范讲解

    1.servlet规范1.1 servlet作用讲解servlet是javaee规范中的一种,主要是为了扩展java作为web服务的功能,统一定义了对应的接口,比如servlet接…

    2024年05月19日 服务器
  • Tomcat服务器的配置与启动全过程

    Tomcat服务器的配置与启动全过程

    tomcat的下载与安装tomcat是apache软件基金会的一个开源免费的项目 , 它是一个轻量级web服务器 , 体积小 , 运行速度快(只实现了servl... [阅读全文]
  • Tomcat生命周期详解

    引言在上篇文章中我们看到了tomcat架构中的核心组件,而且各个组件都有各自的作用,各司其职,而且相互之间也有对应的父子关系,那么这些对象的创建,调用,销毁等操作是怎么处理呢?也就…

    2024年05月19日 服务器
  • war包部署到Tomcat下运行的实现步骤

    war包部署到tomcat下运行1、配置jdk安装路径到环境变量2、配置tomcat安装路径到环境变量3、从eclipse导出war包在控制台console中查看war包保存的位置…

    2024年05月19日 服务器
  • Tomcat启动核心流程示例详解

    一、tomcat的启动核心流程前面给大家介绍了tomcat中的生命周期的设计,掌握了这块对于我们分析tomcat的核心流程是非常有帮助的,也就是我们需要创建相关的核心组件,比如se…

    2024年05月19日 服务器

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com