下面的代码是Servlet 3.1非阻塞IO演示:
UploadServlet:
@WebServlet(name = "UploadServlet", urlPatterns = {"/UploadServlet"}, asyncSupported=true)
public class UploadServlet extends HttpServlet {
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
AsyncContext context = request.startAsync();
// set up async listener
context.addListener(new AsyncListener() {
public void onComplete(AsyncEvent event) throws IOException {
event.getSuppliedResponse().getOutputStream().print("Complete");
}
public void onError(AsyncEvent event) {
System.out.println(event.getThrowable());
}
public void onStartAsync(AsyncEvent event) {
}
public void onTimeout(AsyncEvent event) {
System.out.println("my asyncListener.onTimeout");
}
});
ServletInputStream input = request.getInputStream();
ReadListener readListener = new ReadListenerImpl(input, response, context);
input.setReadListener(readListener);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
RealListenerImpl:
public class ReadListenerImpl implements ReadListener{
private ServletInputStream input = null;
private HttpServletResponse res = null;
private AsyncContext ac = null;
private Queue queue = new LinkedBlockingQueue();
ReadListenerImpl(ServletInputStream in, HttpServletResponse r, AsyncContext c) {
input = in;
res = r;
ac = c;
}
public void onDataAvailable() throws IOException {
System.out.println("Data is available");
StringBuilder sb = new StringBuilder();
int len = -1;
byte b[] = new byte[1024];
while (input.isReady() && (len = input.read(b)) != -1) {
String data = new String(b, 0, len);
sb.append(data);
}
queue.add(sb.toString());
}
public void onAllDataRead() throws IOException {
System.out.println("Data is all read");
// now all data are read, set up a WriteListener to write
ServletOutputStream output = res.getOutputStream();
WriteListener writeListener = new WriteListenerImpl(output, queue, ac);
output.setWriteListener(writeListener);
}
public void onError(final Throwable t) {
ac.complete();
t.printStackTrace();
}
}
WriteListenerImpl:
public class WriteListenerImpl implements WriteListener{
private ServletOutputStream output = null;
private Queue queue = null;
private AsyncContext context = null;
WriteListenerImpl(ServletOutputStream sos, Queue q, AsyncContext c) {
output = sos;
queue = q;
context = c;
}
public void onWritePossible() throws IOException {
while (queue.peek() != null && output.isReady()) {
String data = (String) queue.poll();
output.print(data);
}
if (queue.peek() == null) {
context.complete();
}
}
public void onError(final Throwable t) {
context.complete();
t.printStackTrace();
}
}
上面的代码工作正常,我想知道与阻止IO servlet有什么区别?我想知道上面的代码如何工作。
最佳答案
读取输入数据:
在阻塞方案中,当您从输入流中读取数据时,每个读取都会阻塞,直到有可用数据为止。对于远程客户端发送大量数据而言,这可能要花费很长时间,这意味着线程将保留很长时间。
例如,考虑以固定间隔在2分钟内以13个块接收到入站数据。在阻塞读取中,您读取了第一个块,将线程保持约10秒,读取下一个块,将线程保持了约10秒,依此类推。在这种情况下,线程实际花费的数据可能少于一秒钟,而阻塞将近120秒等待数据。然后,如果您有一台具有10个线程的服务器,则可以看到每2分钟10个客户端的吞吐量。
在非阻塞情况下,readListener读取数据,而isReady()返回true(在每次调用读取数据之前必须检查isReady()),但是当isReady()返回false时,readListener返回,并且线程被放弃。然后,当更多数据到达时,将调用onDataAvailable(),并且readListener再次读取数据,直到isReady为false()为止。
在同一示例中,这次线程读取数据并返回,在10秒后被唤醒,读取下一个数据并返回,10秒后被唤醒,以读取数据并返回,等等。这一次,尽管它仍然占用2分钟读取数据所需的线程仅在不到一秒钟的时间内处于活动状态,并且可用于其他工作。因此,尽管特定请求仍然需要2分钟,但是具有10个线程的服务器现在可以每2分钟处理更多请求。
发送响应数据:
该方案与发送数据类似,在发送大型响应时很有用。例如,在阻塞情况下以13个块发送大型响应可能需要2分钟才能发送,因为客户端需要10秒来确认收到每个块,并且线程在等待时被保留。但是,在非阻塞情况下,线程仅在发送数据时保留,而在等待再次发送时不保留。因此,对于特定的客户端,再次不会更快地发送响应,而是将线程保留了一部分时间,并且处理请求的服务器的吞吐量可能会显着增加。
因此,这里的示例是人为设计的,但仅用于说明一点。关键在于,非阻塞I / O发出单个请求的速度不会比阻塞I / O更快,但是当应用程序读取输入数据的速度比客户端发送请求和/或发送响应数据的速度更快时,非阻塞I / O会提高服务器吞吐量。客户端可以收到的。