设为首页 收藏本站
查看: 538|回复: 0

[经验分享] Tomcat 5.5.26源代码分析——启动过程(一)

[复制链接]

尚未签到

发表于 2017-2-1 10:13:10 | 显示全部楼层 |阅读模式


  • main方法


    • 第一阶段

    • 第二阶段

    • 第三阶段



  • 启动参数的区别

  • 一个Bug

  • configtest启动参数
  一般,我们直接运行startup.sh

启动Tomcat
。最终执行的命令是:


java [options] org.apache.catalina.startup.Bootstrap start
  options是JVM启动参数,这里忽略。

main方法
  
可见,Tomcat
的启动类是org.apache.catalina.startup.Bootstrap

启动参数是start
。我们从该类的main
方法看起。

public static void main(String args[]) {
try {
// Attempt to load JMX class
new ObjectName("test:foo=bar");
} catch (Throwable t) {
System.out.println(JMX_ERROR_MESSAGE);
try {
// Give users some time to read the message before exiting
Thread.sleep(5000);
} catch (Exception ex) {
}
return;
}
if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[0] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[0] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}
}
  

上述代码主要分为三段:


  • 第一段是检查JMX
    支持
  • 第二段创建Boostrap

    例并初始化
  • 第三段根据启动参数,执行进一步操作

第一阶段
  乍一看,第一段似乎可有可无。它只是创
建了一个不被使用的ObjectName
对象,如果创建失败,就打印一条错误信息。而
且在Tomcat 6
中,已经移除了这段代码。

    try {
// Attempt to load JMX class
new ObjectName("test:foo=bar");
} catch (Throwable t) {
System.out.println(JMX_ERROR_MESSAGE);
try {
// Give users some time to read the message before exiting
Thread.sleep(5000);
} catch (Exception ex) {
}
return;
}
  

这段代码的作用是什么呢?这其实和J2SE
1.4

的兼容性有关。Tomcat 5
使用JMX
作为管理和监控机制,但是J2SE
1.4

本身并不支持JMX
,像ObjectName
这些JMX

并不包括J2SE 1.4
API
中。因此,Tomcat 5
不能直接运行
J2SE 1.4
及之前版本上。


但是Tomcat 5
的设计目标是支持运行在J2SE 1.4
上的。因此,要达到这个目标,Tomcat 5

JMX
类作为兼容包(compatibility
package

)额外添加到Tomcat
启动类路径中。


们有两种方法获得兼容包:


  • Tomcat
    的官方下载
    页面下载兼容包,参见http://tomcat.apache.org/download-55.cgi
  • Tomcat
    的源代码构建兼容包,参见http://jarfield.iteye.com/blog/604198
  


管怎样,Tomcat 5
不能保证用户安装了兼容包,因此在启动时,它首先检
查能够加载ObjectName
类,以此判断兼容包是否安装。如果没有安装,则向标准
输出打印一条错误信息:
This release of Apache Tomcat
was packaged to run on J2SE 5.0 or later. It can be run on earlier JVMs
by downloading and installing a compatibility package from the Apache
Tomcat binary download page.





条信息也就是第一段代码中JMX_ERROR_MESSAGE
变量的值。下面是Bootstrap
类声明该变量的代码:

private static final String JMX_ERROR_MESSAGE =
"This release of Apache Tomcat was packaged to run on J2SE 5.0 \n"
+ "or later. It can be run on earlier JVMs by downloading and \n"
+ "installing a compatibility package from the Apache Tomcat \n"
+ "binary download page.";
  

那为什么Tomcat
6中移除了第一段代码,不检查兼容包是否安装了呢?原因很简单,Tomcat 6的设计目标并不包括J2SE 1.4及之前版本。


OK
,看完了第一段,我们进入正题,看看第二段代码。

第二阶段

    if (daemon == null) {
daemon = new Bootstrap();
try {
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
  

可见,这段代码的主要逻辑在Bootstrap
init
方法。该方法的工作包括:



  • Catalina
    Tomcat Servlet
    容器的代号)的路径:CATALINA_HOME
    CATALINA_BASE
  • 初始化Tomcat
    的类加载器体系
  • 创建org.apache.catalina.startup.Catalina

    象(启动阶段剩余的工作由Catalina类
    完成)
  

为了
节省篇幅,init
方法的代码另文表述
。下面我们看看第三段代码。

第三阶段

try {
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[0] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[0] = "stop";
daemon.stop();
} else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}
  


易看出,这段代码的主要逻辑就是根据不同的启动参数,执行不同的工作。可以接受的启动参数包括4
种:startd stopd start stop



启动参数决定了启动还是停止Tomcat
。如何启动,如何停止,另文表述
。本文问想讨论的问题是:除了start
stop
,为什么
还有startd
stopd

它们有什么区别呢?

启动参数的区别
  我们先看看start
startd
。处理start

代码比startd
仅多了一行:

daemon.setAwait(true);
  这行代码执行后,主线程(即main
函数所在的线程)在Tomcat

动过程结束时并不会退出,而是监听SHUTDOWN
端口(默认端口是8005
)。该端口如果接收SHUTDOWN

令,就停止Tomcat
;如果收到的是其他命令,则忽略,继续监听。这样,我
们就可以在Tomcat
进程之外通过网络停止Tomcat



如果以startd

数启动Tomcat
,主线程会在启动结束时退出,只剩下主线程创建的其他线程
HTTP
监听线程、HTTP
请求线程、Tomcat
后台线程等)。
这样,我们并不能通过网络停止Tomcat



如果启动参数是stop
,那么将调用Bootstrap类
的stopServer

方法。该方法通过SHUTDOWN
端口向Tomcat
发送SHUTDOWN

令,从而停止Tomcat
shutdown.bat
就是通过这种方式关闭Tomcat

它最终执行的命令是:
java [options]
org.apache.catalina.startup.Bootstrap stop


如果启动参数是stopd,那么
Bootstrap将直接调用Catalina的stop方法,直接停止Tomcat。



综上所述,以start
启动,就以stop

止;以startd
启动,就以stopd

止。


如果将Tomcat
作为独立的进程运行,那么应该使用start
stop
,这样
我们就可以通过网络停止Tomcat



如果将Tomcat
以嵌入到应用进程的方式运行(例如Eclipse
中运行Tomcat
),
那么应该使用startd
stopd

这样,宿主程序通过普通的方法调用,来启动和停止Tomcat


一个Bug
  关于这4
个启动参数的讨论,可以参见Bug
47881
。这个Bug
的本意是要指出第三段代码的一个问题
,不过恰好讨论了各种启动参数的区别。


至于这个Bug
,也比较明显:



  • args[0] = "start";
    应该是 args[args.length - 1
    ] = "start";


  • args[0] = "stop";
    应该是 args[args.length - 1
    ] = "stop";


  这
Bug
已经在Tomcat
6

中解决了。

Tomcat 7中引入的configtest启动参数
  在Tomcat 7中,新增了configtest启动参数。

public static void main(String args[]) {
// 省略部分代码
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
// 省略部分代码
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
// 省略部分代码
}
  顾名思义,configtest是为了检测配置参数是否正确,配置参数的主要来源就是conf/server.xml。
  从代码可以看出,configtest启动参数就是把load方法给执行了一遍,然后无条件退出。load方法的代码,另文表述
。这里只要知道,load的主要工作之一就是解析conf/server.xml,从而起到检测配置参数是否正确的作用。
  configtest通过exit status来返回检测结果。1表示检测到错误,0表示检测结果正确。如果load方法执行成功,就会创建server实例,通过daemon.getServer方法返回;反之,如果配置参数不正确,load方法执行失败,那么就不创建server实例,deamon.getServer方法就返回null。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-336023-1-1.html 上篇帖子: 译How Tomcat Works(第三章) 下篇帖子: OpenEjb使用笔记--让Tomcat可以部署EJB
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表