写点什么

Tomcat 处理 http 请求之源码分析 | 京东云技术团队

  • 2023-05-29
    北京
  • 本文字数:3282 字

    阅读完需:约 11 分钟

Tomcat处理http请求之源码分析 | 京东云技术团队

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


作者:京东物流 毕会杰

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

发布于: 刚刚阅读数: 3
用户头像

拥抱技术,与开发者携手创造未来! 2018-11-20 加入

我们将持续为人工智能、大数据、云计算、物联网等相关领域的开发者,提供技术干货、行业技术内容、技术落地实践等文章内容。京东云开发者社区官方网站【https://developer.jdcloud.com/】,欢迎大家来玩

评论

发布
暂无评论
Tomcat处理http请求之源码分析 | 京东云技术团队_tomcat_京东科技开发者_InfoQ写作社区