下面的代码是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会提高服务器吞吐量。客户端可以收到的。

10-05 23:04
查看更多