[tomcat]源码简析 结构和一次请求
Posted 还是那个徐东强
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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}
清单代码点评如下:
在NioEndpoint中bind方法中监听Connector中定义的端口(采用NIO的方式)。
创建一个Acceptor线程,专门接收客户端连接请求,获取到一个SocketChannel,则从创建的Pollers中轮询一个,将channel交给Poller的events队列。
创建两个Poller线程,每个Poller中都一个多用复用器Selector,poller负责从自身的events队列中获取到PollerEvent事件,将channel注册到多用复用器selector上,轮询多用复用器selector上注册的通道,一旦某个通道准备就绪,则交给SocketProcessor来处理,最终定位到Http11Processor的service来处理,service中负责将通道中准备就绪的数据转换成request&response,然后交给adapter.service()来执行。
大致的流程图大概是这样的
CoyoteAdapter
1 @Override
2 public void service(org.apache.coyote.Request req, org.apache.coyote.Response res)
3 throws Exception {
4
5 Request request = (Request) req.getNote(ADAPTER_NOTES);
6 Response response = (Response) res.getNote(ADAPTER_NOTES);
7
8 postParseSuccess = postParseRequest(req, request, res, response);
9 if (postParseSuccess) {
10 //check valves if we support async
11 request.setAsyncSupported(
12 connector.getService().getContainer().getPipeline().isAsyncSupported());
13 // ⭐️ 这里将请求交给容器engin去处理
14 connector.getService().getContainer().getPipeline().getFirst().invoke(
15 request, response);
16 }
17 }
清单代码点评如下:
将接受到的请求最终交给容器的pipeline来去执行。
容器engine是哪个,在最初读conf/server.xml中,就可以知道一个service中多个Connector和container的对应关系,container中容器是一层套一层的,最终交给servlet来执行。容器的嵌套如下:
StandardEngine
类图如下:
1 public StandardEngine() {
2
3 super();
4 pipeline.setBasic(new StandardEngineValve());
5 // 略去部分代码
6 }
7
8// StandardEngineValve
9@Override
10 public final void invoke(Request request, Response response)
11 throws IOException, ServletException {
12
13 // Select the Host to be used for this Request
14 Host host = request.getHost();
26 // Ask this Host to process this request
27 host.getPipeline().getFirst().invoke(request, response);
28
29 }
清单代码点评如下:
StandardPipeline中可以通过addValue和setBasic来设置责任链,firstValue->secondValue->ThirdValue->…->baisic。
当value.invoke的场合,会通过getNext.invoke依次执行责任链。
basic是StandardEngineValve,会执行StandardHost。后续容器一层套一层的
比如StandardEngine->StandardHost->StandContext->StandardWrapper(ContextConfig中通过读取web.xml给每一个servlet创建一一对应的wrapper)。
StandardWrapper
invoke执行StandardWrapperValve
1 @Override
2 public final void invoke(Request request, Response response)
3 throws IOException, ServletException {
4
5 // 略去部分代码
6 // Allocate a servlet instance to process this request
7 try {
8 if (!unavailable) {
9 // 每个Wrapper里面都有一一对应的serlvet信息
10 // 根据用户创建的servlet是否是SingleThreadedModel,是实例化servlet,还是实例化20个每次从栈中pop一个
11 servlet = wrapper.allocate();
12 }
13 } catch (UnavailableException e) {
14
15 } catch (ServletException e) {
16
17 }
18
19 // 创建过滤过滤器链条
20 ApplicationFilterChain filterChain =
21 ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
22
23 // 执行过滤器,执行servlet, 最终执行service方法,最终是执行doGet or doPost
24 filterChain.doFilter(request.getRequest(),
25 response.getResponse());
26
27 }
代码清单点评如下
最终执行到Standardwrapper,获取一个servlet实例,创建过滤器chain,过滤器doFilter后,执行servlet.service方法,最终用户重写的doGet或者doPost方法,响应返回。
以上。
以上是关于[tomcat]源码简析 结构和一次请求的主要内容,如果未能解决你的问题,请参考以下文章