当前位置: 首页 > 知识库问答 >
问题:

避免tomcat/Spring Boot中客户端速度慢导致的线程饥饿

濮阳钟展
2023-03-14

我有一个简单的spring boot应用程序。它有一个endpoint,从请求体中获取一个对象,但不执行任何操作:

@Controller
class FooController {
    @RequestMapping(method=RequestMethod.POST, value="/foo")
    public void postFoo(@RequestBody Foo foo) {
    }
}

非常简单的东西。

然后,我通过telnet连接,并通过适当的标头发送,就像我要发送一个json编码的对象一样,但从不发送请求正文——我只是让连接挂起。

运行jstack,我可以看到tomcat已经将请求发送到spring。Spring把它送给了杰克逊。杰克逊被NIO屏蔽,等待更多数据。

Thread 12128: (state = BLOCKED)
 - sun.misc.Unsafe.park(boolean, long) @bci=0 (Compiled frame; information may be imprecise)
 - java.util.concurrent.locks.LockSupport.parkNanos(java.lang.Object, long) @bci=20, line=226 (Compiled frame)
 ...
 - org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitLatch(java.util.concurrent.CountDownLatch, long, java.util.concurrent.TimeUnit) @bci=18, line=1582 (Compiled frame)
 ...
 - org.apache.tomcat.util.net.NioSelectorPool.read(java.nio.ByteBuffer, org.apache.tomcat.util.net.NioChannel, java.nio.channels.Selector, long) @bci=7, line=227 (Compiled frame)
 - org.apache.coyote.http11.InternalNioInputBuffer.readSocket(boolean, boolean) @bci=103, line=427 (Compiled frame)
...
 - org.apache.catalina.connector.CoyoteInputStream.read(byte[], int, int) @bci=76, line=200 (Compiled frame)
 - com.fasterxml.jackson.core.json.ByteSourceJsonBootstrapper.ensureLoaded(int) @bci=49, line=503 (Compiled frame)
 ...
 - com.fasterxml.jackson.databind.ObjectMapper.readValue(java.io.InputStream, com.fasterxml.jackson.databind.JavaType) @bci=6, line=2158 (Compiled frame)
 - org.springframework.http.converter.json.MappingJackson2HttpMessageConverter.readJavaType(com.fasterxml.jackson.databind.JavaType, org.springframework.http.HttpInputMessage) @bci=11, line=225 (Compiled frame)
 ...
 - org.springframework.web.servlet.FrameworkServlet.doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=3, line=863 (Compiled frame)
 - javax.servlet.http.HttpServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=149, line=646 (Compiled frame)
 - org.springframework.web.servlet.FrameworkServlet.service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse) @bci=32, line=837 (Compiled frame)
 - javax.servlet.http.HttpServlet.service(javax.servlet.ServletRequest, javax.servlet.ServletResponse) @bci=30, line=727 (Compiled frame)
 - org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse) @bci=446, line=303 (Compiled frame)
 ...
 - org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(java.nio.channels.SelectionKey, org.apache.tomcat.util.net.NioEndpoint$KeyAttachment) @bci=140, line=1736 (Interpreted frame)
 - org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run() @bci=94, line=1695 (Compiled frame)
 - java.util.concurrent.ThreadPoolExecutor.runWorker(java.util.concurrent.ThreadPoolExecutor$Worker) @bci=95, line=1145 (Compiled frame)
 - java.util.concurrent.ThreadPoolExecutor$Worker.run() @bci=5, line=615 (Interpreted frame)
 - org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run() @bci=4, line=61 (Interpreted frame)
 - java.lang.Thread.run() @bci=11, line=745 (Interpreted frame)

我的问题是,如果有200人这样做,那么我的线程池就会被饿死,合法的请求无法进入。这似乎是一种针对tomcat上运行的任何东西的非常简单的DOS攻击。

我认为有一种方法可以解决这个问题,那就是让NIO HTTP连接器提前读入其缓冲区。如果是这样,我该如何设置?

即便如此,恶意代理似乎也可以通过向服务发送大型对象来轻松破坏服务。当连接缓慢、错误或恶意客户端时,人们通常如何防止线程饥饿?

共有1个答案

欧阳俊逸
2023-03-14

检查Tomcat中的卡住Thread检测阀。

来自文档:

此阀允许检测需要很长时间处理的请求,这可能表明正在处理它的线程被卡住。此外,它还可以选择性地中断此类线程,以尝试取消阻止它们。

当检测到这样的请求时,其线程的当前堆栈跟踪将写入具有WARN级别的Tomcat日志。

您可以指定请求所需的最长秒数(默认为10分钟!),如果超过这个值,这个阀门就会杀死它们。

 类似资料:
  • 好的,我在Eclipse Neon中开发一个游戏,我注意到当我在程序中添加了一个break语句时,程序的速度从大约120 fps降低到了80 fps(这没有什么意义)。所以我决定在另一个班级进行测试,得到了相似的结果。 这是我运行的代码: 所以当我运行代码时(在Eclipse Neon 2 (4.6.2))我得到了意想不到的结果: 使用中断语句:耗时(MS):344.8(5次测试的平均值) 无中断

  • 我的未插入 如果没有NIO: 对于每个保持Alive连接,服务器会继续阻塞一个线程。 使用NIO: 此连接器有几个轮询线程,用于为所有连接的用户保持连接活动,同时每当数据(新的HTTP请求)可用时都会调用工作线程 现在:如果我有一个简单的servlet,它向客户端(浏览器)返回一个100 KB的字符串。 如果具有1 KB连接的客户端连接,则需要大约100秒来接收字符串<那么java线程会被阻塞大约

  • 我正在使用java客户端连接到hazelcast集群。每次我连接到集群时,我都会观察到在成员的jvm上创建了大约5个客户机线程。这些线程在被销毁之前会停留大约30-40秒。这些线是什么?如何控制创建的客户端线程数? 我正在使用hazelcast all。3.3.3

  • 我对和使用了RXJava。我知道他们不是在一条流上并行化排放。换句话说,单个排放流只会放在一个线程上,对吗?我下面的测试似乎表明了这一点。我的理解是,您还必须调度器,如,以在单个流上并行化发射。 同样,如果是这种情况,那么调度器会发生线程饥饿吗?如果我的计算调度程序有5个线程,但我有超过5个长时间运行的异步流正在处理,有没有可能出现饥饿?或者这不太可能仅仅是因为RXJava的性质?

  • 这可能是基于意见的问题。 我想使用古斯HTTP客户端,因为许多人认为它比Symfony HTTP客户端更好,Cloudflare在其PHP API中也使用古斯HTTP客户端。但是,我使用Symfony HTTP客户端和古斯HTTP客户端执行了一个简单的测试。结果表明,古斯HTTP客户端比Symfony HTTP客户端慢得多。 我想知道/理解为什么拥有如此著名的声誉的HTTP客户端缺乏速度。还是我做

  • 我正在寻求关于一个奇怪问题的帮助,在这个问题中,队列中的一个慢速使用者会导致同一队列中的所有其他使用者以30秒的间隔开始使用消息。这是所有的消费者,但慢速的消费者不会以最快的速度消费信息,相反,他们在消费前等待一些神奇的30s障碍。 null 更多背景和调查结果 我已经设法在AMQ 5.8.0、5.9.0(最初注意到该问题的地方)和5.9.1上、在新安装和现有的Ops管理的安装上以及在不同的机器上