天天看點

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作為監聽者。他的生成圖示:

Tomcat處理http請求之源碼分析

通過Mapper找到了該請求對應的Context和Wrapper後,CoyoteAdapter将包裝好的請求交給Container處理。

3 Container處理請求流程

從下面的代碼片段,我們很容易追蹤整個Container的調用鍊: 用時序圖畫出來則是:

Tomcat處理http請求之源碼分析

最終StandardWrapperValve将請求交給Servlet處理完成。至此一次http請求處理完畢。

作者:京東物流 畢會傑

内容來源:京東雲開發者社群

繼續閱讀