Woj*_*ela 5 java streaming multithreading servlets http
我正在尝试编写将处理POST请求并流式传输输入和输出的servlet。我的意思是它应该读取一行输入,在该行上做一些工作,然后写入一行输出。并且它应该能够处理任意长请求(这样也会产生任意长响应)而不会出现内存不足异常。这是我的第一次尝试:
protected void doPost(HttpServletRequest request, HttpServletResponse response) {
ServletInputStream input = request.getInputStream();
ServletOutputStream output = response.getOutputStream();
LineIterator lineIt = lineIterator(input, "UTF-8");
while (lineIt.hasNext()) {
String line = lineIt.next();
output.println(line.length());
}
output.flush();
}
Run Code Online (Sandbox Code Playgroud)
现在,我使用来测试了该servlet curl,它可以工作,但是当我使用Apache HttpClient编写客户端时,客户端线程和服务器线程都会挂起。客户看起来像这样:
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(...);
// request
post.setEntity(new FileEntity(new File("some-huge-file.txt")));
HttpResponse response = client.execute(post);
// response
copyInputStreamToFile(response.getEntity().getContent(), new File("results.txt"));
Run Code Online (Sandbox Code Playgroud)
问题很明显。客户端在一个线程中按顺序执行它的工作-首先它完全发送请求,然后才开始读取响应。但是服务器为每行输入写入一行输出,如果客户端未读取输出(而顺序客户端未读取),则服务器将被阻止尝试写入输出流。反过来,这会阻止客户端尝试将输入发送到服务器。
我猜想是curl有效的,因为它以某种方式同时发送输入和接收输出(在单独的线程中?)。因此,第一个问题是是否可以将Apache HttpClient配置为与以下行为类似curl?
下一个问题是,如何改进servlet,以便使行为不佳的客户端不会导致服务器线程挂起?我的第一个尝试是引入中间缓冲区,该缓冲区将收集输出,直到客户端完成发送输入为止,然后servlet才开始发送输出:
ServletInputStream input = request.getInputStream();
ServletOutputStream output = response.getOutputStream();
// prepare intermediate store
int threshold = 100 * 1024; // 100 kB before switching to file store
File file = File.createTempFile("intermediate", "");
DeferredFileOutputStream intermediate = new DeferredFileOutputStream(threshold, file);
// process request to intermediate store
PrintStream intermediateFront = new PrintStream(new BufferedOutputStream(intermediate));
LineIterator lineIt = lineIterator(input, "UTF-8");
while (lineIt.hasNext()) {
String line = lineIt.next();
intermediateFront.println(line.length());
}
intermediateFront.close();
// request fully processed, so now it's time to send response
intermediate.writeTo(output);
file.delete();
Run Code Online (Sandbox Code Playgroud)
这行得通,行为不正常的客户端可以安全地使用我的servlet,但是另一方面,对于像curl此类解决方案这样的并发客户端,这会增加不必要的延迟。并行客户端正在单独的线程中读取响应,因此当随着请求的使用而逐行生成响应时,它将受益。
所以我认为我需要一个字节缓冲区/队列:
DeferredFileOutputStream)。在servlet中,我将生成新线程以读取输入,对其进行处理并将输出写入缓冲区,而主servlet线程将从该缓冲区读取并将其发送给客户端。
您知道有喜欢这样做的图书馆吗?也许我的假设是错误的,我应该做一些完全不同的事情...
小智 2
要实现同时写入和读取,您可以使用 Jetty HttpClient http://www.eclipse.org/jetty/documentation/current/http-client-api.html
我已经使用此代码创建了对您的存储库的拉取请求。
HttpClient httpClient = new HttpClient();
httpClient.start();
Request request = httpClient.newRequest("http://localhost:8080/line-lengths");
final OutputStreamContentProvider contentProvider = new OutputStreamContentProvider();
InputStreamResponseListener responseListener = new InputStreamResponseListener();
request.content(contentProvider).method(HttpMethod.POST).send(responseListener); //async request
httpClient.getExecutor().execute(new Runnable() {
public void run() {
try (OutputStream outputStream = contentProvider.getOutputStream()) {
writeRequestBodyTo(outputStream); //writing to stream in another thread
} catch (IOException e) {
e.printStackTrace();
}
}
});
readResponseBodyFrom(responseListener.getInputStream()); //reading response
httpClient.stop();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1242 次 |
| 最近记录: |