本文将从请求获取与包装处理、请求传递给 Container、Container 处理请求流程,这 3 部分来讲述一次 http 穿梭之旅。
1 请求包装处理
tomcat 组件 Connector 在启动的时候会监听端口。以 JIoEndpoint 为例,在其 Acceptor 类中:
protected class Acceptor extends AbstractEndpoint.Acceptor {
@Override
public void run() {
while (running) {
……
try {
//当前连接数
countUpOrAwaitConnection();
Socket socket = null;
try {
//取出队列中的连接请求
socket = serverSocketFactory.acceptSocket(serverSocket);
} catch (IOException ioe) {
countDownConnection();
}
if (running && !paused && setSocketOptions(socket)) {
//处理请求
if (!processSocket(socket)) {
countDownConnection();
closeSocket(socket);
}
} else {
countDownConnection();
// Close socket right away
closeSocket(socket);
}
}
……
}
}
}
复制代码
在上面的代码中,socket = serverSocketFactory.acceptSocket(serverSocket);与客户端建立连接,将连接的 socket 交给 processSocket(socket)来处理。在 processSocket 中,对 socket 进行包装一下交给线程池来处理:
protected boolean processSocket(Socket socket) {
try {
SocketWrapper<Socket> wrapper = new SocketWrapper<Socket>(socket);
wrapper.setKeepAliveLeft(getMaxKeepAliveRequests());
wrapper.setSecure(isSSLEnabled());
//交给线程池处理连接
getExecutor().execute(new SocketProcessor(wrapper));
}
……
return true;
}
复制代码
线程池处理的任务 SocketProccessor,通过代码分析:
protected class SocketProcessor implements Runnable {
protected SocketWrapper<Socket> socket = null;
protected SocketStatus status = null;
@Override
public void run() {
boolean launch = false;
synchronized (socket) {
SocketState state = SocketState.OPEN;
try {
serverSocketFactory.handshake(socket.getSocket());
}
……
if ((state != SocketState.CLOSED)) {
//委派给Handler来处理
if (status == null) {
state = handler.process(socket, SocketStatus.OPEN_READ);
} else {
state = handler.process(socket,status);
}
}}}
……
}
复制代码
即在 SocketProcessor 中,将 Socket 交给 handler 处理,这个 handler 就是在 Http11Protocol 的构造方法中赋值的 Http11ConnectionHandler,在该类的父类 process 方法中通过请求的状态,来创建 Http11Processor 处理器进行相应的处理,切到 Http11Proccessor 的父类 AbstractHttp11Proccessor 中。
public SocketState process(SocketWrapper socketWrapper) {
RequestInfo rp = request.getRequestProcessor();
rp.setStage(org.apache.coyote.Constants.STAGE_PARSE);
// Setting up the I/O
setSocketWrapper(socketWrapper);
getInputBuffer().init(socketWrapper, endpoint);
getOutputBuffer().init(socketWrapper, endpoint);
while (!getErrorState().isError() && keepAlive && !comet && !isAsync() &&
upgradeInbound == null &&
httpUpgradeHandler == null && !endpoint.isPaused()) {
……
if (!getErrorState().isError()) {
// Setting up filters, and parse some request headers
rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
try {
//请求预处理
prepareRequest();
}
……
}
……
if (!getErrorState().isError()) {
try {
rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
//交由适配器处理
adapter.service(request, response);
if(keepAlive && !getErrorState().isError() && (
response.getErrorException() != null ||
(!isAsync() &&
statusDropsConnection(response.getStatus())))) {
setErrorState(ErrorState.CLOSE_CLEAN, null);
}
setCometTimeouts(socketWrapper);
}
}
}
……
}
复制代码
可以看到 Request 和 Response 的生成,从 Socket 中获取请求数据,keep-alive 处理,数据包装等等信息,最后交给了 CoyoteAdapter 的 service 方法
2 请求传递给 Container
在 CoyoteAdapter 的 service 方法中,主要有 2 个任务:
•第一个是 org.apache.coyote.Request 和
org.apache.coyote.Response 到继承自 HttpServletRequest 的 org.apache.catalina.connector.Request 和 org.apache.catalina.connector.Response 转换,和 Context,Wrapper 定位。
•第二个是将请求交给 StandardEngineValve 处理。
public void service(org.apache.coyote.Request req,
org.apache.coyote.Response res) {
……
postParseSuccess = postParseRequest(req, request, res, response);
……
connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
……
}
复制代码
在 postParseRequest 方法中代码片段:
connector.getMapper().map(serverName, decodedURI, version,
request.getMappingData());
request.setContext((Context) request.getMappingData().context);
request.setWrapper((Wrapper) request.getMappingData().wrapper);
复制代码
request 通过 URI 的信息找到属于自己的 Context 和 Wrapper。而这个 Mapper 保存了所有的容器信息,不记得的同学可以回到 Connector 的 startInternal 方法中,最有一行代码是 mapperListener.start(); 在 MapperListener 的 start()方法中,
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
findDefaultHost();
Engine engine = (Engine) connector.getService().getContainer();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
registerHost(host);
}
}
}
复制代码
MapperListener.startInternal()方法将所有 Container 容器信息保存到了 mapper 中。那么,现在初始化把所有容器都添加进去了,如果容器变化了将会怎么样?这就是上面所说的监听器的作用,容器变化了,MapperListener 作为监听者。他的生成图示:
通过 Mapper 找到了该请求对应的 Context 和 Wrapper 后,CoyoteAdapter 将包装好的请求交给 Container 处理。
3 Container 处理请求流程
从下面的代码片段,我们很容易追踪整个 Container 的调用链: 用时序图画出来则是:
最终 StandardWrapperValve 将请求交给 Servlet 处理完成。至此一次 http 请求处理完毕。
作者:京东物流 毕会杰
内容来源:京东云开发者社区
评论