当客户端关闭连接时,Spring StreamingResponseBody请求线程未清除 [英] Spring StreamingResponseBody Request Thread not cleaned up, when client closes connection
问题描述
我在Controller中有一个端点,它返回一个 StreamingResponseBody
,用于将文件发送到客户端。代码看起来大致如下:
I have an endpoint in a Controller, which returns a StreamingResponseBody
, which is used to send a file to the client. The Code for this looks roughly like this:
@RestController
@RequestMapping(value="api")
public class Controller {
static class GetContentResponse implements StreamingResponseBody {
@Override
public void writeTo(OutputStream outputStream) throws IOException {
while (!finished) {
try {
byte[] chunk = <get next chunk>;
outputStream.write(chunk);
} catch (InterruptedException e) {
throw new RuntimeException("Interrupted!", e);
}
}
outputStream.close();
}
}
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<StreamingResponseBody> get(@PathVariable(value = "id") String id)
throws FaultResponse, InterruptedException {
GetContentResponse getContentResponse = new GetContentResponse();
HttpHeaders header = new HttpHeaders();
return new ResponseEntity<>(getContentResponse, header, HttpStatus.OK);
}
}
我通过以下方式设置异步请求的超时时间:
I set the timeout for asynchronous requests via:
@Configuration
public class AsyncConfiguration extends WebMvcConfigurerAdapter {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(5000);
}
}
如果客户取消请求,则为响应挂起,没有超时并且没有清理。该线程的堆栈跟踪是:
If a clients cancels the request, the thread for the response hangs, doesn't timeout and is not cleaned up. The stacktrace of the thread is:
Name: MvcAsync9
State: TIMED_WAITING on java.util.concurrent.CountDownLatch$Sync@4c51a108
Total blocked: 1 Total waited: 31
Stack trace:
sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedNanos(AbstractQueuedSynchronizer.java:1037)
java.util.concurrent.locks.AbstractQueuedSynchronizer.tryAcquireSharedNanos(AbstractQueuedSynchronizer.java:1328)
java.util.concurrent.CountDownLatch.await(CountDownLatch.java:277)
org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitLatch(NioEndpoint.java:1361)
org.apache.tomcat.util.net.NioEndpoint$KeyAttachment.awaitWriteLatch(NioEndpoint.java:1364)
org.apache.tomcat.util.net.NioBlockingSelector.write(NioBlockingSelector.java:114)
org.apache.tomcat.util.net.NioSelectorPool.write(NioSelectorPool.java:172)
org.apache.coyote.http11.InternalNioOutputBuffer.writeToSocket(InternalNioOutputBuffer.java:139)
- locked org.apache.coyote.http11.InternalNioOutputBuffer@62f2053a
org.apache.coyote.http11.InternalNioOutputBuffer.addToBB(InternalNioOutputBuffer.java:197)
- locked org.apache.coyote.http11.InternalNioOutputBuffer@62f2053a
org.apache.coyote.http11.InternalNioOutputBuffer.access$000(InternalNioOutputBuffer.java:41)
org.apache.coyote.http11.InternalNioOutputBuffer$SocketOutputBuffer.doWrite(InternalNioOutputBuffer.java:320)
org.apache.coyote.http11.filters.IdentityOutputFilter.doWrite(IdentityOutputFilter.java:84)
org.apache.coyote.http11.AbstractOutputBuffer.doWrite(AbstractOutputBuffer.java:256)
org.apache.coyote.Response.doWrite(Response.java:501)
org.apache.catalina.connector.OutputBuffer.realWriteBytes(OutputBuffer.java:388)
org.apache.tomcat.util.buf.ByteChunk.append(ByteChunk.java:344)
org.apache.catalina.connector.OutputBuffer.writeBytes(OutputBuffer.java:418)
org.apache.catalina.connector.OutputBuffer.write(OutputBuffer.java:406)
org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:97)
org.apache.catalina.connector.CoyoteOutputStream.write(CoyoteOutputStream.java:90)
org.springframework.security.web.context.OnCommittedResponseWrapper$SaveContextServletOutputStream.write(OnCommittedResponseWrapper.java:537)
com.mackie.test.Controller$GetContentResponse.writeTo(Controller.java:98)
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:108)
org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBodyReturnValueHandler$StreamingResponseBodyTask.call(StreamingResponseBodyReturnValueHandler.java:94)
org.springframework.web.context.request.async.WebAsyncManager$4.run(WebAsyncManager.java:316)
java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
java.util.concurrent.FutureTask.run(FutureTask.java:266)
java.lang.Thread.run(Thread.java:745)
当客户端断开连接时,如何确保线程被正确销毁?
How can I make sure, that threads are properly destroyed, when a client disconnects?
推荐答案
我自己发现了问题:服务器和客户端之间有一个代理,它没有中断连接的关闭正确。没有代理,一切正常。
I found the problem myself: There was a proxy between the server and the client and it didn't relay the closing of the connection correctly. Without the proxy everything works fine.
这篇关于当客户端关闭连接时,Spring StreamingResponseBody请求线程未清除的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!