Tomcat启动分析

tomcat在启动时的重点功能如下:

  • 初始化类加载器:主要初始化CommonLoader、CatalinaLoader以及SharedLoader;

  • 解析配置文件:使用Digester组件解析Tomcat的server.xml,初始化各个组件(包含各个web应用,解析对应的web.xml进行初始化);

  • 初始化连接器:初始化声明的Connector,以指定的协议打开端口,等待请求。

不管是通过命令行启动还是通过Eclipse的WST server UI,Tomcat的启动流程是在org.apache.catalina.startup. Bootstrap类的main方法中开始的,在启动时,这个类的核心代码如下所示:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public  static  void  main(String args[]) {
         if  (daemon ==  null ) {
             daemon =  new  Bootstrap(); //实例化该类的一个实例
             try  {
                 daemon.init(); //进行初始化
             catch  (Throwable t) {
                 ……;
             }
         }
         try  {
     …… //此处略去代码若干行
     if  (command.equals( "start" )) {
                 daemon.setAwait( true );
                 daemon.load(args); //执行load,生成组件实例并初始化
                 daemon.start(); //启动各个组件
             }
     …… //此处略去代码若干行
     }

从以上的代码中,可以看到在Tomcat启动的时候,执行了三个关键方法即init、load、和start。后面的两个方法都是通过反射调用org.apache.catalina.startup.Catalina的同名方法完成的,所以后面在介绍时将会直接转到Catalina的同名方法。首先分析一下Bootstrap的init方法,在该方法中将会初始化一些全局的系统属性、初始化类加载器、通过反射得到Catalina实例,在这里我们重点看一下初始化类加载器的方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private  void  initClassLoaders() {
         try  {
             commonLoader = createClassLoader( "common" null );
             if ( commonLoader ==  null  ) {
                 // no config file, default to this loader - we might be in a 'single' env.
                 commonLoader= this .getClass().getClassLoader();
             }
             catalinaLoader = createClassLoader( "server" , commonLoader);
             sharedLoader = createClassLoader( "shared" , commonLoader);
         catch  (Throwable t) {
             log.error( "Class loader creation threw exception" , t);
             System.exit( 1 );
         }
     }

在以上的代码总,我们可以看到初始化了三个类加载器,这三个类加载器将会有篇博文进行简单的介绍。

然后我们进入Catalina的load方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
public  void  load() {
//……
         //初始化Digester组件,定义了解析规则
         Digester digester = createStartDigester();
         //……中间略去代码若干,主要作用为将server.xml文件转换为输入流
         try  {
             inputSource.setByteStream(inputStream);
             digester.push( this );
//通过Digester解析这个文件,在此过程中会初始化各个组件实例及其依赖关系
             digester.parse(inputSource);
             inputStream.close();
         catch  (Exception e) {
           
         }
         // 调用Server的initialize方法,初始化各个组件
         if  (getServer()  instanceof  Lifecycle) {
             try  {
                 getServer().initialize();
             catch  (LifecycleException e) {
                 if  (Boolean.getBoolean( "org.apache.catalina.startup.EXIT_ON_INIT_FAILURE" ))
                     throw  new  java.lang.Error(e);
                 else   
                     log.error( "Catalina.start" , e);
                 
             }
         }
 
     }

在以上的代码中,关键的任务有两项即使用Digester组件按照给定的规则解析server.xml、调用Server的initialize方法。关于Digester组件的使用,后续会有一篇专门的博文进行讲解,而Server的initialize方法中,会发布事件并调用各个Service的initialize方法,从而级联完成各个组件的初始化。每个组件的初始化都是比较有意思的,但是我们限于篇幅先关注Connector的初始化,这可能是最值得关注的。

Connector的initialize方法,核心代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
public  void  initialize()  throws  LifecycleException{
      //该适配器会完成请求的真正处理   
adapter =  new  CoyoteAdapter( this );
     //对于不同的实现,会有不同的ProtocolHandler实现类,我们来看    //Http11Protocol,它用来处理HTTP请求
         protocolHandler.setAdapter(adapter);
         try  {
             protocolHandler.init();
         catch  (Exception e) {
             ……
         }
     }

在Http11Protocol的init方法中,核心代码如下:

?
1
2
3
4
5
6
7
8
9
public  void  init()  throws  Exception {
         endpoint.setName(getName()); //endpoint为JIoEndpoint的实现类
         endpoint.setHandler(cHandler);
         try  {
             endpoint.init(); //核心代码就是调用 JIoEndpoint的初始化方法
         catch  (Exception ex) {
            ……
         }
     }

我们看到最终的初始化方法最终都会调到JIoEndpoint的init方法,网络初始化和对请求的最初处理都是通过该类及其内部类完成的,所以后续的内容将会重点关注此类:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  void  init()  throws  Exception {
         if  (acceptorThreadCount ==  0 ) { //接受请求的线程数
             acceptorThreadCount =  1 ;
         }
         if  (serverSocket ==  null ) {
             try  {
                 if  (address ==  null ) {
     //基于特定端口创建一个ServerSocket对象,准备接受请求
                     serverSocket = serverSocketFactory.createSocket(port, backlog);
                 else  {
                     serverSocket = serverSocketFactory.createSocket(port, backlog, address);
                 }
             catch  (BindException orig) {
              ……
             }
         }
     }

在上面的代码中,我们可以看到此时初始化了一个ServerSocket对象,用来准备接受请求。

如果将其比作赛跑,此时已经到了“各就各位”状态,就等最终的那声“发令枪”了,而Catalina的start方法就是“发令枪”啦:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public  void  start() {
         if  (getServer() ==  null ) {
             load();
         }
         if  (getServer() ==  null ) {
             log.fatal( "Cannot start server. Server instance is not configured." );
             return ;
         }
         if  (getServer()  instanceof  Lifecycle) {
             try  {
                 ((Lifecycle) getServer()).start();
             catch  (LifecycleException e) {
                 log.error( "Catalina.start: " , e);
             }
         }
       //……
  }

此时会调用Server的start方法,这里我们重点还是关注JIoEndpoint的start方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public  void  start()   throws  Exception {
         if  (!initialized) {
             init();
         }
         if  (!running) {
             running =  true ;
             paused =  false ;
             if  (executor ==  null ) {
     //初始化处理连接的线程,maxThread的默认值为200,这也就是为什么    //说Tomcat只能同时处理200个请求的来历
                 workers =  new  WorkerStack(maxThreads);
             }
             for  ( int  i =  0 ; i < acceptorThreadCount; i++) {
     //初始化接受请求的线程
                 Thread acceptorThread =  new  Thread( new  Acceptor(), getName() +  "-Acceptor-"  + i);
                 acceptorThread.setPriority(threadPriority);
                 acceptorThread.setDaemon(daemon);
                 acceptorThread.start();
             }
         }
     }

从以上的代码,可以看到,如果没有在server.xml中声明Executor的话,将会使用内部的一个容量为200的线程池用来后续的请求处理。并且按照参数acceptorThreadCount的设置,初始化线程来接受请求。而Acceptor是真正的幕后英雄,接受请求并分派给处理过程:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected  class  Acceptor  implements  Runnable {
         public  void  run() {
             while  (running) {
                 // 接受发送过来的请求
     Socket socket = serverSocketFactory.acceptSocket(serverSocket);
                     serverSocketFactory.initSocket(socket);
                     //处理这个请求
                     if  (!processSocket(socket)) {
                         //关闭连接
                         try  {
                             socket.close();
                         catch  (IOException e) {
                             // Ignore
                         }
                     }
             }
         }
     }

从这里我们可以看到,Acceptor接受Socket请求,并调用processSocket方法来进行请求的处理。至此,Tomcat的组件整装待命,等待请求的到来。关于请求的处理,会在下篇文章中介绍。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值