我想允许客户端(包括非常慢的客户端)从JAX-RS(泽西岛)Web服务下载大文件而且我被卡住了。似乎JAX-RS中的异步胖子不支持这一点。
AsyncResponse
解决了该问题,但您只能拨打AsyncResponse.resume(Object)
一次。之后,响应正常处理。慢速或恶意客户端将阻塞工作线程,直到传输完所有字节。这里没有异步IO。ChunkedOutput
将块存储在无限的内存中队列中,并且不提供任何公共接口来检查该队列的大小。它设计用于缓慢的小块流。足够慢的客户端最终会导致OutOfMemoryError
。StreamingOutput
根本不是异步的。 StreamingOutput.write(OutputStream)
方法应该阻塞,直到写入所有字节为止。HttpServletRequest.startAsync
)的方法,而不会破坏jerseys内部。 - > IllegalStateException
我没有看到明显的解决方案吗?
答案 0 :(得分:4)
对于合适的新版球衣和码头,以下作品:
@Suspended AsyncResponse
注入您的jax-rs请求处理程序方法。这告诉球衣进入异步模式并保持请求打开。@Context HttpServletRequest
以访问servlet级API。HttpServletRequest.getAsyncContext()
而不是HttpServletRequest.startAsync()
,因为泽西已经切换到异步模式并再次执行此操作会导致IllegalStateException
(这是我上面的问题)。AsyncContext
。泽西岛不抱怨。AsyncContext.complete()
,然后致电AsyncResponse.cancel()
。我认为后者是可选的。我设法以这种方式向100个并发客户端提供10GB文件。线程数从未超过~40个线程,内存消耗很低。我的笔记本电脑的吞吐量约为3GB / s,这有点令人印象深刻。
@GET
public void doAsync(@Suspended final AsyncResponse asyncResponse,
@Context HttpServletRequest servletRequest)
throws IOException {
assert servletRequest.isAsyncStarted();
final AsyncContext asyncContext = servletRequest.getAsyncContext();
final ServletOutputStream s = asyncContext.getResponse().getOutputStream();
s.setWriteListener(new WriteListener() {
volatile boolean done = false;
public void onWritePossible() throws IOException {
while (s.isReady()) {
if(done) {
asyncContext.complete();
asyncResponse.isCancelled();
break;
} else {
s.write(...);
done = true;
}
}
}
});
}
答案 1 :(得分:0)
我遇到了类似你的问题。我需要在我的应用程序的两个实例之间传输大量数据。最初我使用简单的StreamingOutput方法,但很快我就明白这不会起作用,因为与服务器方相比,客户端方案速度相当慢,而且我一直在获得TimeOutException。我能够通过设置我的Grizzly服务器来解决这个问题。通过这种方式,我可以使用StreamingOutput方法传输数百兆字节。我设置超时的代码是这样的:
Collection<NetworkListener> listeners = server.getListeners();
for(NetworkListener listener : listeners) {
final TCPNIOTransport transport = listener.getTransport();
transport.setKeepAlive(true);
transport.setWriteTimeout(0, TimeUnit.MINUTES);
}