在Servlet 3.1中Non-blocking IO
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(); } }
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(); } }
Non-blocking IO Servlet 的一個瑕疵,所有的write 和 read 接口都是使用的byte[] 而不是bytebuffer,以至于無法實現zero copy。接口設計上亦比較底層,若想較方便的使用還需要等待Spring 等其他框架的支持了。
本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!