在Servlet 3.1中Non-blocking IO

jopen 10年前發布 | 29K 次閱讀 Servlet Java開發

Servlet 3.0 中引入了Async Servlet,然而雖然servlet 的執行過程可以是異步的了,但是從request 讀取和向response 寫入的過程,依然是同步的IO。鑒于此,Async Servlet 對于長連接這樣的場景是很適合的,但是對于qps 很高的場景還是無能為例,read 和 write 的過程仍然會占用線程池中的執行時間按。

Servlet 3.1 在Async Servlet 的基礎上,引入了Non-blocking IO Servlet,IO 的過程是通過WriteListener 和 ReadListener來實現的,類似于event driven 的過程。

為InputStream 設置一個ReadListener,這樣有數據可以讀得時候會被回調:

@WebServlet(urlPatterns="/test", asyncSupported=true)
public class TestServlet extends HttpServlet {
    protected void doPost(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {

        // start async
        AsyncContext ac = req.startAsync();
        // set up async listener
        ac.addListener(new AsyncListener() {
            public void onComplete(AsyncEvent event) throws IOException {
                System.out.println("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");
            }
        });

        // set up ReadListener to read data for processing
        ServletInputStream input = req.getInputStream();
        ReadListener readListener = new ReadListenerImpl(input, res, ac);
        input.setReadListener(readListener);
    }
}

ReadListener 的實現,在數據讀完之后(onAllDataRead),為out 設置一個write listener,在可以寫入的時候會被回調:

class ReadListenerImpl implements ReadListener {
    private ServletInputStream input = null;
    private HttpServletResponse res = null;
    private AsyncContext ac = null;
    // store the processed data to be sent back to client later
    private Queue queue = new LinkedBlockingQueue();

    ReadListenerImpl(ServletInputStream in, HttpServletResponse r,
            AsyncContext c) {
        input = in;
        res = r;
        ac = c;
    }

    public void onDataAvailable() throws IOException {
        StringBuilder sb = new StringBuilder();
        int len = -1;
        byte b[] = new byte[1024];
        // We need to check input#isReady before reading data.
        // The ReadListener will be invoked again when 
        // the input#isReady is changed from false to true
        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 {
        // 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();
    }
} 

Write Listener的實現:
class WriteListenerImpl implements WriteListener {
    private ServletOutputStream output = null;
    private Queue queue = null;
    private AsyncContext ac = null;

    WriteListenerImpl(ServletOutputStream sos, Queue q, AsyncContext c) {
        output = sos;
        queue = q;
        ac = c;
    }

    public void onWritePossible() throws IOException {
        // write while there is data and is ready to write
        while (queue.peek() != null && output.isReady()) {
            String data = queue.poll();
            output.print(data);
        }
        // complete the async process when there is no more data to write
        if (queue.peek() == null) {
            ac.complete();
        }
    }

    public void onError(final Throwable t) {
        ac.complete();
        t.printStackTrace();
    }
} 

注意可讀或者可寫回調的時候,都是放在循環里進行的: while(output.isReady()) 和while(input.isReady()),一旦變得不可能或者不可寫了,就會退出循環,這次回調結束。將來變得可讀或可寫時,會再一次回調。

Non-blocking IO Servlet 的一個瑕疵,所有的write 和 read 接口都是使用的byte[] 而不是bytebuffer,以至于無法實現zero copy。接口設計上亦比較底層,若想較方便的使用還需要等待Spring 等其他框架的支持了。

原文鏈接: http://www.dongliu.net/post/5893431851220992

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!