Tomcat源码分析----一个http请求的经历

来源:互联网 发布:tomcat启动端口号 编辑:程序博客网 时间:2024/06/05 00:17

Tomcat源码分析----一个http请求的经历

楚岩 2016-04-06 09:53:46 浏览2675 评论1

http 学生 tomcat源码 tomcat处理http

摘要: 本章节对http请求到tomcat服务端,从监听到处理过程展现给大家。

1 请求获取与包装处理

本章节对http请求到服务端,从监听到处理展现给大家。
在上文中有分析Connector在启动的时候会监听端口。继续以JIoEndpoint为例,在其Accptor类中:

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作为监听者。他的生成图示:
screenshot

通过Mapper找到了该请求对应的Context和Wrapper后,CoyoteAdapter将包装好的请求交给Container处理。

3 Container处理请求流程

从下面的代码片段,我们很容易追踪整个Container的调用链:
screenshot
用时序图画出来则是:
screenshot
最终StandardWrapperValve将请求交给Servlet处理完成
pipeline+valve处理:

系列文章直达:

初始化与启动:https://yq.aliyun.com/articles/20169?spm=0.0.0.0.4yGfpo
容器:https://yq.aliyun.com/articles/20172?spm=0.0.0.0.2uPEZi
连接器:https://yq.aliyun.com/articles/20175?spm=0.0.0.0.2uPEZi
一个http请求的经历:https://yq.aliyun.com/articles/20177?spm=0.0.0.0.2uPEZi
重要的设计模式:https://yq.aliyun.com/articles/20179?spm=0.0.0.0.2uPEZi

用云栖社区APP,舒服~

【云栖快讯】有奖热议中:盘点我们的2016!2016年在一行行代码中闪过。这一年你都有哪些技术收获?是博客阅读量又创新高,还是辛苦开发的应用上线。又有什么样的大事件让你记忆犹新呢?一起来聊聊各自的猿路历程吧。  详情请点击
0 0
原创粉丝点击