关于Servlet的问题3.1非阻塞IO示例

时间:2015-12-05 12:22:37

标签: java nonblocking servlet-3.1

下面的代码是servlet 3.1 Non Blocking IO demo:

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有什么区别?我想知道上面的代码是如何工作的。

1 个答案:

答案 0 :(得分:0)

读取输入数据:

在阻塞方案中,当您从输入流中读取数据时,每个读取都会阻塞,直到数据可用。远程客户端发送大量数据可能需要很长时间,这意味着该线程可以长时间保存。

例如,考虑在13个块中以规则的间隔在2分钟内接收入站数据。在阻塞读取中,您读取第一个块,保持线程约10秒,读取下一个块,保持线程约10秒等。在这种情况下,线程可能花费不到一秒实际处理数据并且几乎120秒被阻止等待数据。然后,如果您有一个包含10个线程的服务器,您可以看到每2分钟就有10个客户端的吞吐量。

在非阻塞方案中,readListener读取数据,而isReady()返回true(在每次调用读取数据之前必须检查isReady()),但是当isReady()返回false时,readListener返回并放弃线程。然后当更多数据到达时,调用DataAvailable()并且readListener再次读取数据,直到isReady为false()。

在同一个例子中,这次线程读取数据并返回,10秒后被唤醒,读取下一个数据并返回,10秒后被唤醒读取数据并返回等。这次,它有仍然需要2分钟才能读取数据所需的线程仅在不到一秒的时间内处于活动状态且可用于其他工作。因此,虽然特定请求仍需要2分钟,但具有10个线程的服务器现在可以每2分钟处理更多请求。

发送回复数据:

此方案与发送数据类似,在发送大型响应时非常有用。例如,在13个块中发送大响应可能需要2分钟才能在阻塞方案中发送,因为客户端需要10秒钟来确认收到每个块并且线程在等待时保持。但是,在非阻塞方案中,线程仅在发送数据时保留,而不是在等待能够再次发送时保留。因此,对于特定客户端而言,响应不会更快地发送,但线程会保留一小部分时间,并且处理请求的服务器的吞吐量会显着增加。

所以这里的例子是人为的,但用来说明一点。关键是非阻塞i / o不会比阻塞i / o更快地发出单个请求,但是当应用程序可以比客户端发送和/或更快地发送响应数据更快地读取输入数据时增加服务器吞吐量客户可以收到它。