[tomcat]源码简析 结构和一次请求
通过源码解析来理清tomcat结构和一次请求运行流程。
tomcat的启动脚本可以看出启动代码类如下:
1org.apache.catalina.startup.Bootstrap.main
Bootstrap
main入口代码如下:
1public static void main(String args[]) { 2 3 if (daemon == null) { 4 Bootstrap bootstrap = new Bootstrap(); 5 try { 6 // bootstrap的初始化做了两件事情: 7 // 1.自定义类加载器. 2.实例化org.apache.catalina.startup.Catalina 8 bootstrap.init(); 9 } catch (Throwable t) { 10 handleThrowable(t); 11 t.printStackTrace(); 12 return; 13 } 14 daemon = bootstrap; 15 } else { 16 } 17 18 try { 19 String command = "start"; 20 //略部分代码,只看启动命令,tomcat的运行流程 21 } else if (command.equals("start")) { 22 daemon.setAwait(true); 23 // 执行bootstrap的load方法和start方法 24 daemon.load(args); 25 daemon.start(); 26 if (null == daemon.getServer()) { 27 System.exit(1); 28 } 29 } 30 }
代码清单点评:
上面的代码做了三件事情
1.bootstrap的init方法,做了两件事情。
初始化加载器,这里涉及到一个问题,我们知道jdk中类加载类使用的是“双亲委托模式”,就是加载一个类的时候,首先交给其父类加载器去加载,父类加载器找不到,才用自己的去加载,这解决了一个什么问题呢。(这是从安全因素去考虑,假如不使用这种委托模式,那么我们自定义java.lang.String就有可能代替掉jdk中的基本类型,但是如果一旦使用了双亲委托模式,会先用父类加载器去加载,发现已经加载过了,那么自定义的String是无法加载成功的);bootstrap的init中创建了三个自定义的类加载器,这又是为什么,为什么说tomcat是不遵守“双亲委托模式”?
实例化了
org.apache.catalina.startup.Catalina,并且指定上面的一个自定义的类加载器作为其父类加载器
2.bootstrap的load方法 (执行Catalina的load
)
3.bootstrap的start方法 (执行Catalina的start
)
Catalina
load代码如下:
1 public void load() { 2 3 if (loaded) { 4 return; 5 } 6 loaded = true; 7 8 // Create and execute our Digester 9 Digester digester = createStartDigester(); 10 InputSource inputSource = null; 11 InputStream inputStream = null; 12 File file = null; 13 try { 14 try { 15 // 读取tomcat的下载包中的conf/server.xml 16 file = configFile(); 17 // 略掉了部分代码 18 inputSource.setByteStream(inputStream); 19 digester.push(this); 20 // 通过发digester来解析 conf/server.xml 配置文件 21 digester.parse(inputSource); 22 } catch (SAXParseException spe) { 23 // 略掉部分代码 24 getServer().setCatalina(this); 25 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); 26 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); 27 28 getServer().init(); 29 } 30 31// 这里解释了server.xml的不同的标签对应的标准类 32 protected Digester createStartDigester() { 33 long t1=System.currentTimeMillis(); 34 // Initialize the digester 35 Digester digester = new Digester(); 36 // 这里主要是创建了一些核心组件 37 38 digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className"); 59 60 // When the 'engine' is found, set the parentClassLoader. 61 digester.addRule("Server/Service/Engine", 62 new SetParentClassLoaderRule(parentClassLoader)); 63 addClusterRuleSet(digester, "Server/Service/Engine/Cluster/"); 64 65 long t2=System.currentTimeMillis(); 66 if (log.isDebugEnabled()) { 67 log.debug("Digester for server.xml created " + ( t2-t1 )); 68 } 69 return (digester); 70 71 }
conf/server.xml如下:
1<Server port="8005" shutdown="SHUTDOWN"> 2 <Service name="Catalina"> 3 <Connector port="8080" protocol="HTTP/1.1" 4 connectionTimeout="20000" 5 redirectPort="8443" /> 6 <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> 7 8 <Engine name="Catalina" defaultHost="localhost"> 9 10 <Realm className="org.apache.catalina.realm.LockOutRealm"> 11 <Realm className="org.apache.catalina.realm.UserDatabaseRealm" 12 resourceName="UserDatabase"/> 13 </Realm> 14 <Host name="localhost" appBase="webapps" 15 unpackWARs="true" autoDeploy="true"> 16 <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" 17 prefix="localhost_access_log" suffix=".txt" 18 pattern="%h %l %u %t "%r" %s %b" /> 19 20 </Host> 21 </Engine> 22 </Service> 23</Server>
代码清单点评如下:
-
load方法中:通过Digester来解析conf/server.xml配置文件,创建tomcat的核心组件。并且执行StandardServer的
init
方法 -
start方法中: 执行StandardServer的
start
方法
创建的标准容器如下:
StandardServer
---|StandardService
--->---|Connector
--->---|StandardEngine
--->--->---|StandardHost
--->--->--->---|StandardContext
tomcat的结构图如下:
-
Server: 一个tomcat只有一个Server
-
Service: Server中的一个逻辑功能层,一个Server可以包含多个Service; 一个Service中有可以多个Connector和一个Container
-
Connector: Service的核心组件之一,负责接收请求
-
Container: Service的另一核心组件之一,负责处理处理请求,按照层级有- Engine, Host, Context, Wrapper
LifecycleBase
后续所有标准容器均继承生命周期父类,用于在容器的init和start操作前后,将状态变更事件发送给订阅者。容器具体的init和start方法下放到具体的容器中实现。
1LifecycleBase 2@Override 3 public final synchronized void init() throws LifecycleException { 4 if (!state.equals(LifecycleState.NEW)) { 5 invalidTransition(Lifecycle.BEFORE_INIT_EVENT); 6 } 7 8 try { 9 // 调用LifecycleBase内部的方法,主要是初始化中变更事件发送给订阅者 10 setStateInternal(LifecycleState.INITIALIZING, null, false); 11 // 模板方法,实现下方到具体的容器中实现 12 initInternal(); 13 // 调用LifecycleBase内部的方法,主要将初始化完了变更事件发送给订阅者 14 setStateInternal(LifecycleState.INITIALIZED, null, false); 15 } catch (Throwable t) { 16 20 } 21 } 22 23@Override 24 public final synchronized void start() throws LifecycleException { 25 // 略部分代码 26 try { 27 // 将开始前变更事件发送给订阅者 28 setStateInternal(LifecycleState.STARTING_PREP, null, false); 29 // 模板方法,实现下发到具体的容器中 30 startInternal(); 31 if (state.equals(LifecycleState.FAILED)) { 32 stop(); 33 } else if (!state.equals(LifecycleState.STARTING)) { 34 invalidTransition(Lifecycle.AFTER_START_EVENT); 35 } else { 36 // 将已经开始了变更事件发送给订阅者 37 setStateInternal(LifecycleState.STARTED, null, false); 38 } 39 } catch (Throwable t) { 40 41 } 42 }
StandardServer
相应的类图如下:
1 @Override 2 protected void initInternal() throws LifecycleException { 3 4 super.initInternal(); 5 // 略去部分代码 6 // init Server的下层容器 service 7 for (int i = 0; i < services.length; i++) { 8 services[i].init(); 9 } 10 } 11protected void startInternal() throws LifecycleException { 12 13 fireLifecycleEvent(CONFIGURE_START_EVENT, null); 14 setState(LifecycleState.STARTING); 15 16 globalNamingResources.start(); 17 // start Server的下层容器 service,这里也可以看出来,一个server容器中可能有多个services 18 synchronized (servicesLock) { 19 for (int i = 0; i < services.length; i++) { 20 services[i].start(); 21 } 22 } 23 }
代码清单点评:
server的init方法中: 执行service的init
方法
server的start方法中: 执行service的start
方法
StandardService
类图如下:
1 @Override 2 protected void initInternal() throws LifecycleException { 3 4 super.initInternal(); 5 6 // conf/server.xml中定义的StandardEngin 7 if (engine != null) { 8 engine.init(); 9 } 21 22 // Initialize our defined Connectors 23 // conf/server.xml中定义的多个Connector 24 synchronized (connectorsLock) { 25 for (Connector connector : connectors) { 26 try { 27 connector.init(); 28 } catch (Exception e) { 29 } 30 } 31 } 32 } 33 @Override 34 protected void startInternal() throws LifecycleException { 35 36 setState(LifecycleState.STARTING); 37 38 // 启动 StandardEngine 39 if (engine != null) { 40 synchronized (engine) { 41 engine.start(); 42 } 43 } 53 // 启动conf/server.xml中的定义的connector 54 synchronized (connectorsLock) { 55 for (Connector connector: connectors) { 56 try { 57 if (connector.getState() != LifecycleState.FAILED) { 58 connector.start(); 59 } 60 } catch (Exception e) { 61 } 62 } 63 } 64 }
清单代码点评如下:
-
init方法中: 执行tomcat的核心组件connector和engine的
init
方法 -
start方法: 执行tomcat的核心组件connector和engine的
start
方法
Connector
1public Connector(String protocol) { 2 setProtocol(protocol); 3 // Instantiate protocol handler 4 ProtocolHandler p = null; 5 try { 6 // Connector 根据配置文件中指定的协议加载对应的类 比如 org.apache.coyote.http11.Http11NioProtocol 7 Class<?> clazz = Class.forName(protocolHandlerClassName); 8 p = (ProtocolHandler) clazz.getConstructor().newInstance(); 9 } catch (Exception e) { 10 } finally { 11 this.protocolHandler = p; 12 } 13} 35 36 @Override 37 protected void initInternal() throws LifecycleException { 38 super.initInternal(); 39 // Initialize adapter 40 adapter = new CoyoteAdapter(this); 41 protocolHandler.setAdapter(adapter); 42 43 // 略去部分代码 44 try { 45 protocolHandler.init(); 46 } catch (Exception e) { 47 } 48 } 49@Override 50 protected void startInternal() throws LifecycleException { 51 52 // Validate settings before starting 53 if (getPort() < 0) { 54 throw new LifecycleException(sm.getString( 55 "coyoteConnector.invalidPort", Integer.valueOf(getPort()))); 56 } 57 58 setState(LifecycleState.STARTING); 59 60 try { 61 protocolHandler.start(); 62 } catch (Exception e) { 63 } 64 }
清单代码点评如下:
根据conf/server.xml中的Connector节点的配置的协议,设定对应的协议处理器,比如默认就是Http11NioProtocol。
initInternal方法中: 设置CoyoteAdapter
为协议处理器的适配器,并且执行协议处理器ProtocolHandler的init
方法。
startInternal方法中: 执行协议处理器ProtocolHandler的start
方法。
ProtocolHandler (NioEndpoint)
类图如下:
Http11NioProtocol中默认指定了endpoint为NioEndpoint
。
1//AbstractProtocol 2 @Override 3 public void init() throws Exception { 4 // 略部分代码 5 // 最终执行 EndPoint的init方法 6 // NioEndpoint 内部是啥,什么时候构造的 7 endpoint.init(); 8 } 9 10 @Override 11 public void start() throws Exception { 12 // 略部分代码 13 // 执行 EndPoint的start方法 14 endpoint.start(); 15 } 16//AbstractEndpoint 17public void init() throws Exception { 18 if (bindOnInit) { 19 bind(); 20 bindState = BindState.BOUND_ON_INIT; 21 } 22 // 略去部分代码 23 } 24public final void start() throws Exception { 25 if (bindState == BindState.UNBOUND) { 26 bind(); 27 bindState = BindState.BOUND_ON_START; 28 } 29 startInternal(); 30 }
清单代码点评如下:
Http11NioProtocol的init和start方法
--> AbstractProtocol的init和start方法
--> NioEndpoint的init和start方法
--> AbstractEndpoint的init和start方法
--> 执行NioEndpoint的bind和startInternal方法
终于进入主题了,开始NIO网络监听端口
NioEndpoint
1@Override 2 public void bind() throws Exception { 3 4 if (!getUseInheritedChannel()) { 5 // nio的网络监听方式 6 serverSock = ServerSocketChannel.open(); 7 socketProperties.setProperties(serverSock.socket()); 8 InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort())); 9 serverSock.socket().bind(addr,getAcceptCount()); 10 } else { 11 // 略去部分代码 12 } 13 serverSock.configureBlocking(true); //mimic APR behavior 14 }
清单代码点评如下:
在jdk1.4之前,网络连接使用的均是ServerSocket(阻塞模式),来一个请求,创建一个线程去处理,改进只能是引入了线程池。
jdk1.4有了ServerSocketChannel(非阻塞),同时引入了通道和缓冲区的概念。
阻塞:应用程序在获取网络数据的时候,如果网络太慢,则应用程序一直阻塞。
非阻塞:将网络数据发送到缓冲区,等完全发好了以后,通知应用程序数据已经全部放到缓冲区了,应用程序再来拿(在此期间可以干其他事情)。并且引进了多路复用器Selector,不管是ServerSocketChannel还是SocketChannel均注册到多路复用器Selector,Selector不断轮询注册到它上面的通道,哪个通道就绪了,则处理。这样一个Selector可以处理成千上万个channel通道。
1.7有了AsynchronousServerSocketChannel,aio的监听方式,对应的代码可以在Nio2Endpoint中。
1 @Override 2 public void startInternal() throws Exception { 3 4 if (!running) { 5 running = true; 6 paused = false; 7 8 // 略去部分代码 9 // 创建两个Poller 并且启动Poller线程 10 pollers = new Poller[getPollerThreadCount()]; 11 for (int i=0; i<pollers.length; i++) { 12 pollers[i] = new Poller(); 13 Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-"+i); 14 pollerThread.setPriority(threadPriority); 15 pollerThread.setDaemon(true); 16 pollerThread.start(); 17 } 18 // 默认创建一个Acceptor 19 // Acceptor的run中根据ServerSocketChannel.open获取到一个客户端过来的通道SocketChannel 20 // 将SocketChannel封装成一个PollerEvent事件,然后轮询上面创建出来的一个Poller,扔进Poller的event事件队列中 21 startAcceptorThreads(); 22 } 23 } 24 25// 默认创建1个Acceptor,用监听ServerSocketChannel.open 26protected final void startAcceptorThreads() { 27 int count = getAcceptorThreadCount(); 28 acceptors = new Acceptor[count]; 29 30 for (int i = 0; i < count; i++) { 31 acceptors[i] = createAcceptor(); 32 String threadName = getName() + "-Acceptor-" + i; 33 acceptors[i].setThreadName(threadName); 34 Thread t = new Thread(acceptors[i], threadName); 35 t.setPriority(getAcceptorThreadPriority()); 36 t.setDaemon(getDaemon()); 37 t.start(); 38 } 39 } 40 41// Acceptor线程 42protected class Acceptor extends AbstractEndpoint.Acceptor { 43 @Override 44 public void run() { 45 SocketChannel socket = null; 46 try { 47 // 监听来自客户端的请求 48 socket = serverSock.accept(); 49 } catch (IOException ioe) { 50 // 略去部分代码 51 } 52 53 // Configure the socket 54 if (running && !paused) { 55 // 这里将SocketChannel封装成NioChannel封装陈PollerEvent,然后轮询一个Poller,将PollerEvent扔到Poller的events队列中 56 if (!setSocketOptions(socket)) { 57 closeSocket(socket); 58 } 59 } else { 60 closeSocket(socket); 61 } 62 } 63} 64// Poller线程 65public class Poller implements Runnable { 66 // 打开多用复用器 67 public Poller() throws IOException { 68 this.selector = Selector.open(); 69 } 70 @Override 71 public void run() { 72 73 while (true) { 74 75 boolean hasEvents = false; 76 77 try { 78 if (!close) { 79 // events方法中从Poller的events中poll出一个PollerEvent事件,将其注册到多路复用器上 80 hasEvents = events(); 81 if (wakeupCounter.getAndSet(-1) > 0) { 82 keyCount = selector.selectNow(); 83 } else { 84 keyCount = selector.select(selectorTimeout); 85 } 86 wakeupCounter.set(0); 87 } 88 // 略去部分代码 89 } catch (Throwable x) { 90 91 } 95 Iterator<SelectionKey> iterator = 96 keyCount > 0 ? selector.selectedKeys().iterator() : null; 97 // 轮询多用复用器Selector上注册的通道,准备就绪(可读Or可写),则处理 98 while (iterator != null && iterator.hasNext()) { 99 SelectionKey sk = iterator.next(); 100 NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment(); 101 // Attachment may be null if another thread has called 102 // cancelledKey() 103 if (attachment == null) { 104 iterator.remove(); 105 } else { 106 iterator.remove(); 107 processKey(sk, attachment); 108 } 109 }//while 110 111 //process timeouts 112 timeout(keyCount,hasEvents); 113 }//while 114 115 getStopLatch().countDown(); 116 } 117} 118public static class PollerEvent implements Runnable { 119 @Override 120 public void run() { 121 if (interestOps == OP_REGISTER) { 122 try { 123 // 将PollerEvent中封装的SocketChannel注册到多路复用器Selector上 124 socket.getIOChannel().register( 125 socket.getPoller().getSelector(), SelectionKey.OP_READ, socketWrapper); 126 } catch (Exception x) { 127 log.error(sm.getString("endpoint.nio.registerFail"), x); 128 } 129 } 130 // 略去了部分代码 131 } 132}