Tomcat处理http请求之源码分析

 更新时间:2023年05月29日 10:30:09   作者:京东云开发者  
本文将从请求获取与包装处理、请求传递给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请求处理完毕。

到此这篇关于Tomcat处理http请求之源码分析的文章就介绍到这了,更多相关Tomcat请求处理内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Tomcat启动核心流程示例详解

    Tomcat启动核心流程示例详解

    这篇文章主要为大家介绍了Tomcat启动核心流程示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • 怎么减少本地调试tomcat重启次数你知道吗

    怎么减少本地调试tomcat重启次数你知道吗

    这篇文章主要为大家详细介绍了怎么减少本地调试tomcat重启次数,使用Groovy,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-01-01
  • 详解Tomcat服务器绑定多域名和虚拟目录的方法

    详解Tomcat服务器绑定多域名和虚拟目录的方法

    这篇文章主要介绍了Tomcat服务器绑定多域名和虚拟目录的方法,Tomcat用于动态解析JSP和Servlet程序,需要的朋友可以参考下
    2015-12-12
  • 浅谈Tomcat7的热部署

    浅谈Tomcat7的热部署

    这篇文章主要介绍了浅谈Tomcat7的热部署,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-06-06
  • Win7系统下tomcat7.0配置教程

    Win7系统下tomcat7.0配置教程

    这篇文章主要为大家详细介绍了Windows7系统下tomcat7.0配置教程,感兴趣的小伙伴们可以参考一下
    2016-06-06
  • Tomcat核心组件及应用架构详解

    Tomcat核心组件及应用架构详解

    众所周知Tomcat 就是一个 Servlet 容器,为了方便使用,他们具有http服务器的功能,所以Tomcat 就是一个“HTTP 服务器 + Servlet 容器”,我们也叫它们 Web 容器,今天重点给大家普及Tomcat核心组件及应用架构相关知识,感兴趣的朋友一起了解下吧
    2021-05-05
  • 关于Tomcat启动失败报循环依赖的问题解决(AncestorAxisIterator)

    关于Tomcat启动失败报循环依赖的问题解决(AncestorAxisIterator)

    这篇文章主要介绍了关于Tomcat启动失败报循环依赖的问题解决(AncestorAxisIterator),Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,需要的朋友可以参考下
    2023-07-07
  • 解决Tomcat运行startup.bat闪退问题方法

    解决Tomcat运行startup.bat闪退问题方法

    这篇文章主要为大家介绍了解决Tomcat运行startup.bat闪退问题方法详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-12-12
  • Centos7安装配置tomcat9并设置自动启动的方法

    Centos7安装配置tomcat9并设置自动启动的方法

    这篇文章主要介绍了Centos7安装配置tomcat9并设置自动启动的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-09-09
  • 关于给Tomcat设置maxPostSize的问题及注意事项

    关于给Tomcat设置maxPostSize的问题及注意事项

    这篇文章主要介绍了关于给Tomcat设置maxPostSize的问题及注意事项,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-07-07

最新评论