Tomcat笔记

Tomcat组件和流程

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

  组件结构

  

                                  图1.1

  Tomcat核心的组件即为图中左侧的Connector和Container

  1 Connector

   EndPoint负责实现并处理TCP/IP协议

   Processor负责实现并处理Http协议,封装Request、Response对象;

   ProtocolHandler:协议接口,tomcat中有6个实现类,用于实现具体的协议处理能力,包括:AjpNioProtocol、AjpApr、AjpNio、Http11Nio、Http11Nio2、Http11Apr。

   Adapter:适配器,CoyoteAdapter,负责把Request转换成ServletRequest对象。Response反之。

  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

    

 

posted @ 2020-10-24 21:04  Justhing  阅读(169)  评论(0编辑  收藏  举报