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