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