有人在使用 Spring MVC 的非 Spring Boot 应用程序中使用虚拟线程吗?当使用任务调度程序执行计划任务时,我们可以毫无问题地让它工作,但是我们无法让请求控制器方法(例如用 @GetMapping、@PostMapping 等注释的方法)来使用它们。我们使用的环境是Spring 6.1(使用XML配置),Java 21,Tomcat 10.1.18。
我们定义了一个
SimpleAsyncTaskExecutor
,我们将其与 ThreadPoolTaskScheduler
一起使用,并且可以很好地执行计划任务:
<bean class="org.springframework.core.task.SimpleAsyncTaskExecutor">
<property name="virtualThreads" value="true"/>
</bean>
我们用来确定是否正在使用虚拟线程的方法是:
Thread.currentThread().isVirtual()
以下是我们的Tomcat server.xml文件中与虚拟线程相关的配置:
<Executor name="tomcatThreadPoolVirtual" class="org.apache.catalina.core.StandardVirtualThreadExecutor"/>
<Connector port="8080" protocol="org.apache.coyote.http11.Http11NioProtocol"
redirectPort="8443"
maxPostSize="-1"
useVirtualThreads="true"
/>
是的,虚拟线程可以在非 Spring Boot Spring MVC 应用程序中工作。如果它是一个要部署到 Servlet 容器/Web 服务器(例如 Tomcat)的 Web 应用程序,那么
@Controller
请求处理方法是否将在虚拟线程或平台线程上执行将取决于该容器的配置。
就像OP正确所说的那样,从Tomcat版本10.1.18开始,连接器的协议必须使用
useVirtualThreads
属性进行配置,如
<Connector ...
useVirtualThreads="true" />
这适用于任何
org.apache.tomcat.util.net.AbstractEndpoint
子类,特别是 NIO 和 NIO2。注意,配置
<Executor name="tomcatThreadPoolVirtual" class="org.apache.catalina.core.StandardVirtualThreadExecutor"/>
是不必要的,因为
AbstractEndpoint
硬编码了 org.apache.tomcat.util.threads.VirtualThreadExecutor
的用法,后者创建了名为 http-nio-<port>-virt-<number>
的虚拟线程。
在 servlet 容器之外/对于非 web-app 基于 Spring 的应用程序,就 Spring 而言,可以通过使用
@Async
注解 Spring Bean 方法来实现虚拟线程的使用:
@Service
public class HomeService {
@Async
public void goHome() {
...
}
}
这个方法的调用看起来像这样
@Autowired
private HomeService homeService;
...
homeService.goHome();
异步机制应该正确配置:使用
@EnableAsync
注释到 @Configuration
类,或者,如果使用 XML 配置,如 @EnableAsync 的 Spring XML 等效项中所述
如果您认为某些异步方法应该在虚拟线程上执行,而有些则不应该,那么您可以定义多个
org.springframework.core.task.TaskExecutor
:一个 - 用于虚拟线程,另一个 - 用于平台线程:
@Bean
public TaskExecutor platformExecutor() {
return new ThreadPoolTaskExecutor();
}
@Bean
public TaskExecutor virtualExecutor() {
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
executor.setVirtualThreads(true);
return executor;
}
,那么异步方法可能如下所示:
@Async("virtualExecutor")
public void goHomeVirtually() {
...
}
@Async("platformExecutor")
public void goHome() {
...
}
可以在 GitHub 存储库中找到完整的工作示例。