我们可以使用 Jetty 10 的org.eclipse.jetty.util.VirtualThreads.Configurable.setVirtualThreadsExecutor(Executor)
API 将其配置为使用可用的虚拟线程。两者都ExecutorThreadPool
实现QueuedThreadPool
了这个API。我们QueuedThreadPool
现在配置 Jetty 使用 a
[spring-projects/spring-boot]启用虚拟线程后,配置 Jetty 以使用它们
回答
当使用Jetty推荐的方法时,会有一个使用平台线程的池。这些平台线程用于 NIO 选择/接受/销毁。当分派调用用户代码的操作时AdaptiveExecutionStrategy
,将使用虚拟线程。该虚拟线程不计入池的最大大小 - 这意味着使用虚拟线程时可以有(更多)并发调用用户代码。
我们可以考虑部署一个ConnectionLimit来防止连接级别上的无限制调用。
new QueuedThreadPool(200, 0, 60000, 0, null, null, Thread.ofVirtual().name("jetty-", 0).factory());
创建一个仅使用虚拟线程的池(最多 200 个,最小 0 个,60 秒空闲时间)。但这会池化虚拟线程(不推荐),并且可能会对性能或其他方面产生其他副作用。我认为坚持 Jetty 编程指南是个好主意。
我在这里制作了一些原型:https://github.com/mhalbritter/spring-boot/tree/mh/virtual-threads。
它添加了一个@ConditionalOnVirtualThreads
,如果在 Java 21 上运行且spring.main.use-virtual-threads
设置为,则启用true
。完成后,在 Jetty 服务器上JettyVirtualThreadsWebServerFactoryCustomizer
配置以使用虚拟执行器。ThreadPool
QueuedThreadPool
为了防止 DoS,我添加了通过设置属性或调用 来限制 Jetty 最大并发连接数的可能性ConfigurableJettyWebServerFactory.setMaxConnections
。使用虚拟线程时会自动JettyVirtualThreadsWebServerFactoryCustomizer
将连接限制为最大池大小,因此运行 Jetty 的虚拟线程的行为应与运行 Jetty 的平台线程大致相同。
再想一想,也许自动限制连接并不是一个好主意 - 通过连接保持活动,这可能会变得非常糟糕。
我不确定我们是否应该useVirtualThreads
添加SpringApplication
。感觉像是与该类别并没有真正联系的东西。
那是我的建议。在实现细节方面,惰性初始化略有不同,因为它SpringApplication
安装了LazyInitializationBeanFactoryPostProcessor
,而它不会为虚拟线程做任何事情(我不认为)。不过,从用户的角度来看,我认为它们非常相似,因为它们都是对整个应用程序产生影响的配置设置。正是从这个角度思考它,让我觉得这可能是一个很好的补充SpringApplication
。不过我对此感觉并不强烈。
看看分支的变化,似乎大部分更新都是自动配置的,这让我有点不愿意改变SpringApplication
。使用现有的原型代码,该类SpringApplication
添加属性源以确保该属性可用。这对我来说有点不寻常,也让我想知道我们是否应该重新考虑它的添加位置。
我们这样的房产并不多。也许CloudPlatform
非常接近,但也有点奇怪,因为它用作spring.main.cloud-platform
属性,但实际上并未绑定到SpringApplication
.
我想知道人们在实践中会如何使用虚拟线程。他们可能只想将它们用于堆栈的某些部分,这也会使全局属性过于广泛。也许我们可能还需要细粒度控制和某种全局默认设置。例如:
spring.application.threads.builder=platform
server.jetty.threads.builder=virtual
他们可能只想将它们用于堆栈的某些部分,这也会使全局属性过于广泛
我的期望是情况不会如此。会有例外(如 Jetty 的接受器线程),使用虚拟线程没有意义,但我希望那些想要使用虚拟线程的人希望它们在任何对其有意义的地方使用。
我想知道人们在实践中会如何使用虚拟线程。
我的应用程序严重依赖事件。我有 50 多个事件,其中十几个事件绑定到事务上下文,其他事件是 @Async 因此,对于纤程,我期望更好的性能,因为不会存在与操作系统线程相关的潜在限制,即 threadPoolTaskScheduler.setPoolSize、AsyncConfigurer
关于从另一面命名 - 作为一名开发人员,实际上,我并不关心你如何命名它。对我来说,最重要的是在迁移时减少工作量,例如 - hibernate 决定“帮助”方言。因此,我花了一些时间寻找解决方案,然后我在 SO 上发布了问题 - https://stackoverflow.com/questions/76444941/springboot-3-hibernate-6-how-to-configure-version-of- mysql 方言
附:我的事件应用程序 - https://github.com/sergmain/metaheuristic
抱歉迟来的评论。
当前配置了虚拟线程池的 Jetty 行为有点奇怪 - 在负载下,它启动所有max
(默认为 200 个)平台线程,但使用虚拟线程进行调度。因此,您有 200 个无用的平台线程占用本机内存。
当基于 QueuedThreadPool 为 Jetty 配置虚拟线程时,减少max
可用 CPU 核心是有意义的。
请参阅我的实验项目 - https://github.com/cit-consulting/boot-loom-jdbc
Jetty 使用平台线程来调度接受 HTTP 连接所需的一些操作。然后通过虚拟线程完成对用户代码的分派。
我认为在使用虚拟线程时默认降低池中最大平台线程的数量不是一个好主意。这是Jetty 团队推荐的方法。它使用new QueuedThreadPool()
默认的最大线程数 200。
我们相信 Jetty 会在这里做正确的事。如果您不希望 200 个线程执行(用您的话来说)“无用”的工作,您可以使用服务器属性来减少池中的最大线程数。
我不只是思考,而是分析 - 200 个线程中只有 2-4 个执行实际工作。该线程用于解码 HTTP 帧,因此它仅用于 CPU 工作。所以它在数量 > CPU 数量方面没有用处。
你是对的 - 这不是 Spring 问题。我已向 Jetty 提交问题。
请链接回此问题(或将 Jetty 问题链接放在这里),以便我们可以跟踪此问题。谢谢你!