关于tomcat:Tomcat处理http请求之源码分析-京东云技术团队

4次阅读

共计 4296 个字符,预计需要花费 11 分钟才能阅读完成。

本文将从申请获取与包装解决、申请传递给 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 申请处理完毕。

作者:京东物流 毕会杰

内容起源:京东云开发者社区

正文完
 0