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