所以 , 这里最终会调用AbstractEndpoint的processSocket方法 , 之前看过我前面博客的同学应该有印象 , EndPoint是用来接受和处理请求的 , 接下来就会交给Processor去进行协议处理 。
类:AbstractProcessorLightpublic SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status) throws IOException { //省略部分diam SocketState state = SocketState.CLOSED; Iterator<DispatchType> dispatches = null; do { if (dispatches != null) { DispatchType nextDispatch = dispatches.next(); state = dispatch(nextDispatch.getSocketStatus()); } else if (status == SocketEvent.DISCONNECT) {} else if (isAsync() || isUpgrade() || state == SocketState.ASYNC_END) { state = dispatch(status); if (state == SocketState.OPEN) { state = service(socketWrapper); } } else if (status == SocketEvent.OPEN_WRITE) { state = SocketState.LONG; } else if (status == SocketEvent.OPEN_READ){ state = service(socketWrapper); } else { state = SocketState.CLOSED; } } while (state == SocketState.ASYNC_END || dispatches != null && state != SocketState.CLOSED); return state; }这部分是重点 , AbstractProcessorLight会根据SocketEvent的状态来判断是不是要去调用service(socketWrapper),该方法最终会去调用到容器 , 从而完成业务逻辑的调用 , 我们这个请求是执行完成后调用的 , 肯定不能进容器了 , 不然就是死循环了 , 这里通过isAsync() 判断 , 就会进入dispatch(status),最终会调用CoyoteAdapter的asyncDispatch方法
public boolean asyncDispatch(org.Apache.coyote.Request req, org.apache.coyote.Response res, SocketEvent status) throws Exception { //省略部分代码 Request request = (Request) req.getNote(ADAPTER_NOTES); Response response = (Response) res.getNote(ADAPTER_NOTES); boolean success = true; AsyncContextImpl asyncConImpl = request.getAsyncContextInternal(); try { if (!request.isAsync()) { response.setSuspended(false); } if (status==SocketEvent.TIMEOUT) { if (!asyncConImpl.timeout()) { asyncConImpl.setErrorState(null, false); } } else if (status==SocketEvent.ERROR) {} if (!request.isAsyncDispatching() && request.isAsync()) { WriteListener writeListener = res.getWriteListener(); ReadListener readListener = req.getReadListener(); if (writeListener != null && status == SocketEvent.OPEN_WRITE) { ClassLoader oldCL = null; try { oldCL = request.getContext().bind(false, null); res.onWritePossible();//这里执行浏览器响应 , 写入数据 if (request.isFinished() && req.sendAllDataReadEvent() && readListener != null) { readListener.onAllDataRead(); } } catch (Throwable t) {} finally { request.getContext().unbind(false, oldCL); } }} } //这里判断异步正在进行 , 说明这不是一个完成方法的回调 , 是一个正常异步请求 , 继续调用容器 。if (request.isAsyncDispatching()) { connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); Throwable t = (Throwable) request.getAttribute(RequestDispatcher.ERROR_EXCEPTION); if (t != null) { asyncConImpl.setErrorState(t, true); } } //注意 , 这里 , 如果超时或者出错 , request.isAsync()会返回false , 这里是为了尽快的输出错误给客户端 。if (!request.isAsync()) { //这里也是输出逻辑 request.finishRequest(); response.finishResponse(); } //销毁request和response if (!success || !request.isAsync()) { updateWrapperErrorCount(request, response); request.recycle(); response.recycle(); } } return success; }上面的代码就是ctx.complete()执行最终的方法了(当然省略了很多细节) , 完成了数据的输出 , 最终输出到浏览器 。
这里有同学可能会说 , 我知道异步执行完后 , 调用ctx.complete()会输出到浏览器 , 但是 , 第一次doGet请求执行完成后 , Tomcat是怎么知道不用返回到客户端的呢?关键代码在CoyoteAdapter中的service方法 , 部分代码如下:
postParseSuccess = postParseRequest(req, request, res, response); //省略部分代码 if (postParseSuccess) { request.setAsyncSupported( connector.getService().getContainer().getPipeline().isAsyncSupported()); connector.getService().getContainer().getPipeline().getFirst().invoke( request, response); } if (request.isAsync()) { async = true; } else { //输出数据到客户端 request.finishRequest(); response.finishResponse(); if (!async) { updateWrapperErrorCount(request, response); //销毁request和response request.recycle(); response.recycle(); }这部分代码在调用完Servlet后 , 会通过request.isAsync()来判断是否是异步请求 , 如果是异步请求 , 就设置 async = true 。如果是非异步请求就执行输出数据到客户端逻辑 , 同时销毁request和response 。这里就完成了请求结束后不响应客户端的操作 。
推荐阅读
- 湖南安化黑茶文化节主题歌你来的正是时候面世
- MySQL报错找不到问题?可能是你的SQL用了关键字
- 金矿石为什么是黑色的 矿石金掉色吗
- PHP Web开源的文件管理器
- thinkphp如何防止sql注入xss攻击
- IndexedDB 是什么
- 优秀架构师修成之路
- 阿里资深架构师:同样是数据中台,为什么差距那么大?
- Uber Go语言编码规范
- 自旋锁、排队自旋锁、MCS锁、CLH锁
