Tomcat 9 源碼解析 -- Connector組件(2)

 

Tomcat 9 源碼解析 -- Connector組件

上文中已經分析到了processKey()方法。

Poller #run()   在run方法最後調用了   processKey(sk, attachment); // 處理SelectKey

 

/**
         	processKey()這個方法主要通過調用processSocket()方法創建一個SocketProcessor,然後丟到Tomcat線程池中去執行。每個Endpoint都有自己的SocketProcessor實現,
         	從Endpoint的屬性中可以看到,這個Processor也有緩存機制。
			總結一下Poller所做的事:遍歷PollerEvents隊列,將每個事件中的通道感興趣的事件註冊到Selector,當事件就緒時,創建一個SocketProcessor或者從緩存中取出一個SocketProcessor,
			然後放到線程池執行或者直接執行它的run方法執行。
         */
        protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
            try {
                if ( close ) {
                    cancelledKey(sk);
                } else if ( sk.isValid() && attachment != null ) {
                    if (sk.isReadable() || sk.isWritable() ) {
                        if ( attachment.getSendfileData() != null ) {
                            processSendfile(sk,attachment, false);
                        } else {
                            unreg(sk, attachment, sk.readyOps());
                            boolean closeSocket = false;
                            // Read goes before write
                            if (sk.isReadable()) {
                                if (!processSocket(attachment, SocketEvent.OPEN_READ, true)) {
                                    closeSocket = true;
                                }
                            }
                            if (!closeSocket && sk.isWritable()) {
                                if (!processSocket(attachment, SocketEvent.OPEN_WRITE, true)) {
                                    closeSocket = true;
                                }
                            }
                            if (closeSocket) {
                                cancelledKey(sk);
                            }
                        }
                    }
                } else {
                    //invalid key
                    cancelledKey(sk);
                }
            } catch ( CancelledKeyException ckx ) {
                cancelledKey(sk);
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error("",t);
            }
        }

processKey()這個方法主要通過調用processSocket()方法創建一個SocketProcessor,然後丟到Tomcat線程池中去執行。每個Endpoint都有自己的SocketProcessor實現,從Endpoint的屬性中可以看到,這個Processor也有緩存機制。
總結一下Poller所做的事:遍歷PollerEvents隊列,將每個事件中的通道感興趣的事件註冊到Selector,當事件就緒時,創建一個SocketProcessor或者從緩存中取出一個SocketProcessor,然後放到線程池執行或者直接執行它的run方法執行。
 

processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch)

public boolean processSocket(SocketWrapperBase<S> socketWrapper, SocketEvent event, boolean dispatch) {

		LogPropertiesTest
				.debug("  --------------  17、NioEndpoint :  執行processSocket(SocketWrapperBase<S> socketWrapper,\r\n"
						+ "            SocketEvent event, boolean dispatch) 方法, 執行類 :" + this.getClass());
		try {
			if (socketWrapper == null) {
				return false;
			}
			SocketProcessorBase<S> sc = processorCache.pop();
			if (sc == null) {
				sc = createSocketProcessor(socketWrapper, event);
			} else {
				sc.reset(socketWrapper, event);
			}
			Executor executor = getExecutor();
			if (dispatch && executor != null) {
				executor.execute(sc);
			} else {

				LogPropertiesTest
						.debug("  --------------  17、NioEndpoint :  執行   sc.run();  方法, 執行類 :" + this.getClass());

				sc.run();
			}
		} catch (RejectedExecutionException ree) {
			getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper), ree);
			return false;
		} catch (Throwable t) {
			ExceptionUtils.handleThrowable(t);
			// This means we got an OOM or similar creating a thread, or that
			// the pool and its queue are full
			getLog().error(sm.getString("endpoint.process.fail"), t);
			return false;
		}
		return true;
	}

 

SocketProcessor

SocketProcessor實現Runnable接口,對外暴露run()方法,內部封裝doRun()。

protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
    public SocketProcessor(SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
        super(socketWrapper, event);
    }

    @Override
    protected void doRun() {
        NioChannel socket = socketWrapper.getSocket();
        SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

        try {
            // 這裏的 handshake 是用來標記https的握手完成情況,
            // 如果是http不需要該握手階段,在從c1處直接置爲0
            int handshake = -1;
            try {
                if (key != null) {
                    if (socket.isHandshakeComplete()) { // c1
                        handshake = 0;
                    } else if (event == SocketEvent.STOP || event == SocketEvent.DISCONNECT ||
                               event == SocketEvent.ERROR) {
                        // 如果不能完成TLS握手過程,標記握手失敗
                        handshake = -1;
                    } else {
                        // 處理https的SecureNioChannel覆寫了該hanshake()方法
                        // 返回註冊的SelectionKey
                        handshake = socket.handshake(key.isReadable(), key.isWritable());
                        event = SocketEvent.OPEN_READ;
                    }
                }
            } catch (IOException x) {
                ...
            }
            if (handshake == 0) { // 握手完成
                SocketState state = SocketState.OPEN; // 標記Socket狀態
                // 處理該Sockt裏的請求
                if (event == null) {
                    // 2222222222
                    state = getHandler().process(socketWrapper, SocketEvent.OPEN_READ);
                } else {
                    state = getHandler().process(socketWrapper, event);
                }
                if (state == SocketState.CLOSED) {
                    close(socket, key); // 否則關閉通道
                }
            } else if (handshake == -1) { // 握手失敗則關閉通道
                close(socket, key);
            } else if (handshake == SelectionKey.OP_READ) { // TLS會走到這裏
                socketWrapper.registerReadInterest(); // 註冊讀就緒
            } else if (handshake == SelectionKey.OP_WRITE) {
                socketWrapper.registerWriteInterest(); // 註冊寫就緒
            }
        } catch (CancelledKeyException cx) {
            ... // 處理異常
        } finally {
            socketWrapper = null;
            event = null;
            if (running && !paused) {
                processorCache.push(this); // 將SocketProcessor放回緩存中
            }
        }
    }
}

在  22222222   處,該方法將包裝好的socketWrapper繼續傳下去,來看下調用棧,  最終在Http11Processor的service方法中處理  ,而這個Http11Processor正是Processor的一個實現類,用來真正解析流。

 

Http11Processor

先看一下Http11Processor的構造方法。

1 、首先調用抽象父類的構造方法,new了一個Request和Response,用於接收解析結果和Servlet處理完的內容,看這裏的Request和Response是org.apache.coyote包下的,與org.apache.catalina.connector包下的Request和Response不同。

對Request來說,前者是服務器請求的一種底層,但高效的表示,同時它也是GC-free,並且將消耗計算資源的操作作了延遲處理,可以說是按需再取。說它底層且高效是因爲它是直接操作數據流,轉換成需要的信息,所以它不適合用戶代碼。而後者connector 包下的 Request是 Coyote Request的包裝,爲用戶(也就是servlet)提供了一個高級視圖(外觀模式)。

 

public abstract class AbstractProcessor extends AbstractProcessorLight implements ActionHook {

    private static final StringManager sm = StringManager.getManager(AbstractProcessor.class);

    // Used to avoid useless B2C conversion on the host name.
    private char[] hostNameC = new char[0];

    protected final Adapter adapter;
    protected final AsyncStateMachine asyncStateMachine;
    private volatile long asyncTimeout = -1;
    protected final Request request;
    protected final Response response;
    protected volatile SocketWrapperBase<?> socketWrapper = null;
    protected volatile SSLSupport sslSupport;


    /**
     * Error state for the request/response currently being processed.
     */
    private ErrorState errorState = ErrorState.NONE;


    public AbstractProcessor(Adapter adapter) {
        this(adapter, new Request(), new Response());
    }

    protected AbstractProcessor(Adapter adapter, Request coyoteRequest, Response         
        coyoteResponse) {
        this.adapter = adapter;
        asyncStateMachine = new AsyncStateMachine(this);
        request = coyoteRequest;
        response = coyoteResponse;
        response.setHook(this);
        request.setResponse(response);
        request.setHook(this);
    }

..............
}

 

並且在  33333333333   處可以看到傳入了一個Adapter,這個 Adapter  則是基於 coyote 的 servlet 容器中的入口點。也就是說其作用是Connector與Container之間的橋樑。

上文中   Tomcat 9 源碼解析 -- Connector組件  也提到過  :

Connector數據結構

關於Connector,有兩個非常重要的接口, ProtocolHandler  和   Adapter,前者用於處理請求,而後者則是Connector與Container容器之間的一個連接器。

public Http11Processor(AbstractHttp11Protocol<?> protocol, Adapter adapter) {
    super(adapter);
    this.protocol = protocol; // protocol

    httpParser = new HttpParser(protocol.getRelaxedPathChars(),
            protocol.getRelaxedQueryChars());

    inputBuffer = new Http11InputBuffer(request, protocol.getMaxHttpHeaderSize(),
            protocol.getRejectIllegalHeaderName(), httpParser);
    request.setInputBuffer(inputBuffer); // 輸入緩衝,用於解析請求頭及傳輸編碼

    outputBuffer = new Http11OutputBuffer(response, protocol.getMaxHttpHeaderSize());
    response.setOutputBuffer(outputBuffer);  // 輸出緩衝,寫headers和response主題

    inputBuffer.addFilter(new IdentityInputFilter(protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new IdentityOutputFilter()); // 添加身份認證過濾器
    inputBuffer.addFilter(new ChunkedInputFilter(protocol.getMaxTrailerSize(),
            protocol.getAllowedTrailerHeadersInternal(), protocol.getMaxExtensionSize(),
            protocol.getMaxSwallowSize()));
    outputBuffer.addFilter(new ChunkedOutputFilter()); // 添加分塊過濾器
    
    // Void輸入輸出過濾器,在嘗試讀取時返回-1。 與GET,HEAD或類似請求一起使用。
    inputBuffer.addFilter(new VoidInputFilter());
    outputBuffer.addFilter(new VoidOutputFilter()); 

    // 輸入過濾器負責讀取和緩衝請求體,以便它不會干擾客戶端SSL握手消息。
    inputBuffer.addFilter(new BufferedInputFilter());

    //inputBuffer.addFilter(new GzipInputFilter());
    outputBuffer.addFilter(new GzipOutputFilter()); // Gzip

    pluggableFilterIndex = inputBuffer.getFilters().length; // 標記過濾器數量
}

看完基本的構造方法,大致能瞭解這個Http11Processor的功能便是解析流,然後封裝成Request,最後給Servlet做處理。

下面來看下調用棧中提到的service方法。

可以看到處理socket分爲幾個階段:

解析——>準備——>服務——>結束輸入、輸出——>KEEPALIVE——>結束。

當然如果對於keepalive的請求來說,會在前5個階段循環。前四個階段表示了一個完成的請求處理及響應。在c4處可以看到獲取了Adapter,而這個Adapter的實現類爲CoyoteAdapter,這個Adapter將Request和Reponse的處理委託給servlet。

最終在Http11Processor的service方法中處理

@Override
    public SocketState service(SocketWrapperBase<?> socketWrapper)
        throws IOException {
    	// 包含有關請求處理的統計、管理信息。
        RequestInfo rp = request.getRequestProcessor();
        rp.setStage(org.apache.coyote.Constants.STAGE_PARSE); // 解析階段

        // Setting up the I/O
        setSocketWrapper(socketWrapper);// 初始化I/O設置,
        inputBuffer.init(socketWrapper);
        outputBuffer.init(socketWrapper);

        // Flags
        keepAlive = true;// 標誌位
        openSocket = false;
        readComplete = true;
        boolean keptAlive = false;
        SendfileState sendfileState = SendfileState.DONE;
        
        while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
                sendfileState == SendfileState.DONE && !protocol.isPaused()) {

            // Parsing the request header
        	//解析請求頭信息  從buffer中讀取並解析請求基本信息,比如方法(GET/POST/PUT等等)、URI、協議等等
            try {
                if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
                        protocol.getKeepAliveTimeout())) {
                    if (inputBuffer.getParsingRequestLinePhase() == -1) {
                        return SocketState.UPGRADING;
                    } else if (handleIncompleteRequestLineRead()) {
                        break;
                    }
                }

                if (protocol.isPaused()) {
                    // 503 - Service unavailable
                    response.setStatus(503);  // 如果協議解析服務被暫停,則返回503
                    setErrorState(ErrorState.CLOSE_CLEAN, null);
                } else {
                    keptAlive = true;
                    // Set this every time in case limit has been changed via JMX
                    // 每次更新header屬性的數量限制(可以通過JMX修改)
                    request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
                    if (!inputBuffer.parseHeaders()) {
                        // We've read part of the request, don't recycle it
                        // instead associate it with the socket
                        openSocket = true;
                        readComplete = false;
                        break;
                    }
                    if (!protocol.getDisableUploadTimeout()) {
                        socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
                    }
                }
            } catch (IOException e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("http11processor.header.parse"), e);
                }
                setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                break;
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                UserDataHelper.Mode logMode = userDataHelper.getNextMode();
                if (logMode != null) {
                    String message = sm.getString("http11processor.header.parse");
                    switch (logMode) {
                        case INFO_THEN_DEBUG:
                            message += sm.getString("http11processor.fallToDebug");
                            //$FALL-THROUGH$
                        case INFO:
                            log.info(message, t);
                            break;
                        case DEBUG:
                            log.debug(message, t);
                    }
                }
                // 400 - Bad Request
                response.setStatus(400);  // 返回400 異常
                setErrorState(ErrorState.CLOSE_CLEAN, t);
            }

            // Has an upgrade been requested?
            Enumeration<String> connectionValues = request.getMimeHeaders().values("Connection");
            boolean foundUpgrade = false;
            while (connectionValues.hasMoreElements() && !foundUpgrade) {
                foundUpgrade = connectionValues.nextElement().toLowerCase(
                        Locale.ENGLISH).contains("upgrade");
            }

            if (foundUpgrade) {
                // Check the protocol 檢查協議
            	// 如果包含upgrade,表示協議需要升級
                String requestedProtocol = request.getHeader("Upgrade");  // 獲取升級協議

                UpgradeProtocol upgradeProtocol = protocol.getUpgradeProtocol(requestedProtocol);
                if (upgradeProtocol != null) {
                    if (upgradeProtocol.accept(request)) {
                        response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
                        response.setHeader("Connection", "Upgrade");  // 設置響應體
                        response.setHeader("Upgrade", requestedProtocol);
                        action(ActionCode.CLOSE,  null);
                        getAdapter().log(request, response, 0);

                        InternalHttpUpgradeHandler upgradeHandler =
                                upgradeProtocol.getInternalUpgradeHandler(
                                        socketWrapper, getAdapter(), cloneRequest(request));
                        UpgradeToken upgradeToken = new UpgradeToken(upgradeHandler, null, null);
                        action(ActionCode.UPGRADE, upgradeToken);
                        return SocketState.UPGRADING;
                    }
                }
            }

            if (getErrorState().isIoAllowed()) {
                // Setting up filters, and parse some request headers
                rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
                try {
                	// 主要設置過濾器,並且解析部分headers
                    // 比如檢查是否是keepAlive、如果是http/1.1,判斷是否包含Expect:100-continue
                    // (用於客戶端在發送POST數據給服務器前,徵詢服務器情況,
                    // 看服務器是否處理POST的數據,常用於大文件post,例如文件上傳)、
                    // 用戶代理user-agent狀況、host等等
                    prepareRequest();
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("http11processor.request.prepare"), t);
                    }
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                }
            }

            // 如果是長連接,則需要判斷長連接是否達到服務器上限
            int maxKeepAliveRequests = protocol.getMaxKeepAliveRequests();
            if (maxKeepAliveRequests == 1) {
                keepAlive = false;
            } else if (maxKeepAliveRequests > 0 &&
                    socketWrapper.decrementKeepAlive() <= 0) {
                keepAlive = false;
            }

            // Process the request in the adapter
            // 通過Adapter交給Container處理請求,並構建Response
            if (getErrorState().isIoAllowed()) {
                try {
                    rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
                    
                    getAdapter().service(request, response);
                    
                    // Handle when the response was committed before a serious
                    // error occurred.  Throwing a ServletException should both
                    // set the status to 500 and set the errorException.
                    // If we fail here, then the response is likely already
                    // committed, so we can't try and set headers.
                    if(keepAlive && !getErrorState().isError() && !isAsync() &&
                            statusDropsConnection(response.getStatus())) {
                        setErrorState(ErrorState.CLOSE_CLEAN, null);
                    }
                } catch (InterruptedIOException e) {
                    setErrorState(ErrorState.CLOSE_CONNECTION_NOW, e);
                } catch (HeadersTooLargeException e) {
                    log.error(sm.getString("http11processor.request.process"), e);
                    // The response should not have been committed but check it
                    // anyway to be safe
                    if (response.isCommitted()) {
                        setErrorState(ErrorState.CLOSE_NOW, e);
                    } else {
                        response.reset();
                        response.setStatus(500);
                        setErrorState(ErrorState.CLOSE_CLEAN, e);
                        response.setHeader("Connection", "close"); // TODO: Remove
                    }
                } catch (Throwable t) {
                    ExceptionUtils.handleThrowable(t);
                    log.error(sm.getString("http11processor.request.process"), t);
                    // 500 - Internal Server Error
                    response.setStatus(500);
                    setErrorState(ErrorState.CLOSE_CLEAN, t);
                    getAdapter().log(request, response, 0);
                }
            }

            // Finish the handling of the request
            // 完成 request請求處理
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDINPUT);
            if (!isAsync()) {
                // If this is an async request then the request ends when it has
                // been completed. The AsyncContext is responsible for calling
                // endRequest() in that case.
            	// 如果這是異步請求,則請求在完成後結束。
                // 在這種情況下,AsyncContext負責調用endRequest()。
                endRequest();
            }
            rp.setStage(org.apache.coyote.Constants.STAGE_ENDOUTPUT);

            // If there was an error, make sure the request is counted as
            // and error, and update the statistics counter
            // 確保設置錯誤碼,且更新請求的統計計數
            if (getErrorState().isError()) {
                response.setStatus(500);
            }

            if (!isAsync() || getErrorState().isError()) {
                request.updateCounters();
                if (getErrorState().isIoAllowed()) {
                    inputBuffer.nextRequest();
                    outputBuffer.nextRequest();
                }
            }

            if (!protocol.getDisableUploadTimeout()) {
                int connectionTimeout = protocol.getConnectionTimeout();
                if(connectionTimeout > 0) {
                    socketWrapper.setReadTimeout(connectionTimeout);
                } else {
                    socketWrapper.setReadTimeout(0);
                }
            }

            // 如果是長連接繼續循環
            rp.setStage(org.apache.coyote.Constants.STAGE_KEEPALIVE);

            sendfileState = processSendfile(socketWrapper);
        }

        rp.setStage(org.apache.coyote.Constants.STAGE_ENDED);

        if (getErrorState().isError() || protocol.isPaused()) {
            return SocketState.CLOSED;
        } else if (isAsync()) {
            return SocketState.LONG;
        } else if (isUpgrade()) {
            return SocketState.UPGRADING;
        } else {
            if (sendfileState == SendfileState.PENDING) {
                return SocketState.SENDFILE;
            } else {
                if (openSocket) {
                    if (readComplete) {
                        return SocketState.OPEN;
                    } else {
                        return SocketState.LONG;
                    }
                } else {
                    return SocketState.CLOSED;
                }
            }
        }
    }

Connector的對請求數據流的處理流程基本已經完成。

接下去便是交由Container組件去做具體的Servlet處理了。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章