Tomcat源碼解析:請求處理過程


我們都知道,一個http請求過程一般爲瀏覽器發送請求->建立socket連接->通過socket讀取數據->解析http數據->調用後臺服務完成響應,而對於Tomcat來說,着重點在建立socket連接->通過socket讀取數據->解析http數據->調用filter->調用servlet處理並返回響應,本文將詳細介紹Tomcat請求過程。


前言

當Tomcat啓動時,會啓動一組線程用於不同階段的請求處理過程。

在這裏插入圖片描述

從圖中可以看出,根據不同協議分別啓動了Acceptor和Poller線程,先介紹一下這兩個線程的作用,這裏只對http協議的請求過程進行分析。

  • Acceptor:用於接收新連接,並將新連接添加到Poller事件隊列中。
  • Poller:用於監聽Socket事件,當Socket可讀或者可寫時,調用線程池處理Socket請求。

建立Socket連接

Acceptor#run

public void run() {

    int errorDelay = 0;

    // Loop until we receive a shutdown command
    while (endpoint.isRunning()) {

        try {
           //...

            U socket = null;
            // 監聽socket負責接收新連接
            socket = endpoint.serverSocketAccept();
        
            // Successful accept, reset the error delay
            errorDelay = 0;

            // Configure the socket
            if (endpoint.isRunning() && !endpoint.isPaused()) {
                // setSocketOptions() will hand the socket off to
                // an appropriate processor if successful
                //處理接受到的socket對象併發布事件
                if (!endpoint.setSocketOptions(socket)) {
                    endpoint.closeSocket(socket);
                }
            } else {
                endpoint.destroySocket(socket);
            }
        } catch (Throwable t) {
            //...
        }
    }
    state = AcceptorState.ENDED;
}
  1. Acceptor 在啓動後會阻塞在 endpoint.serverSocketAccept(); 方法處,當有新連接到達時,該方法返回一個 SocketChannel。這裏的endpointNioEndpoint
protected SocketChannel serverSocketAccept() throws Exception {
    return serverSock.accept();
}
  1. endpoint.setSocketOptions(socket)會將socket封裝到NioChannel中,並註冊到Poller。
protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    // Disable blocking, polling will be used
    socket.configureBlocking(false);
    Socket sock = socket.socket();
    socketProperties.setProperties(sock);

    NioChannel channel = null;
    if (nioChannels != null) {
        channel = nioChannels.pop();
    }
    if (channel == null) {
        SocketBufferHandler bufhandler = new SocketBufferHandler(
            socketProperties.getAppReadBufSize(),
            socketProperties.getAppWriteBufSize(),
            socketProperties.getDirectBuffer());
        if (isSSLEnabled()) {
            channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
        } else {
            channel = new NioChannel(socket, bufhandler);
        }
    } else {
        channel.setIOChannel(socket);
        channel.reset();
    }
    //獲取Poller並將socket註冊到Poller當中
    getPoller0().register(channel);
    //...
    return true;
}

發佈PollerEvent事件

NioEndpoint 維護了一個 Poller 數組,使用時,從中取出一個。如果當前Poller線程只有一個時,默認返回pollers[0],否則當一個連接分配給 pollers[index] 時,下一個連接就會分配給 pollers[(index+1)%pollers.length]

public Poller getPoller0() {
    if (pollerThreadCount == 1) {
        return pollers[0];
    } else {
        int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
        return pollers[idx];
    }
}

然後將 Socket 添加到該PollerPollerEvent隊列中。

public void register(final NioChannel socket) {
    socket.setPoller(this);
    NioSocketWrapper socketWrapper = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(socketWrapper);
    socketWrapper.setPoller(this);
    socketWrapper.setReadTimeout(getConnectionTimeout());
    socketWrapper.setWriteTimeout(getConnectionTimeout());
    socketWrapper.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    socketWrapper.setSecure(isSSLEnabled());
    socketWrapper.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    PollerEvent r = null;
    if (eventCache != null) {
        r = eventCache.pop();
    }
    if (r == null) {
        r = new PollerEvent(socket, socketWrapper, OP_REGISTER);
    } else {
        r.reset(socket, socketWrapper, OP_REGISTER);
    }
    // 添加到PollerEvent隊列當中
    addEvent(r);
}

private void addEvent(PollerEvent event) {
    // 添加到PollerEvent隊列當中
    events.offer(event);
    if (wakeupCounter.incrementAndGet() == 0) {
        selector.wakeup();
    }
}

處理PollerEvent事件

Poller線程會消費PollerEvent隊列

Poller#run

public void run() {
	//..
    if (!close) {
        //這裏會處理PollerEvent事件
        hasEvents = events();
    }
    //...
    Iterator<SelectionKey> iterator =
        keyCount > 0 ? selector.selectedKeys().iterator() : null;
    while (iterator != null && iterator.hasNext()) {
        SelectionKey sk = iterator.next();
        NioSocketWrapper socketWrapper = (NioSocketWrapper) sk.attachment();
        if (socketWrapper == null) {
            iterator.remove();
        } else {
            iterator.remove();
            //處理socket請求
            processKey(sk, socketWrapper);
        }
    }
}
  1. events()處理PollerEvent事件並將socket註冊到selector當中,省略了部分代碼,Poller和PollerEvent都是NioEndpoint的內部類。
public class Poller implements Runnable {
    public boolean events() {
        boolean result = false;
        PollerEvent pe = null;
        for (int i = 0, size = events.size(); i < size && (pe = events.poll()) != null; i++ ) {
            result = true;
            //調用PollerEvent的run
            pe.run();
            pe.reset();
            if (running && !paused && eventCache != null) {
                eventCache.push(pe);
            }

        }

        return result;
    }
}    
    
public static class PollerEvent implements Runnable {
     public void run() {
            if (interestOps == OP_REGISTER) {
                //將Socket註冊到channel中
                socket.getIOChannel().register(socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper);
               
            } 
        }
}
  1. processKey(sk, socketWrapper)處理socket數據。

Poller#processKey

protected void processKey(SelectionKey sk, NioSocketWrapper attachment) {
    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;
        }
    }
}

當Socket可讀或者可寫時,處理Socket數據。

讀取Socket數據

AbstractEndpoint#processSocket

public boolean processSocket(SocketWrapperBase<S> socketWrapper,
                             SocketEvent event, boolean dispatch) {
    if (socketWrapper == null) {
        return false;
    }
    SocketProcessorBase<S> sc = null;
    if (processorCache != null) {
        sc = processorCache.pop();
    }
    // 1.創建socket數據處理器
    if (sc == null) {
        
        sc = createSocketProcessor(socketWrapper, event);
    } else {
        sc.reset(socketWrapper, event);
    }
    //2.啓動線程處理socket數據,
    Executor executor = getExecutor();
    if (dispatch && executor != null) {
        executor.execute(sc);
    } else {
        sc.run();
    }
    
    return true;
}
  1. 創建socket數據處理器

**NioEndpoint#createSocketProcessor **

protected SocketProcessorBase<NioChannel> createSocketProcessor(
    SocketWrapperBase<NioChannel> socketWrapper, SocketEvent event) {
    return new SocketProcessor(socketWrapper, event);
}
  1. 啓動線程處理socket數據。如果配置了線程池會啓動線程池來執行。
protected class SocketProcessor extends SocketProcessorBase<NioChannel> {
    protected void doRun() {
        NioChannel socket = socketWrapper.getSocket();
        SelectionKey key = socket.getIOChannel().keyFor(socket.getPoller().getSelector());

        int handshake = -1;

        if (handshake == 0) {
            SocketState state = SocketState.OPEN;
            // Process the request from this socket
            // 處理socket數據
            if (event == null) {
                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){
            socketWrapper.registerReadInterest();
        } else if (handshake == SelectionKey.OP_WRITE){
            socketWrapper.registerWriteInterest();
        }
            
    }
}

這裏的handler爲ConnectionHandler,來源於Http11NioProtocol構造方法。

public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {

    public Http11NioProtocol() {
        super(new NioEndpoint());
    }
}

public abstract class AbstractHttp11Protocol<S> extends AbstractProtocol<S> {
    public AbstractHttp11Protocol(AbstractEndpoint<S,?> endpoint) {
        super(endpoint);
        setConnectionTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        ConnectionHandler<S> cHandler = new ConnectionHandler<>(this);
        setHandler(cHandler);
        getEndpoint().setHandler(cHandler);
    } 
}

那麼接下來聚焦於它的process方法。

ConnectionHandler#process

public SocketState process(SocketWrapperBase<S> wrapper, SocketEvent status) {
    //...
    S socket = wrapper.getSocket();
    Processor processor = connections.get(socket);
    //....
    if (processor == null) {
        //創建processor
        processor = getProtocol().createProcessor();
        register(processor);
    }
    SocketState state = SocketState.CLOSED;
    do {
        //處理socket
        state = processor.process(wrapper, status);
        //...省略了處理完成之後清理socket的操作
    } while ( state == SocketState.UPGRADING);
    // Make sure socket/processor is removed from the list of current
    // connections
    connections.remove(socket);
    release(processor);
    return SocketState.CLOSED;
}

默認一個新連接的情況下,會調用processor.process(wrapper, status)代碼,而processor的實例一般是在getProtocol().createProcessor()創建的,而protocol則:

public ConnectionHandler(AbstractProtocol<S> proto) {
    this.proto = proto;
}

protected AbstractProtocol<S> getProtocol() {
    return proto;
}

AbstractProtocol有兩個實現類,AbstractHttp11ProtocolAbstractAjpProtocol,這裏是Http請求,所以獲取到的protocol爲AbstractHttp11Protocol。而AbstractHttp11Protocol創建了Http11Processor實例。

protected Processor createProcessor() {
    Http11Processor processor = new Http11Processor(this, adapter);
    return processor;
}

由於Http11Processor沒有實現process方法,所以實際調用了其父類的父類AbstractProcessorLight的process方法。

public abstract class AbstractProcessorLight implements Processor {

    @Override
    public SocketState process(SocketWrapperBase<?> socketWrapper, SocketEvent status)
            throws IOException {
		
        SocketState state = SocketState.CLOSED;
        Iterator<DispatchType> dispatches = null;
        do {
            //...
            if (status == SocketEvent.OPEN_READ){
                 //
                state = service(socketWrapper);
            } else {
                state = SocketState.CLOSED;
            }
        } while (state == SocketState.ASYNC_END ||
                dispatches != null && state != SocketState.CLOSED);

        return state;
    }
}

根據不同的SocketEvent做不同操作,這裏的事件是OPEN_READ,所以調用service方法解析http數據。

解析Http數據

Http11Processor#service

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);
    inputBuffer.init(socketWrapper);
    outputBuffer.init(socketWrapper);

    // Flags
    while (!getErrorState().isError() && keepAlive && !isAsync() && upgradeToken == null &&
           sendfileState == SendfileState.DONE && !protocol.isPaused()) {

        // Parsing the request header
        // 解析requestLine
        if (!inputBuffer.parseRequestLine(keptAlive, protocol.getConnectionTimeout(),
                                          protocol.getKeepAliveTimeout())) {
            if (inputBuffer.getParsingRequestLinePhase() == -1) {
                return SocketState.UPGRADING;
            } else if (handleIncompleteRequestLineRead()) {
                break;
            }
        }

        if (protocol.isPaused()) {
            response.setStatus(503);
            setErrorState(ErrorState.CLOSE_CLEAN, null);
        } else {
            keptAlive = true;
            request.getMimeHeaders().setLimit(protocol.getMaxHeaderCount());
            //解析header
            if (!inputBuffer.parseHeaders()) {
                openSocket = true;
                readComplete = false;
                break;
            }
            if (!protocol.getDisableUploadTimeout()) {
                socketWrapper.setReadTimeout(protocol.getConnectionUploadTimeout());
            }
        }
        //...
        //指定request body的讀取filter
        prepareRequest();
        //...
        // Process the request in the adapter
        if (getErrorState().isIoAllowed()) {
            rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
            //處理request
            getAdapter().service(request, response);

            if(keepAlive && !getErrorState().isError() && !isAsync() &&
               statusDropsConnection(response.getStatus())) {
                setErrorState(ErrorState.CLOSE_CLEAN, null);
            }
        }

        // Finish the handling of the request
        //...
    }
}
  1. 解析請求header。
  2. 指定request body的讀取filter。
  3. 匹配servlet。

這裏主要針對匹配servlet作詳細解釋。

CoyoteAdapter#service

public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
    throws Exception {
    //...
    // 解析request並匹配servlet
    postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
        request.setAsyncSupported(
            connector.getService().getContainer().getPipeline().isAsyncSupported());
        // 調用匹配成功的servlet
        connector.getService().getContainer().getPipeline().getFirst().invoke(
            request, response);
    }
    //...
}
  1. 解析request並匹配servlet
  2. 調用匹配成功的servlet

匹配servlet

protected boolean postParseRequest(org.apache.coyote.Request req, Request request,
                                   org.apache.coyote.Response res, Response response) throws IOException, ServletException {
    //...
    connector.getService().getMapper().map(serverName, decodedURI,
                    version, request.getMappingData());
    //..
}

這裏只關注如何匹配,省略了一些有關seesion和cookie的處理。

Mapper在StandardService啓動時MapperListener 的start方法初始化。MapperListener啓動這裏放一張截圖方便理解。

在這裏插入圖片描述

Mapper#map

public void map(MessageBytes host, MessageBytes uri, String version,
                MappingData mappingData) throws IOException {

    if (host.isNull()) {
        String defaultHostName = this.defaultHostName;
        if (defaultHostName == null) {
            return;
        }
        host.getCharChunk().append(defaultHostName);
    }
    host.toChars();
    uri.toChars();
    //匹配context(即war包)名稱,設置到mappingData中,匹配成功後匹配context的child wrapper名稱,設置到mappingData中。
    internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData); 
}

private final void internalMap(CharChunk host, CharChunk uri,
            String version, MappingData mappingData) throws IOException {

        if (mappingData.host != null) {
            throw new AssertionError();
        }
		//...
        ContextList contextList = mappedHost.contextList;
        MappedContext[] contexts = contextList.contexts;
        int pos = find(contexts, uri);
        if (pos == -1) {
            return;
        }

        int lastSlash = -1;
        int uriEnd = uri.getEnd();
        int length = -1;
        boolean found = false;
        MappedContext context = null;
        while (pos >= 0) {
            context = contexts[pos];
            //匹配Context
            if (uri.startsWith(context.name)) {
                length = context.name.length();
                if (uri.getLength() == length) {
                    found = true;
                    break;
                } else if (uri.startsWithIgnoreCase("/", length)) {
                    found = true;
                    break;
                }
            }
            if (lastSlash == -1) {
                lastSlash = nthSlash(uri, contextList.nesting + 1);
            } else {
                lastSlash = lastSlash(uri);
            }
            uri.setEnd(lastSlash);
            pos = find(contexts, uri);
        }
        uri.setEnd(uriEnd);

        if (!found) {
            if (contexts[0].name.equals("")) {
                context = contexts[0];
            } else {
                context = null;
            }
        }
        if (context == null) {
            return;
        }

        mappingData.contextPath.setString(context.name);
		//匹配contextversion
        ContextVersion contextVersion = null;
        ContextVersion[] contextVersions = context.versions;
        final int versionCount = contextVersions.length;
        if (versionCount > 1) {
            Context[] contextObjects = new Context[contextVersions.length];
            for (int i = 0; i < contextObjects.length; i++) {
                contextObjects[i] = contextVersions[i].object;
            }
            mappingData.contexts = contextObjects;
            if (version != null) {
                contextVersion = exactFind(contextVersions, version);
            }
        }
        if (contextVersion == null) {
            // Return the latest version
            // The versions array is known to contain at least one element
            contextVersion = contextVersions[versionCount - 1];
        }
        mappingData.context = contextVersion.object;
        mappingData.contextSlashCount = contextVersion.slashCount;

        // Wrapper mapping 匹配wrapper
        if (!contextVersion.isPaused()) {
            internalMapWrapper(contextVersion, uri, mappingData);
        }

    }

這裏主要分爲兩步進行匹配。

  1. 首先匹配context.name,匹配成功後設置到mappingData.contextPath
  2. 匹配wrapper.name,匹配成功後設置到mappingData.wrapperPath
private final void internalMapWrapper(ContextVersion contextVersion,
                                      CharChunk path,
                                      MappingData mappingData) throws IOException {

    //...

    // Rule 1 -- Exact Match 匹配真實的wrapper,即項目裏自己寫的
    MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
    internalMapExactWrapper(exactWrappers, path, mappingData);

    // Rule 2 -- Prefix Match 通配匹配
    boolean checkJspWelcomeFiles = false;
    MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
    if (mappingData.wrapper == null) {
        internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
                                   path, mappingData);
        if (mappingData.wrapper != null && mappingData.jspWildCard) {
            char[] buf = path.getBuffer();
            if (buf[pathEnd - 1] == '/') {
                mappingData.wrapper = null;
                checkJspWelcomeFiles = true;
            } else {
                mappingData.wrapperPath.setChars(buf, path.getStart(),
                                                 path.getLength());
                mappingData.pathInfo.recycle();
            }
        }
    }
	//...

    // Rule 3 -- Extension Match 擴展匹配
    MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
    if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
        internalMapExtensionWrapper(extensionWrappers, path, mappingData,
                                    true);
    }

    // Rule 4 -- Welcome resources processing for servlets
    if (mappingData.wrapper == null) {
        boolean checkWelcomeFiles = checkJspWelcomeFiles;
        if (!checkWelcomeFiles) {
            char[] buf = path.getBuffer();
            checkWelcomeFiles = (buf[pathEnd - 1] == '/');
        }
        if (checkWelcomeFiles) {
            for (int i = 0; (i < contextVersion.welcomeResources.length)
                 && (mappingData.wrapper == null); i++) {
                path.setOffset(pathOffset);
                path.setEnd(pathEnd);
                path.append(contextVersion.welcomeResources[i], 0,
                            contextVersion.welcomeResources[i].length());
                path.setOffset(servletPath);

                // Rule 4a -- Welcome resources processing for exact macth
                internalMapExactWrapper(exactWrappers, path, mappingData);

                // Rule 4b -- Welcome resources processing for prefix match
                if (mappingData.wrapper == null) {
                    internalMapWildcardWrapper
                        (wildcardWrappers, contextVersion.nesting,
                         path, mappingData);
                }

                // Rule 4c -- Welcome resources processing
                //            for physical folder
                if (mappingData.wrapper == null
                    && contextVersion.resources != null) {
                    String pathStr = path.toString();
                    WebResource file =
                        contextVersion.resources.getResource(pathStr);
                    if (file != null && file.isFile()) {
                        internalMapExtensionWrapper(extensionWrappers, path,
                                                    mappingData, true);
                        if (mappingData.wrapper == null
                            && contextVersion.defaultWrapper != null) {
                            mappingData.wrapper =
                                contextVersion.defaultWrapper.object;
                            mappingData.requestPath.setChars
                                (path.getBuffer(), path.getStart(),
                                 path.getLength());
                            mappingData.wrapperPath.setChars
                                (path.getBuffer(), path.getStart(),
                                 path.getLength());
                            mappingData.requestPath.setString(pathStr);
                            mappingData.wrapperPath.setString(pathStr);
                        }
                    }
                }
            }

            path.setOffset(servletPath);
            path.setEnd(pathEnd);
        }

    }

    //...
    // Rule 7 -- Default servlet
    if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
        if (contextVersion.defaultWrapper != null) {
            mappingData.wrapper = contextVersion.defaultWrapper.object;
            mappingData.requestPath.setChars
                (path.getBuffer(), path.getStart(), path.getLength());
            mappingData.wrapperPath.setChars
                (path.getBuffer(), path.getStart(), path.getLength());
            mappingData.matchType = MappingMatch.DEFAULT;
        }
        // Redirection to a folder
        char[] buf = path.getBuffer();
        if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
            String pathStr = path.toString();
            // Note: Check redirect first to save unnecessary getResource()
            //       call. See BZ 62968.
            if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
                WebResource file;
                // Handle context root
                if (pathStr.length() == 0) {
                    file = contextVersion.resources.getResource("/");
                } else {
                    file = contextVersion.resources.getResource(pathStr);
                }
                if (file != null && file.isDirectory()) {
                    // Note: this mutates the path: do not do any processing
                    // after this (since we set the redirectPath, there
                    // shouldn't be any)
                    path.setOffset(pathOffset);
                    path.append('/');
                    mappingData.redirectPath.setChars
                        (path.getBuffer(), path.getStart(), path.getLength());
                } else {
                    mappingData.requestPath.setString(pathStr);
                    mappingData.wrapperPath.setString(pathStr);
                }
            } else {
                mappingData.requestPath.setString(pathStr);
                mappingData.wrapperPath.setString(pathStr);
            }
        }
    }

    path.setOffset(pathOffset);
    path.setEnd(pathEnd);
}

匹配規則:

  1. 匹配exact servlet,即項目裏真實的servlet。
  2. 通用匹配,
  3. 擴展匹配,tomcat容器web.xml默認的servlet
  4. reources匹配,即歡迎頁面等
  5. 匹配未配置的擴展名映射,如index.jsf,index.do。
  6. default servlet的匹配。

這裏重點看一下匹配exact servlet

private final void internalMapExactWrapper
    (MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
    //根據path尋找wrapper
    MappedWrapper wrapper = exactFind(wrappers, path);
    if (wrapper != null) {
        mappingData.requestPath.setString(wrapper.name);
        mappingData.wrapper = wrapper.object;
        if (path.equals("/")) {//根目錄
            // Special handling for Context Root mapped servlet
            mappingData.pathInfo.setString("/");
            mappingData.wrapperPath.setString("");
            // This seems wrong but it is what the spec says...
            mappingData.contextPath.setString("");
            mappingData.matchType = MappingMatch.CONTEXT_ROOT;
        } else {
            mappingData.wrapperPath.setString(wrapper.name);
            mappingData.matchType = MappingMatch.EXACT;
        }
    }
}

匹配成功後,將wrapper.namematchType設置到mappingData中以備調用servlet時使用。這裏放一張截圖方便理解。

在這裏插入圖片描述

到這裏postParseRequest就完成了。

調用servlet

CoyoteAdapter#service

connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);

這裏通過調用每個容器的pipeline的Valve對象,實現了對Servlet的調用,每個容器都有一個或者多個Valve,這些Valve來源於兩個地方,一個是conf/server.xml中配置的Valve,另外一個就是每一個容器的構造其中自己初始化的Valve

這裏調用了StandardEngineValve.invoke,然後繼續往下調用。

public final void invoke(Request request, Response response)
    throws IOException, ServletException {

    // Select the Host to be used for this Request
    Host host = request.getHost();
    if (host == null) {
        return;
    }
    if (request.isAsyncSupported()) {
        request.setAsyncSupported(host.getPipeline().isAsyncSupported());
    }

    // Ask this Host to process this request
    host.getPipeline().getFirst().invoke(request, response);
}

通過debug代碼,可以得到一個完整的調用鏈。

->org.apache.catalina.core.StandardEngineValve#invoke
-->org.apache.catalina.valves.AbstractAccessLogValve#invoke
--->org.apache.catalina.valves.ErrorReportValve#invoke
---->org.apache.catalina.core.StandardHostValve#invoke
----->org.apache.catalina.authenticator.AuthenticatorBase#invoke
------>org.apache.catalina.core.StandardContextValve#invoke
------->org.apache.catalina.core.StandardWrapperValve#invoke

因爲StandardWrapperValve中最終調用了servlet,所以重點看下。

final class StandardWrapperValve extends ValveBase {
    
     public final void invoke(Request request, Response response) throws IOException, ServletException {
         //...
         if (!unavailable) {
             //實例化servlet
             servlet = wrapper.allocate();
         }
         //...
         //create filter調用鏈
         ApplicationFilterChain filterChain =
                ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
         //...
         //調用filter
         filterChain.doFilter(request.getRequest(),
                                    response.getResponse());
     }
}
  1. 實例化servlet。
  2. 創建filter調用鏈。
  3. 調用filter

創建filter調用鏈

ApplicationFilterFactory#createFilterChain

public static ApplicationFilterChain createFilterChain(ServletRequest request,
                                                       Wrapper wrapper, Servlet servlet) {

    // If there is no servlet to execute, return null
    if (servlet == null)
        return null;

    // Create and initialize a filter chain object
    ApplicationFilterChain filterChain = null;
    if (request instanceof Request) {
        Request req = (Request) request;
        if (Globals.IS_SECURITY_ENABLED) {
            filterChain = new ApplicationFilterChain();
        } else {
            filterChain = (ApplicationFilterChain) req.getFilterChain();
            if (filterChain == null) {
                filterChain = new ApplicationFilterChain();
                req.setFilterChain(filterChain);
            }
        }
    } else {
        // Request dispatcher in use
        filterChain = new ApplicationFilterChain();
    }

    filterChain.setServlet(servlet);
    filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());

    // 獲取啓動時初始化的filter
    StandardContext context = (StandardContext) wrapper.getParent();
    FilterMap filterMaps[] = context.findFilterMaps();

    // If there are no filter mappings, we are done
    // 如果沒有filter 直接返回
    if ((filterMaps == null) || (filterMaps.length == 0))
        return filterChain;

    // Acquire the information we will need to match filter mappings
    DispatcherType dispatcher =
        (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);

    String requestPath = null;
    Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);
    if (attribute != null){
        requestPath = attribute.toString();
    }

    String servletName = wrapper.getName();
    //校驗requestPath是否匹配filter的urlPatterns
    // Add the relevant path-mapped filters to this filter chain
    for (int i = 0; i < filterMaps.length; i++) {
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        if (!matchFiltersURL(filterMaps[i], requestPath))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMaps[i].getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        filterChain.addFilter(filterConfig);
    }
    //校驗servlet name是否匹配filter的servletNames
    // Add filters that match on servlet name second
    for (int i = 0; i < filterMaps.length; i++) {
        if (!matchDispatcher(filterMaps[i] ,dispatcher)) {
            continue;
        }
        if (!matchFiltersServlet(filterMaps[i], servletName))
            continue;
        ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
            context.findFilterConfig(filterMaps[i].getFilterName());
        if (filterConfig == null) {
            // FIXME - log configuration problem
            continue;
        }
        filterChain.addFilter(filterConfig);
    }

    // Return the completed filter chain
    return filterChain;
}
  • 首先創建並初始化一個filterChain對象。
  • 循環調用matchFiltersURL(filterMaps[i], requestPath)匹配requestPath與filter的urlPatterns,requestPath即servlet路徑,匹配成功後將當前filter添加到filterChain
private static boolean matchFiltersURL(FilterMap filterMap, String requestPath) {

    // Check the specific "*" special URL pattern, which also matches
    // named dispatches
    if (filterMap.getMatchAllUrlPatterns())
        return true;
    if (requestPath == null)
        return false;
    // Match on context relative request path
    String[] testPaths = filterMap.getURLPatterns();

    for (int i = 0; i < testPaths.length; i++) {
        if (matchFiltersURL(testPaths[i], requestPath)) {
            return true;
        }
    }
    // No match
    return false;

}

private static boolean matchFiltersURL(String testPath, String requestPath) {

    if (testPath == null)
        return false;

    // Case 1 - Exact Match
    if (testPath.equals(requestPath))
        return true;

    // Case 2 - Path Match ("/.../*")
    if (testPath.equals("/*"))
        return true;
    if (testPath.endsWith("/*")) {
        if (testPath.regionMatches(0, requestPath, 0,
                                   testPath.length() - 2)) {
            if (requestPath.length() == (testPath.length() - 2)) {
                return true;
            } else if ('/' == requestPath.charAt(testPath.length() - 2)) {
                return true;
            }
        }
        return false;
    }

    // Case 3 - Extension Match
    if (testPath.startsWith("*.")) {
        int slash = requestPath.lastIndexOf('/');
        int period = requestPath.lastIndexOf('.');
        if ((slash >= 0) && (period > slash)
            && (period != requestPath.length() - 1)
            && ((requestPath.length() - period)
                == (testPath.length() - 1))) {
            return testPath.regionMatches(2, requestPath, period + 1,
                                          testPath.length() - 2);
        }
    }

    // Case 4 - "Default" Match
    return false; // NOTE - Not relevant for selecting filters

}
  • 循環調用matchFiltersServlet(filterMaps[i], servletName)匹配servletName與filter的servletNames,匹配成功後將當前filter添加到filterChain
  private static boolean matchFiltersServlet(FilterMap filterMap,
                                        String servletName) {

        if (servletName == null) {
            return false;
        }
        // Check the specific "*" special servlet name
        else if (filterMap.getMatchAllServletNames()) {
            return true;
        } else {
            String[] servletNames = filterMap.getServletNames();
            for (int i = 0; i < servletNames.length; i++) {
                if (servletName.equals(servletNames[i])) {
                    return true;
                }
            }
            return false;
        }

    }

到這裏創建filter調用鏈就結束了,開始調用filter。

調用Filter

ApplicationFilterChain#doFilter

public void doFilter(ServletRequest request, ServletResponse response)
    throws IOException, ServletException {
    //...
    internalDoFilter(request,response);
}

private void internalDoFilter(ServletRequest request,
                              ServletResponse response)
    throws IOException, ServletException {

    // n=filter.length ,addFilter時初始化。
    if (pos < n) {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = filterConfig.getFilter();
		//調用filter
        filter.doFilter(request, response, this);
      
        return;
    }
	//...
    //filter調用完成之後調用servlet的service方法。
    servlet.service(request, response);
   
}

調用servlet

最後會調用HttpServlet的service方法來區分調用GET/POST等方法。

protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException {

    String method = req.getMethod();

    if (method.equals(METHOD_GET)) {
        long lastModified = getLastModified(req);
        if (lastModified == -1) {
            doGet(req, resp);
        } else {
            long ifModifiedSince;
            try {
                ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
            } catch (IllegalArgumentException iae) {
                ifModifiedSince = -1;
            }
            if (ifModifiedSince < (lastModified / 1000 * 1000)) {
                // If the servlet mod time is later, call doGet()
                // Round down to the nearest second for a proper compare
                // A ifModifiedSince of -1 will always be less
                maybeSetLastModified(resp, lastModified);
                doGet(req, resp);
            } else {
                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
            }
        }

    } else if (method.equals(METHOD_HEAD)) {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);

    } else if (method.equals(METHOD_POST)) {
        doPost(req, resp);

    } else if (method.equals(METHOD_PUT)) {
        doPut(req, resp);

    } else if (method.equals(METHOD_DELETE)) {
        doDelete(req, resp);

    } else if (method.equals(METHOD_OPTIONS)) {
        doOptions(req,resp);

    } else if (method.equals(METHOD_TRACE)) {
        doTrace(req,resp);

    } else {
        //
        // Note that this means NO servlet supports whatever
        // method was requested, anywhere on this server.
        //

        String errMsg = lStrings.getString("http.method_not_implemented");
        Object[] errArgs = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);

        resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
    }
}

以上。

總結

通過上面的分析,其實我們已經清楚了當一個請求過來以後,Tomcat是如何處理的。我們再來做一個總體的總結:

  1. 用戶瀏覽器發送請求,請求會發送到對應的Connector監聽的Socket端口。
  2. Connector從Socket流中獲取數據,然後根據Http協議將其解析爲Request和Reponse對象。
  3. 找到Request對象對應的Host,Context,Wrapper。
  4. 調用filter鏈。
  5. 調用最終的Servelt的service進行處理。

最後放一張整體的調用鏈。

在這裏插入圖片描述

參考:

https://github.com/c-rainstorm/blog/blob/master/tomcat/%E8%B0%88%E8%B0%88Tomcat%E8%AF%B7%E6%B1%82%E5%A4%84%E7%90%86%E6%B5%81%E7%A8%8B.md

https://blog.csdn.net/jiaomingliang/article/details/47414657

https://www.jianshu.com/p/d8a2bc7d3c21

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