天天看點

Tomcat-Connector源碼分析源碼分析

《看透SpringMVC源碼分析與實踐》

引用 https://yq.aliyun.com/articles/20177

Connector用于接收請求并将請求封裝成Request和Response來具體處理,最底層是使用Socket協定來進行連接配接的,在封裝完成之後交給Container進行處理(即Servlet容器),Container處理完畢之後傳回給Connector,最終Connector使用Socket将處理結果傳回給用戶端。這樣整個請求就處理完成了。

Connector中具體是用

ProtocolHandler

來處理請求的,不同的ProtocolHandler代表不同的連接配接類型:

三種HTTP協定,用戶端與Tomcat伺服器直接連接配接。

  • Http11Protocol

    : BIO,支援http1.1協定
  • Http11NioProtocol

    : NIO,支援http1.1協定
  • Http11AprProtocol

    : ARP(Apache portable runtime),支援http1.1協定 Tomcat 7.0.30版本開始,預設使用此方式。

以及三種帶AJP13是定向包協定.

  • AjpProtocol

    : BIO,支援AJP協定
  • AjpNioProtocol

    ,NIO,支援AJP協定
  • AjpAprProtocol

    ,Apr ,支援AJP協定

什麼是AJP

AJPv13協定是面向資料包的。WEB伺服器和Servlet容器通過TCP連接配接來互動;為了節省SOCKET建立的昂貴代價,WEB伺服器會嘗試維護一個

永久TCP連接配接(長連接配接)

到servlet容器,并且在多個請求和響應周期過程會重用連接配接。
  • Apache是世界排名第一的Web伺服器,它支援處理靜态頁面,Apache可以支援PHP。
  • Tomcat支援動态頁面(servlet, JSP),對靜态頁面的處理不太理想。
  • Tomcat能夠承擔性能要求較低的WEB應用。
  • 通常會使用Apache + Tomcat的配置下,Apache處理靜态頁面,對于JSP則轉發交給Tomcat來處理。
AJP就是Apache和Tomcat之間的通訊基本有三種方式之一,另外常見的還有`Mod_Jk、HTTP_Proxy`協定. ![這裡寫圖檔描述](https://img-blog.csdn.net/20180815215028689?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2l0X2ZyZXNobWFu/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 引用:http://blog.chinaunix.net/uid-20662363-id-3012760.html

ProtocolHandler有三個非常重要的元件:Endpoint,Processor和Adapter.

  • Endpoint

    : 用于處理底層Socket的網絡協定。 Endpoint的抽象實作類

    AbstractEndpoint

    中定義了Acceptor,AsyncTimeout兩個内部類和一個接口Handler
    • Acceptor:

      用于監聽請求。
    • AsyncTimeout:

      用于異步檢查request逾時。
    • Handler:

      用于處理接收到的Socket,在内部調用了

      Processor

  • Processor

    : 用于将Endpoint接收到的Socket封裝成Request
  • Adapter

    : 用于将封裝好的Request交給Containner進行具體處理。

Connector處理請求過程

如下圖:

Tomcat-Connector源碼分析源碼分析

源碼分析

執行個體化##

Connector執行個體化

Connector是在Catalina的load方法中,執行

createStartDigester()

解析

server.xml

配置檔案的配置資訊所建立的。

server.xml

-part

<Connector port="8080" protocol="HTTP/1.1" 
connectionTimeout="20000" redirectPort="8443" />

<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
           

java建立-流程

//org.apache.catalina.startup.Catalina.createStartDigester()
digester.addRule("Server/Service/Connector", new ConnectorCreateRule());

//org.apache.catalina.startup.ConnectorCreateRule.begin()                         
Connector con = new Connector(attributes.getValue("protocol"));
           

可見最終Connector的建立流程,執行到了Connector的構造函數中。

//org.apache.catalina.Connector
public Connector(String protocol) {
	  // 将server.xml中配置的protocol “HTTP/1.1”,“AJP/1.3” 轉換為上文中的6種protocol;  
      setProtocol(protocol);
      Class<?> clazz = Class.forName(protocolHandlerClassName);
		
	  //調用具體protocol的無參構造函數,執行個體化。
      this.protocolHandler = (ProtocolHandler) clazz.newInstance();
}

 
public void setProtocol(String protocol) {
	//若支援apr預設為:xxxAprProtocol
     if (AprLifecycleListener.isAprAvailable()) {
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpAprProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         } else {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11AprProtocol");
         }
     } else {
      //否則預設為:Http11Protocol 或者 AjpProtocol
         if ("HTTP/1.1".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.http11.Http11Protocol");
         } else if ("AJP/1.3".equals(protocol)) {
             setProtocolHandlerClassName
                 ("org.apache.coyote.ajp.AjpProtocol");
         } else if (protocol != null) {
             setProtocolHandlerClassName(protocol);
         }
     }

 }
           

在構造函數中,最主要的就是指定ProtocolHandler的類型,并通過執行newInstance方法,建立具體的ProtocolHandler執行個體。

ProtocolHandler執行個體化

ProtocolHandler類圖,如下:

Tomcat-Connector源碼分析源碼分析

從上圖可以看到,ProtocolHandler有一個抽象實作類

AbstractProtocol

,AbstractProtocol又有

AbstractAjpProtocol

AbstractHttp11Protocol

抽象子類,以及本文開局提到的最終的六種ProtocolHandler子類。

本文以**

Http11NioProtocol

**為例,分析ProtocolHandler。

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProtocol() {
	// 建立NioEndpoint
     endpoint=new NioEndpoint();
     //建立Http11ConnectionHandler
     cHandler = new Http11ConnectionHandler(this);
	//指定NioEndpoint的handler,
     ((NioEndpoint) endpoint).setHandler(cHandler);
     setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
     setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
     setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
 }
           

構造函數中,最主要是NioEndpoint類型的Endpoint,Http11ConnectionHandler類型的Handler并将其設定到Endpoint中。

Endpoint

Endpoint用于處理具體的連接配接和傳輸資料,

AbstractEndpoint

是所有Endpoint的父類,類圖如下:

Tomcat-Connector源碼分析源碼分析

其中NioEndpoint中處理請求的具體流程如下,

Tomcat-Connector源碼分析源碼分析

Poller和SocketProcessor

是NioEndpoint的内部類,後續會講到。

初始化##

整個Connector的初始化過程流程見下圖:

Connector初始化

根據之前的經驗,Connector的生命周期方法是在Service中調用的,首先調用生命周期LifecycleBase.init(),然後再調用Connector.initInternal()。

Tomcat-Connector源碼分析源碼分析
//org.apache.catalina.Connector
protected void initInternal() throws LifecycleException {
     super.initInternal();

     //建立新的Adapter,并設定到protocolHandler,
     //Adapter的主要作用是通過自身的service方法調用Container管道中的invoke方法來處理強求
     adapter = new CoyoteAdapter(this);

	//将Adapter設定到protocolHandler中
     protocolHandler.setAdapter(adapter);

	 //省略部分代碼....
	
	//初始化protocolHandler
    protocolHandler.init();

     //初始化mapperListener
     mapperListener.init();
 }
           

ProtocolHandler初始化

本文的ProtocolHandler預設使用

Http11NioProtocol

,Http11NioProtocol的初始化邏輯是調用

AbstractProtocol.init() --> Endpoint.init()

:

Endpoint初始化

Http11NioProtocol中的Endpoint類型為:

NioEndpoint

,在ProtocolHandler的初始化過程中會調用NioEndpoint初始化方法, 而NioEndpoint的初始化邏輯是調用

AbstractEndpoint.init() -->NioEndpoint.bind():

//org.apache.tomcat.util.net.NioEndpoint
public void bind() throws Exception {
	//初始化ServerSocketChannel
	 serverSock = ServerSocketChannel.open();
     socketProperties.setProperties(serverSock.socket());
     InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
     serverSock.socket().bind(addr,getBacklog());
     serverSock.configureBlocking(true); //mimic APR behavior
     serverSock.socket().setSoTimeout(getSocketProperties().getSoTimeout());
	
	//需要啟動acceptor線程的個數,如果是0,則設定為1
	if (acceptorThreadCount == 0) {
       acceptorThreadCount = 1;
     }
	
	//需要設定poller線程個數
     if (pollerThreadCount <= 0) {
         pollerThreadCount = 1;
     }
	//省略ssl....
	
   if (oomParachute>0) reclaimParachute(true);
	 
	//建立一個NioBlockingSelector
    selectorPool.open();
}
           

啟動

啟動流程如下圖:

Tomcat-Connector源碼分析源碼分析

Endpoint啟動

//org.apache.tomcat.util.net.NioEndpoint
 public void startInternal() throws Exception {

        if (!running) {
            running = true;
            paused = false;

            // 建立工作線程池
            if ( getExecutor() == null ) {
                createExecutor();
            }

            initializeConnectionLatch();

            // 啟動Poller線程,用來處理Acceptor傳遞來的socket請求。
            pollers = new Poller[getPollerThreadCount()];
            for (int i=0; i<pollers.length; i++) {
                pollers[i] = new Poller();
                Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
                pollerThread.setPriority(threadPriority);
                pollerThread.setDaemon(true);
                pollerThread.start();
            }
			
			//啟動Acceptor線程,用來監聽接收請求;
            startAcceptorThreads();
        }
    }
           

這裡首先初始化了一些屬性,然後啟動了Poller和Acceptor線程,通過這些線程或線程組用來監聽請求并處理請求。

MapperListener啟動

//org.apache.catalina.connector.MapperListener
public void startInternal() throws LifecycleException {
    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())) {
            //注冊host至mapper,同時遞歸的将所有的context,wrapper注冊
            registerHost(host);
        }
    }
}
           

MapperListener啟動,将所有的Context,以及Wrapper都注冊值mapper屬性中,供後續的請求uri比對。

Tomcat-Connector源碼分析源碼分析

HTTP請求處理

1.Acceptor

org.apache.tomcat.util.net.NioEndpoint.Acceptor

是NioEndpoint的一個内部類,它用來接收請求:

//org.apache.tomcat.util.net.NioEndpoint.Acceptor 
 protected class Acceptor extends AbstractEndpoint.Acceptor {
	  @Override
      public void run() {
        int errorDelay = 0;
		while (running) {
			//省略其他判斷.....
			SocketChannel socket =  serverSock.accept();
			
			//将socket注冊到Poller中的selector中。
			 if (!setSocketOptions(socket)) {
               countDownConnection();
               closeSocket(socket);
             }
		}
      }
 }
           

setSocketOptions()

// org.apache.tomcat.util.net.NioEndpoint
protected boolean setSocketOptions(SocketChannel socket) {
	//設定socket非阻塞。
	socket.configureBlocking(false);
	Socket sock = socket.socket();
	
	// 設定socket熟悉,如keepAlive,timeout 等資訊
	socketProperties.setProperties(sock);
	
	// channel 包含
	NioChannel channel = nioChannels.poll();
	NioBufferHandler bufhandler = new NioBufferHandler(
		socketProperties.getAppReadBufSize(),
		socketProperties.getAppWriteBufSize(),
		socketProperties.getDirectBuffer());
	channel = new NioChannel(socket, bufhandler);
	channel.setIOChannel(socket);
	channel.reset();
	
	// 将chanel注冊到Poller線程
	getPoller0().register(channel);

}
           

2.Poller

org.apache.tomcat.util.net.NioEndpoint.Poller

也是NioEndpoint的一個内部類:

//org.apache.tomcat.util.net.NioEndpoint.Poller
public class Poller implements Runnable {
     
   public Poller() throws IOException {
     synchronized (Selector.class) {
	   		//擷取目前線程的selector,不是bind方法中selectorPool.open();的selector
        this.selector = Selector.open();
     }
   }
	
	public void register(final NioChannel socket) {
	    socket.setPoller(this);
	    KeyAttachment key = keyCache.poll();
	    
	    /*
	     * 建立一個新的KeyAttachment,入參是socket
	     * KeyAttachment的類結構繼承SocketWrapper<NioChannel>
	     */
	    final KeyAttachment ka = key!=null?key:new KeyAttachment(socket);
	    ka.reset(this,socket,getSocketProperties().getSoTimeout());
	    
	    //關注read事件
	  	ka.interestOps(SelectionKey.OP_READ);
	  	
	    PollerEvent r = eventCache.poll();
	    if ( r==null) {
	    	//PollerEvent作用:将SocketChannel注冊到selector中
	    	r = new PollerEvent(socket,ka,OP_REGISTER);
	    } else {
	    	r.reset(socket,ka,OP_REGISTER);
	    }
	    //添加event
	    addEvent(r);
	}
   	
   @Override
   public void run() {
     //省略其他.....
     
     keyCount = selector.selectNow();
     if ( keyCount == 0 ) hasEvents = (hasEvents | events());
     
     //輪詢準備就緒的Key
     Iterator<SelectionKey> iterator = keyCount > 0 ? selector.selectedKeys().iterator() : null;

     while (iterator != null && iterator.hasNext()) {
      SelectionKey sk = iterator.next(); //阻塞...
      KeyAttachment attachment = (KeyAttachment)sk.attachment();
      if (attachment == null) {
          iterator.remove();
      } else {
          attachment.access();
          iterator.remove();
          
          //調用processKey 處理socket
          processKey(sk, attachment);
     }
    }
  }
  
   //處理SelectionKey,使用read,write方法等。
   protected boolean processKey(SelectionKey sk, KeyAttachment attachment) {
	  unreg(sk, attachment, sk.readyOps());
		boolean closeSocket = false;
		if (sk.isReadable()) {
			//調用processSocket處理socket
		    if (!processSocket(channel, SocketStatus.OPEN_READ, true)) {
		        closeSocket = true;
		    }
		}
		if (!closeSocket && sk.isWritable()) {
		    if (!processSocket(channel, SocketStatus.OPEN_WRITE, true)) {
		        closeSocket = true;
		    }
		}
		if (closeSocket) {
		    cancelledKey(sk,SocketStatus.DISCONNECT,false);
		}
  }
}
           

,上述代碼的作用

  • 處理Acceptor接收到NioChannel并注冊到

    selector

  • 然後調用後續的

    processKey()

    方法,該方法用來處理NioChannel的read,write方法等。
  • 上述方法内部又調用了

    Endpoint.processSocket()

    來處理,在本方法内部主要是将請求轉交給

    SocketProcessor

//org.apache.tomcat.util.net.NioEndpoint
 public boolean processSocket(NioChannel socket, SocketStatus status, boolean dispatch) {
 	KeyAttachment attachment = (KeyAttachment)socket.getAttachment(false);
 	SocketProcessor sc = processorCache.poll();
 	//構造SocketProcessor
    if ( sc == null ) {
    	sc = new SocketProcessor(socket,status);
    } else {
    	sc.reset(socket,status);
    }
    
    if ( dispatch && getExecutor()!=null ) {
        //通過線程池處理SocketProcessor
    	getExecutor().execute(sc);
    }else {
        //調用SocketProcessor.run()處理請求
    	sc.run();
    }
 }
           

4.SocketProcessor

protected class SocketProcessor implements Runnable {

    protected NioChannel socket = null;
    protected SocketStatus status = null;

    public SocketProcessor(NioChannel socket, SocketStatus status) {
        reset(socket,status);
    }

    public void reset(NioChannel socket, SocketStatus status) {
        this.socket = socket;
        this.status = status;
    }
    
   public void run() {
	    SelectionKey key = socket.getIOChannel().keyFor(
	            socket.getPoller().getSelector());
	    KeyAttachment ka = (KeyAttachment)key.attachment();
	    //省略其他....
	    
	    doRun(key, ka);
   }
    
   private void doRun(SelectionKey key, KeyAttachment ka) {
    SocketState state = SocketState.OPEN;
   	 /*
   	  * 調用handler處理請求, 
   	  * 此時的handler為在Http11NioProtocol構造函數中設定的Http11ConnectionHandler(this)
   	  */
   	 state = handler.process(ka, SocketStatus.OPEN_READ); 
   }

}
           

SocketProcessor處理請求,将請求交給

Http11ConnectionHandler

處理。

5.Handler

Hander的實際類型為Http11ConnectionHandler,它的類圖如下:

Tomcat-Connector源碼分析源碼分析

process()

方法最終在

AbstractProtocol.AbstractConnectionHandler

中實作。

//org.apache.coyote.AbstractProtocol.AbstractConnectionHandler
public SocketState process(SocketWrapper<S> wrapper,
                SocketStatus status) {
	S socket = wrapper.getSocket();   
	Processor<S> processor = connections.get(socket);
	if (processor == null) {
		//擷取processor執行個體
	    processor = createProcessor();
	}         
	//調用processor.process()
	SocketState state = processor.process(wrapper);
	
	//省略其他...
	return SocketState.CLOSED;
}
           

,方法的主要邏輯是

  • 擷取建立Processor,此時示例類型是

    Http11NioProcessor

  • 然後調用Processor處理請求

6.Processor

通過

Http11NioProtocol.createProcessor()

擷取的Processor,代碼如下:

//org.apache.coyote.http11.Http11NioProtocol
public Http11NioProcessor createProcessor() {
    Http11NioProcessor processor = new Http11NioProcessor(
            proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
            proto.getMaxTrailerSize(), proto.getMaxExtensionSize());
    processor.setAdapter(proto.adapter);
    processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
    processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
  	//省略其他一些屬性配置......
    register(processor);
    return processor;
}
           

,由上述代碼可知Processor的具體類型是

Http11NioProcessor

processor.process()

方法處理請求邏輯來自它的父類

AbstractHttp11Proccessor

//org.apache.coyote.http11.AbstractHttp11Processor
public SocketState process(SocketWrapper<S> socketWrapper)
       throws IOException {
   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);
   
    if (!error) {
      rp.setStage(org.apache.coyote.Constants.STAGE_PREPARE);
      // 預處理,設定解析請求頭,URI,METHOD等等....
      prepareRequest();
   }      
   if (!error) {
     rp.setStage(org.apache.coyote.Constants.STAGE_SERVICE);
     //調用Adapter執行請求
     adapter.service(request, response);
   }
}
           

在這個方法裡面,最終請求交給Adapter處理,

注意可以看到此時請求的參數是org.apache.coyote.Request,org.apache.coyote.Response

7.Adapter

Tomcat7中Adapter隻有一個實作類

CoyoteAdapter

,處理邏輯如下:

//org.apache.catalina.connector.CoyoteAdapter
public void service(org.apache.coyote.Request req,
                    org.apache.coyote.Response res){
	
	/*
	 * 将org.apache.coyote.Request , Response
	 * 轉換為org.apache.catalina.connector.Request , Response
	 */
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);
    
    // postParseRequest:設定scheme,serverName,port,sessionId等
    boolean postParseSuccess = postParseRequest(req, request, res, response);
    if (postParseSuccess) {
    	//交給container處理請求	
    	connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
	}

	//判斷目前請求類型
      AsyncContextImpl asyncConImpl = (AsyncContextImpl)request.getAsyncContext();
       if (asyncConImpl != null) {
           async = true;
       } else if (!comet) {
     		// 如果是非異步請求,則結束請求
           request.finishRequest();
           response.finishResponse();
           if (postParseSuccess && request.getMappingData().context != null) {
               ((Context) request.getMappingData().context).logAccess( request, response,System.currentTimeMillis() - req.getStartTime(), false);      
           }
           req.action(ActionCode.POST_REQUEST , null);
       }
	
}
           

service方法的作用,主要是完成如下2個任務:

  • 預處理請求,通過request URI的資訊找到屬于自己的Context和Wrapper
  • 調用container處理請求。

1).postParseRequest()

a.解析,host,context,wrapper,session等資訊。

//org.apache.catalina.connector.CoyoteAdapter
 protected boolean postParseRequest(org.apache.coyote.Request req,
                                       Request request,
                                       org.apache.coyote.Response res,
                                       Response response) {
    String proxyName = connector.getProxyName();
    int proxyPort = connector.getProxyPort();
    if (proxyPort != 0) {
        req.setServerPort(proxyPort);
    }
    if (proxyName != null) {
        req.serverName().setString(proxyName);
    }
    
    //該方法主要作用是解析url中解析出來參數,并放置到request參數清單裡面。
    parsePathParameters(req, request);
     
    //略...
     boolean mapRequired = true;
     while (mapRequired) {
	    // 将host,context,wrapper等屬性解析存放至 request.getMappingData()
	    connector.getMapper().map(serverName, decodedURI, version,request.getMappingData());
	           
	    //設定比對的context,wrapper                   
	    request.setContext((Context) request.getMappingData().context);
	    request.setWrapper((Wrapper) request.getMappingData().wrapper);
	          
	    //略....
	    String sessionID = null;
	    if (request.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.URL)) {     
	        //從url參數中設定的sessionId
	        //形如:protocol://host:port/path/servlet.do;jsessionid=xxxxx?username=trump 的url中包含sessionId
	        //url參數清單中session的key為 "jsessionid",定義在org.apache.catalina.util.SessionConfig.DEFAULT_SESSION_PARAMETER_NAME
	        sessionID = request.getPathParameter(SessionConfig.getSessionUriParamName(request.getContext()));
	        if (sessionID != null) {
	        		//設定sessionId,且來自URL
	            request.setRequestedSessionId(sessionID);
	            request.setRequestedSessionURL(true);
	        }
	    }
	    //從Cookie中尋找SessionId,可能會覆寫浏覽器中的sessionId
	    parseSessionCookiesId(req, request);
	    parseSessionSslId(request);
		
			//略.....
	  }
      	
}

//從cookie中解析SessionId
protected void parseSessionCookiesId(org.apache.coyote.Request req, Request request) {
    //如果已禁用目前通過cookie的會話跟蹤上下文,不要在cookie中查找cookie中的SessionID
    Context context = (Context) request.getMappingData().context;
    if (context != null && !context.getServletContext().getEffectiveSessionTrackingModes().contains(SessionTrackingMode.COOKIE)) {     
        return;
    }

    // Parse session id from cookies
    Cookies serverCookies = req.getCookies();
    int count = serverCookies.getCookieCount();
    if (count <= 0) {
        return;
    }
	
		//預設值為 "JSESSIONID"
    String sessionCookieName = SessionConfig.getSessionCookieName(context);

    for (int i = 0; i < count; i++) {
        ServerCookie scookie = serverCookies.getCookie(i);
     		//周遊cookie
        if (scookie.getName().equals(sessionCookieName)) {    		
        		//“requestedSessionId”當為空時進入if,否則進入else
            if (!request.isRequestedSessionIdFromCookie()) {
            		//僅接受第一個來自cookie的SessionID
                convertMB(scookie.getValue());
                //設定SessionId,内部邏輯為給“requestedSessionId”指派
                request.setRequestedSessionId (scookie.getValue().toString());
                //session來自cookie設定true, 來自url設為false
                request.setRequestedSessionCookie(true);
                request.setRequestedSessionURL(false); 
            } else {
            		//判斷session是否過期
                if (!request.isRequestedSessionIdValid()) { 
                		//替換sessionId,直到sessionId有效為止
                    convertMB(scookie.getValue());
                    request.setRequestedSessionId(scookie.getValue().toString());        
                }
            }
        }
    }

}
           

b.servlet擷取session的過程

Tomcat-Connector源碼分析源碼分析
參考Tomcat中session的管理機制

2).Container處理請求

Containner處理請求的流程如下圖:

Tomcat-Connector源碼分析源碼分析
  • StandardEngineValve.invoke()

    :尋找host,并交由host.getPipeline()處理請求
  • StandardHostValve.invoke()

    :設定context,并設定

    context.getLoader().getClassLoader()

    ,最終交由context.getPipeline()處理請求
  • `StandardContextValve.invoke()``:尋找wrapper,并交由wrapper.getPipeline()處理請求
  • StandardWrapperValve.invoke()

    :通過wrapper定位初&始化、

    javax.servlet.Servlet

    ,并執行FilterChain執行過程中調用

    servlet.service()方法

//org.apache.catalina.core.StandardWrapperValve
 public final void invoke(Request request, Response response)
        throws IOException, ServletException {
        StandardWrapper wrapper = (StandardWrapper) getContainer();
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();
        
        //定位Servlet,并執行servlet.init()
        servlet = wrapper.allocate();
        
        //建立本次請求的filterChain
        ApplicationFilterFactory factory = ApplicationFilterFactory.getInstance(); 
        ApplicationFilterChain filterChain = factory.createFilterChain(request, wrapper, servlet);
        
        //省略其他....
        
        //調用filterChain,并在此方法内部執行service.service()
        filterChain.doFilter(request.getRequest(), response.getResponse());
} 
           
//org.apache.catalina.core.ApplicationFilterChain
 private void internalDoFilter(ServletRequest request, 
                                  ServletResponse response)
        throws IOException, ServletException {
        ApplicationFilterConfig filterConfig = filters[pos++];
        Filter filter = null;
        filter = filterConfig.getFilter();
         
        // 省略其他.....
        servlet.service(request, response);
}
           

繼續閱讀