[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 &quot;%r&quot; %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, nullfalse);
11            // 模板方法,实现下方到具体的容器中实现
12            initInternal();
13            // 调用LifecycleBase内部的方法,主要将初始化完了变更事件发送给订阅者
14            setStateInternal(LifecycleState.INITIALIZED, nullfalse);
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, nullfalse);
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, nullfalse);
38            }
39        } catch (Throwable t) {
40
41        }
42    }

StandardServer

相应的类图如下:

[tomcat]源码简析 结构和一次请求

 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

类图如下:

[tomcat]源码简析 结构和一次请求

 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)

类图如下:

[tomcat]源码简析 结构和一次请求

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()来执行。

大致的流程图大概是这样的

[tomcat]源码简析 结构和一次请求

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]源码简析 结构和一次请求的主要内容,如果未能解决你的问题,请参考以下文章

[tomcat]源码简析 异步/非阻塞和请求构成

Tomcat启动流程简析

[tomcat] tomcat简析

第五节:JQuery框架源码简析

第五节:JQuery框架源码简析

Flask 启动时的源码简析