Tomcat筆記

Tomcat組件和流程java

  源碼:https://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-8/v8.5.59/src/apache-tomcat-8.5.59-src.zipweb

  組件結構apache

  

                                  圖1.1tomcat

  Tomcat核心的組件即爲圖中左側的Connector和Container網絡

  1 Connectorsession

   EndPoint負責實現並處理TCP/IP協議併發

   Processor負責實現並處理Http協議,封裝Request、Response對象;app

   ProtocolHandler:協議接口,tomcat中有6個實現類,用於實現具體的協議處理能力,包括:AjpNioProtocol、AjpApr、AjpNio、Http11Nio、Http11Nio二、Http11Apr。webapp

   Adapter:適配器,CoyoteAdapter,負責把Request轉換成ServletRequest對象。Response反之。socket

  2 Container

  Container包含了一個Engine組件,在Tomcat中的java類組成結構來看,與圖中的右側一致,可理解爲一層一層的包裝類。

  Engine組件中能夠包含多個Host;Host組件中包含多個Context;Context包含多個Wrapper;

  能夠經過一個Url連接來理解其中的包含關係: http://localhost:8080/shop/user/getuserInfo?userid=1001

  

                                    圖1.2

  Wrapper即爲具體的Servlet,用於處理具體請求。開發人員定義的Servlet會被tomcat包裝成一個Wrapper;

Tomcat源碼

   

                  圖1.3

  從Catalina.sh腳本中可追蹤到,Tomcat的入口爲Bootstrap類中的main方法。「org.apache.catalina.startup.Bootstrap  start」

  啓動流程

                                                                                           流程圖來源:拉鉤-應癲

 

 

                                                   圖1.4

 

  

  Catalina的load方法中經過Digester類來解析server.xml,而後實例化一個Server對象,從左向右,類可看作左側包含右側的組合模式。Catalina經過load方法觸發Server實例化,並調用Server.init(),逐步調用各組件的子組件的init方法,也就是圖中的從左向右的順序進行初始化。同理,Bootstrap中的start()方法同樣。

  Tomcat類圖 

      高清地址(https://www.processon.com/view/link/5d108a4de4b0955b9368b911  

 

  lifecycle類爲頂層接口,定義了生命週期的基本4個方法init()、start()、stop()、destory(),各個組件的Sandard*類實現用於具體功能的操做,貫徹了tomcat全部組件。抽象類LifecycleBase則使用的模板方法和狀態機模式來實現。tomcat還定義了LifecycleState(狀態)、LifecycleEvent(事件)、LifecycleListener(監聽)用於實現狀態、監聽以及觸發。

  init()執行完成以後,開始實現start()方法。同init方法一直。逐層傳遞啓動。在Service中的start()方法分別調用了Engine、Executor和Connector的start()方法

  Service.start()

  

 1     protected void startInternal() throws LifecycleException {
 2 
 3         if(log.isInfoEnabled())
 4             log.info(sm.getString("standardService.start.name", this.name));
 5         setState(LifecycleState.STARTING);
 6 
 7         // Start our defined Container first
 8         if (engine != null) {
 9             synchronized (engine) {
10                 engine.start();
11             }
12         }
13 
14         synchronized (executors) {
15             for (Executor executor: executors) {
16                 executor.start();
17             }
18         }
19 
20         mapperListener.start();
21 
22         // Start our defined Connectors second
23         synchronized (connectorsLock) {
24             for (Connector connector: connectors) {
25                 try {
26                     // If it has already failed, don't try and start it
27                     if (connector.getState() != LifecycleState.FAILED) {
28                         connector.start();
29                     }
30                 } catch (Exception e) {
31                     log.error(sm.getString(
32                             "standardService.connector.startFailed",
33                             connector), e);
34                 }
35             }
36         }
37     }

 

  engine.start()主要加載webapps下的項目配置,也就是Context。同時開啓Background線程,用於監控session數據。

  connector.start(),調用ProtocolHandler.start();經過配置的協議調用相應Endpoint.start();

  下面是Http11(NioEndPoint.class)的startInternal()

  

 1  public void startInternal() throws Exception {
 2 
 3         if (!running) {
 4             running = true;
 5             paused = false;
 6 
 7             processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
 8                     socketProperties.getProcessorCache());
 9             eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
10                             socketProperties.getEventCache());
11             nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
12                     socketProperties.getBufferPool());
13 
14             // Create worker collection
15             if ( getExecutor() == null ) {
16                 createExecutor();
17             }
18 
19             initializeConnectionLatch();
20 
21             // Start poller threads
22             pollers = new Poller[getPollerThreadCount()];
23             for (int i=0; i<pollers.length; i++) {
24                 pollers[i] = new Poller();
25                 Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i);
26                 pollerThread.setPriority(threadPriority);
27                 pollerThread.setDaemon(true);
28                 pollerThread.start();
29             }
30 
31             startAcceptorThreads();
32         }
33     }

   方法中開啓了Poller線程,用於監聽後續Socket.accept(); 每一個Poller對象中包含一個Selector; 線程中用於處理每次服務接受的網絡的請求:selector.selectNow();

   startAcceptorThreads():

    protected final void startAcceptorThreads() {
        int count = getAcceptorThreadCount();
        acceptors = new Acceptor[count];

        for (int i = 0; i < count; i++) {
            acceptors[i] = createAcceptor();
            String threadName = getName() + "-Acceptor-" + i;
            acceptors[i].setThreadName(threadName);
            Thread t = new Thread(acceptors[i], threadName);
            t.setPriority(getAcceptorThreadPriority());
            t.setDaemon(getDaemon());
            t.start();
        }
    }
createAcceptor();方法建立了Acceptor對象。Acceptor爲Runnable子類。其run()方法中開啓了Socket(SocketChannel)監聽:socket = serverSock.accept(); 用於監聽來自用戶發過來的請求。
 1  protected class Acceptor extends AbstractEndpoint.Acceptor {
 2 
 3         @Override
 4         public void run() {
 5 
 6             int errorDelay = 0;
 7 
 8             // Loop until we receive a shutdown command
 9             while (running) {
10 
11                 // Loop if endpoint is paused
12                 while (paused && running) {
13                     state = AcceptorState.PAUSED;
14                     try {
15                         Thread.sleep(50);
16                     } catch (InterruptedException e) {
17                         // Ignore
18                     }
19                 }
20 
21                 if (!running) {
22                     break;
23                 }
24                 state = AcceptorState.RUNNING;
25 
26                 try {
27                     //if we have reached max connections, wait
28                     countUpOrAwaitConnection();
29 
30                     SocketChannel socket = null;
31                     try {
32                         // Accept the next incoming connection from the server
33                         // socket
34                         socket = serverSock.accept();
35                     } catch (IOException ioe) {
36                         // We didn't get a socket
37                         countDownConnection();
38                         if (running) {
39                             // Introduce delay if necessary
40                             errorDelay = handleExceptionWithDelay(errorDelay);
41                             // re-throw
42                             throw ioe;
43                         } else {
44                             break;
45                         }
46                     }
47                     // Successful accept, reset the error delay
48                     errorDelay = 0;
49 
50                     // Configure the socket
51                     if (running && !paused) {
52                         // setSocketOptions() will hand the socket off to
53                         // an appropriate processor if successful
54                         if (!setSocketOptions(socket)) {
55                             closeSocket(socket);
56                         }
57                     } else {
58                         closeSocket(socket);
59                     }
60                 } catch (Throwable t) {
61                     ExceptionUtils.handleThrowable(t);
62                     log.error(sm.getString("endpoint.accept.fail"), t);
63                 }
64             }
65             state = AcceptorState.ENDED;
66         }

  Catalina的start流程主要實例化了一些的線程池並開啓監聽Socket端口。

Tomcat請求與響應(Request、Response)

  Mapper類

    mapper可理解爲一個映射,包含了Host、Context和Wrapper的信息。其組成也結構也爲層級包含。Mapper包含Host。Host包含Context,Context包含Wrapper。Mapper類中定義了對應的MapElement的內部類。MappedHost、MappedContext和MappedWrapper,從左向右,爲一對多的包含關係。

   上面提到EndPoint.start()會開啓Poller線程用於處理網絡的請求。

  Poller --> run() : 

 1 while (iterator != null && iterator.hasNext()) {
 2                     SelectionKey sk = iterator.next();
 3                     NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
 4                     // Attachment may be null if another thread has called
 5                     // cancelledKey()
 6                     if (attachment == null) {
 7                         iterator.remove();
 8                     } else {
 9                         iterator.remove();
10                         processKey(sk, attachment);
11                     }
12                 }

 

  processKey()方法會調用processSocket()來處理Socket請求。

  

 1 public boolean processSocket(SocketWrapperBase<S> socketWrapper,
 2             SocketEvent event, boolean dispatch) {
 3         try {
 4             if (socketWrapper == null) {
 5                 return false;
 6             }
 7             SocketProcessorBase<S> sc = processorCache.pop();
 8             if (sc == null) {
 9                 sc = createSocketProcessor(socketWrapper, event);
10             } else {
11                 sc.reset(socketWrapper, event);
12             }
13             Executor executor = getExecutor();
14             if (dispatch && executor != null) {
15                 executor.execute(sc);
16             } else {
17                 sc.run();
18             }
19         } catch (RejectedExecutionException ree) {
20             getLog().warn(sm.getString("endpoint.executor.fail", socketWrapper) , ree);
21             return false;
22         } catch (Throwable t) {
23             ExceptionUtils.handleThrowable(t);
24             // This means we got an OOM or similar creating a thread, or that
25             // the pool and its queue are full
26             getLog().error(sm.getString("endpoint.process.fail"), t);
27             return false;
28         }
29         return true;
30     }

  processSocket方法則爲每一個Socket又開啓了一個線程對象 SocketProcessorBase,最終處理Http請求,根據Http請求的狀態作不一樣的操做,最終得到一個Processor對象 : processor = connections.get(socket); Processor對象負責把Http請求和響應封裝成Request和Response對象。

  Processor類中的process()方法根據不一樣的協議調用不一樣的service()方法。本文采起的是Http11Processor;

  Processer經過 getAdapter().service(request, response); 

  service()方法中 調用postParseRequest(req, request, res, response);  

  postParseRequest() 方法最終根據url請求得到對應的Mapper(對應的Host、Context和Wrapper) :connector.getService().getMapper().map(serverName, decodedURI,version, request.getMappingData());

  MappingData類:

 1 public class MappingData {
 2 
 3     public Host host = null;
 4     public Context context = null;
 5     public int contextSlashCount = 0;
 6     public Context[] contexts = null;
 7     public Wrapper wrapper = null;
 8     public boolean jspWildCard = false;
 9     .....
10 }

  postParseRequest()根據Http請求最終找到對應的Wrapper(Servlet) ,並封裝上下文信息(Host、Context);

  最後執行方法調用:

1  connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
 該代碼執行了一系列Valve方法調用。Container調用Host;Host調用Context;Context調用Wrapper;造成層級調用。
 最後執行到Wrapper的invoke()
 SandardWrapperValve --> invoke():
 1 public final void invoke(Request request, Response response)
 2         throws IOException, ServletException {
 3          //...........doSomething..........
 4         Servlet servlet = null;
 5         Context context = (Context) wrapper.getParent();
 6 
 7          //...........doSomething..........
 8 
 9         // Allocate a servlet instance to process this request
10         try {
11             if (!unavailable) {
12                 servlet = wrapper.allocate();
13             }
14         } catch (Exception e) {
15              //...........doSomething..........
16         }
17 
18         // Create the filter chain for this request
19         ApplicationFilterChain filterChain =
20                 ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
21 
22         // Call the filter chain for this request
23         // NOTE: This also calls the servlet's service() method
24         try {
25             if ((servlet != null) && (filterChain != null)) {
26                 // Swallow output if needed
27                 if (context.getSwallowOutput()) {
28                     try {
29                         SystemLogHandler.startCapture();
30                         if (request.isAsyncDispatching()) {
31                             request.getAsyncContextInternal().doInternalDispatch();
32                         } else {
33                             filterChain.doFilter(request.getRequest(),response.getResponse());
34                         }
35                     } finally {
36                         //...........doSomething..........
37                     }
38                 } else {
39                     //...........doSomething..........
40                 }
41 
42             }
43         } catch (Exception e) {
44             
45            //...........doSomething..........
46            
47         } finally {
48             //...........doSomething..........
49         }
50     }

  Wrapper中把Filler和Servlet封裝成ApplicationFilterChain(filterChain)對象。最終執行開發人員所熟悉的doFilter()方法。

 doFilter方法先執行Filter的doFilter()方法啊,就是開發人員所熟知的過濾器。filter.doFilter(request, response, this); 
 最終執行 servlet.service(request, response); HttpServlet中的service()實現對doGet、doPost、do...一系列的Servlet方法調用。
 至此,一次Servlet調用流程結束。
 請求調用流程:
 

Tomcat調優

  JVM參數

參數 參數做用 優化
-server 啓動Server,以服務端模式運行 生產環境建議開啓
-Xms  最小堆內存  
-Xmx 最大堆內存 建議設置爲可用內存的80%
-XX:MetaspaceSize 元空間(方法區)初始值       
-XX:MaxMetaSpaceSize 元空間(方法區)最大值
-XX:NewRatio 年輕代和老年代大小比值,默認2
-XX:SurvivoRadio

Eden區和Survivor區的大小比值,默認8

 

   JVM垃圾回收

    1.串行收集器(Serial Collector)   "-XX:UseSeriaGC"

    2.並行收集器(Parallel Collector)  "-XX:UseParallelGC" ,  "-XX:UseParNewGC"

    3.併發收集器(Concurrent Collector) 

    4.CMS收集器(Concurrent Mark Sweep Collector),標記清除 "-XX:UseConcMarjkSweepGC"

    5.G1收集器(Garbage-First Garbage Collector) "-XX:UseG1GC"

 

  Tomcat優化

    參數

      maxConnections : 最大連接數

      maxThreads:最大線程數

      acceptCount:最大排隊數

    禁用AJP連接:註釋server.xml中AJP配置

    調整IO模式。tomcat8以後默認NIO,也可升級使用APR

    動靜分離:Tomcat+Nginx

相關文章
相關標籤/搜索