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

[经验分享] tomcat 启动流程

[复制链接]

尚未签到

发表于 2017-1-20 06:27:33 | 显示全部楼层 |阅读模式
刚开始的时候,只是想导入源码直接看,但是水平不够,有些流程不知道怎么走了, DSC0000.gif , 于是搭建了调试的环境,边调试边学习。 搭建调试的环境可以参考下面的链接:  http://blog.csdn.net/teabook00/article/details/5620023
  下面先粗略的过下整个流程。
  1, tomcat的启动是从Bootstrap开始, 下面是main()的主要代码

  public static void main(String args[]) {
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[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "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();
}
}
  通过反射的方式,分别调用Catalina的load()和start()的方法。
  2, load方法主要完成组件的装配以及组件的初始化。 先看下面的load方法,

//因为需要调用多个方法,这里将load方法的主要代码合并到了一起
public void load() {
Digester digester = new Digester();
// Configure the actions we will be using
digester.addObjectCreate("Server",
"org.apache.catalina.core.StandardServer",
"className");
digester.addSetProperties("Server");
digester.addSetNext("Server",
"setServer",
"org.apache.catalina.Server");
digester.addObjectCreate("Server/Service",
"org.apache.catalina.core.StandardService",
"className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service",
"addService",
"org.apache.catalina.Service");
digester.addRule("Server/Service/Connector",
new ConnectorCreateRule());
digester.addRule("Server/Service/Connector",
new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector",
"addConnector",
"org.apache.catalina.connector.Connector");
// Add RuleSets for nested elements
digester.addRuleSet(new EngineRuleSet("Server/Service/"));
digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
// When the 'engine' is found, set the parentClassLoader.
digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader));
digester.addRuleSet(ClusterRuleSetFactory.getClusterRuleSet("Server/Service/Engine/Cluster/"));
try {
file = configFile(); //读取conf/server.xml
inputStream = new FileInputStream(file);
inputSource = new InputSource("file://" + file.getAbsolutePath());
} catch (Exception e) {
}
try {
inputSource.setByteStream(inputStream);
digester.parse(inputSource);
} catch (Exception e) {
return;
}
getServer().initialize();
}
  tomcat使用SAX来解析conf/server.xml, 以Service为例,来了解tomcat如何完成组件的组装。
  (1) 下面是server.xml的配置文件

<?xml version='1.0' encoding='utf-8'?>
<Server port="8005" shutdown="SHUTDOWN">
<Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
<Listener className="org.apache.catalina.core.JasperListener" />
<Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
<Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
<Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
<GlobalNamingResources>
<Resource name="UserDatabase" auth="Container"
type="org.apache.catalina.UserDatabase"
description="User database that can be updated and saved"
factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
pathname="conf/tomcat-users.xml" />
</GlobalNamingResources>
<Service name="Catalina">
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
<Engine name="Catalina" defaultHost="localhost">
<Realm className="org.apache.catalina.realm.UserDatabaseRealm"
resourceName="UserDatabase"/>
<Host name="localhost"  appBase="webapps"
unpackWARs="true" autoDeploy="true"
xmlValidation="false" xmlNamespaceAware="false">
</Host>
</Engine>
</Service>
</Server>

  (2) Digester 继承 DefaultHandler,作为server.xml解析的handler,下面是startElment 和 endElement的主要代码

public void startElement(String namespaceURI, String localName,
String qName, Attributes list)
throws SAXException {
// Fire "begin" events for all relevant rules
List rules = getRules().match(namespaceURI, match);
matches.push(rules);
if ((rules != null) && (rules.size() > 0)) {
for (int i = 0; i < rules.size(); i++) {
Rule rule = (Rule) rules.get(i);
rule.begin(namespaceURI, name, list);
}
}
}
public void endElement(String namespaceURI, String localName,
String qName) throws SAXException {
// Fire "body" events for all relevant rules
List rules = (List) matches.pop();
if ((rules != null) && (rules.size() > 0)) {
String bodyText = this.bodyText.toString();
for (int i = 0; i < rules.size(); i++) {
Rule rule = (Rule) rules.get(i);
rule.body(namespaceURI, name, bodyText);
}
}
// Fire "end" events for all relevant rules in reverse order
if (rules != null) {
for (int i = 0; i < rules.size(); i++) {
int j = (rules.size() - i) - 1;
Rule rule = (Rule) rules.get(j);
rule.end(namespaceURI, name);
}
}
}
  (2) 调用addObjectCreate的方法,创建了ObjectCreateRule,下面是ObjectCreateRule的方法,begin方法主要根据className来创建一个实例,即创建了StandardService

public void begin(Attributes attributes) throws Exception {
// Identify the name of the class to instantiate
String realClassName = className;
if (attributeName != null) {
String value = attributes.getValue(attributeName);
if (value != null) {
realClassName = value;
}
}
// Instantiate the new object and push it on the context stack
Class clazz = digester.getClassLoader().loadClass(realClassName);
Object instance = clazz.newInstance();
digester.push(instance);
}
public void end() throws Exception {
Object top = digester.pop();
}
  (3)调用addSetProperties方法,创建SetPropertiesRule,下面是SetPropertiesRule的begin方法,主要用来初始化参数(根据server.xml中的配置)

public void begin(Attributes attributes) throws Exception {
// Populate the corresponding properties of the top object
Object top = digester.peek();
for (int i = 0; i < attributes.getLength(); i++) {
String name = attributes.getLocalName(i);
if ("".equals(name)) {
name = attributes.getQName(i);
}
String value = attributes.getValue(i);
IntrospectionUtils.setProperty(top, name, value)
}
}
  (4) 调用setNextRule方法,创建SetNextRule,下面是SetNextRule的end方法

public void end() throws Exception {
// Identify the objects to be used
Object child = digester.peek(0);
Object parent = digester.peek(1);
// Call the specified method
IntrospectionUtils.callMethod1(parent, methodName,
child, paramType, digester.getClassLoader());
}
  其中,child为Service,parent为Server,methodName为addService,这样就完成了Server/Service的组装。
  3, 调用getServer().initialize() 方法初始化StandardServer对象,下面是StandardServer的initialize方法

public void initialize()
throws LifecycleException
{
if (initialized) {
log.info(sm.getString("standardServer.initialize.initialized"));
return;
}
lifecycle.fireLifecycleEvent(INIT_EVENT, null);
initialized = true;
if( oname==null ) {
try {
oname=new ObjectName( "Catalina:type=Server");
Registry.getRegistry(null, null)
.registerComponent(this, oname, null );
} catch (Exception e) {
log.error("Error registering ",e);
}
}
// Register global String cache
try {
ObjectName oname2 =
new ObjectName(oname.getDomain() + ":type=StringCache");
Registry.getRegistry(null, null)
.registerComponent(new StringCache(), oname2, null );
} catch (Exception e) {
log.error("Error registering ",e);
}
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services.initialize();
}
}
 使用JMX管理Server对象,然后调用Service的initialize方法。后面StandardEngine, Connector等的初始化方法都相似。  4, 调用Catalina的start方法,主要代码如下

        // Start the new server
if (getServer() instanceof Lifecycle) {
try {
((Lifecycle) getServer()).start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
}
 5,调用StandardServer的start方法,主要代码如下
public void start() throws LifecycleException {
// Validate and update our current component state
if (started) {
log.debug(sm.getString("standardServer.start.started"));
return;
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our defined Services
synchronized (services) {
for (int i = 0; i < services.length; i++) {
if (services instanceof Lifecycle)
((Lifecycle) services).start();
}
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}

 6, 调用StandardService的start方法,代码如下
public void start() throws LifecycleException {
// Validate and update our current component state
if (started) {
return;
}
if( ! initialized )
init();
lifecycle.fireLifecycleEvent(START_EVENT, null);
started = true;
// Start our defined Container first
if (container != null) {
synchronized (container) {
if (container instanceof Lifecycle) {
((Lifecycle) container).start();
}
}
}
synchronized (executors) {
for ( int i=0; i<executors.size(); i++ ) {
executors.get(i).start();
}
}
// Start our defined Connectors second
synchronized (connectors) {
for (int i = 0; i < connectors.length; i++) {
try {
((Lifecycle) connectors).start();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connectors), e);
}
}
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
 7, 调用StandardEngine的start方法,主要代码如下
public void start() throws LifecycleException {
if( started ) {
return;
}
if( !initialized ) {
init();
}
// Standard container startup
super.start();
}
 StandardEngine继承ContainerBase,下面是ContainerBase的start方法
public synchronized void start() throws LifecycleException {
// Validate and update our current component state
if (started) {
return;
}
started = true;
// Start our subordinate components, if any
if ((logger != null) && (logger instanceof Lifecycle))
((Lifecycle) logger).start();
if ((manager != null) && (manager instanceof Lifecycle))
((Lifecycle) manager).start();
if ((cluster != null) && (cluster instanceof Lifecycle))
((Lifecycle) cluster).start();
if ((realm != null) && (realm instanceof Lifecycle))
((Lifecycle) realm).start();
if ((resources != null) && (resources instanceof Lifecycle))
((Lifecycle) resources).start();
// Start our child containers, if any  //StandardHost
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children instanceof Lifecycle)
((Lifecycle) children).start();
}
// Start the Valves in our pipeline (including the basic), if any
if (pipeline instanceof Lifecycle)
((Lifecycle) pipeline).start();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);
// Start our thread
threadStart();
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
}
 在前面的组件装配过程中,通过digester.addSetNext("Server/Service/Engine/Host", "addChild",  "org.apache.catalina.Container") 方法,将StandardHost与StandardEngine组装。因此findChildren得到StandardHost对象,执行其start方法。  8, 调用StandardHost的start方法,代码如下

public synchronized void start() throws LifecycleException {
if( started ) {
return;
}
if( ! initialized )
init();
// Set error report valve
if ((errorReportValveClass != null)
&& (!errorReportValveClass.equals(""))) {
try {
boolean found = false;
if(errorReportValveObjectName != null) {
ObjectName[] names =
((StandardPipeline)pipeline).getValveObjectNames();
for (int i=0; !found && i<names.length; i++)
if(errorReportValveObjectName.equals(names))
found = true ;
}
if(!found) {         
Valve valve = (Valve) Class.forName(errorReportValveClass)
.newInstance();
addValve(valve);
errorReportValveObjectName = ((ValveBase)valve).getObjectName() ;
}
} catch (Throwable t) {
}
}
super.start();
}
 同样,StandardHost继承ContainerBase(代码见上面的ContainerBase),在调用lifecycle.fireLifecycleEvent(START_EVENT, null)时,会调用先前注册的LifecycleListener, 注册方法在前面的组装过程中,代码如下
digester.addRule(prefix + "Host",
new LifecycleListenerRule
("org.apache.catalina.startup.HostConfig",
"hostConfigClass"));
   9, 调用HostConfig的lifecycleEvent方法

// Identify the host we are associated with
try {
host = (Host) event.getLifecycle();
if (host instanceof StandardHost) {
setDeployXML(((StandardHost) host).isDeployXML());
setUnpackWARs(((StandardHost) host).isUnpackWARs());
setXmlNamespaceAware(((StandardHost) host).getXmlNamespaceAware());
setXmlValidation(((StandardHost) host).getXmlValidation());
}
} catch (ClassCastException e) {
log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
return;
}
// Process the event that has occurred
if (event.getType().equals(Lifecycle.START_EVENT))
start();
else if (event.getType().equals(Lifecycle.STOP_EVENT))
stop();
 在start方法中,会调用deployApps方法,具体如下
protected void deployApps() {
File appBase = appBase();
File configBase = configBase();
String[] filteredAppPaths = filterAppPaths(appBase.list());
// Deploy XML descriptors from configBase
deployDescriptors(configBase, configBase.list());
// Deploy WARs, and loop if additional descriptors are found
deployWARs(appBase, filteredAppPaths);
// Deploy expanded folders
deployDirectories(appBase, filteredAppPaths);
}
   10, 调用deployDescriptor方法,代码如下

  protected void deployDescriptor(String contextPath, File contextXml, String file) {
if (deploymentExists(contextPath)) {
return;
}
DeployedApplication deployedApp = new DeployedApplication(contextPath);        
Context context = null;
try {
synchronized (digester) {
try {
context = (Context) digester.parse(contextXml);
if (context == null) {
log.error(sm.getString("hostConfig.deployDescriptor.error",
file));
return;
}
} finally {
digester.reset();
}
}
if (context instanceof Lifecycle) {
Class clazz = Class.forName(host.getConfigClass());
LifecycleListener listener =
(LifecycleListener) clazz.newInstance();
((Lifecycle) context).addLifecycleListener(listener);
}
context.setConfigFile(contextXml.getAbsolutePath());
context.setPath(contextPath);
host.addChild(context);
...
}
   11, StandardHost的addChild方法会调用addChildInternal,代码如下

private void addChildInternal(Container child) {
synchronized(children) {
if (children.get(child.getName()) != null)
throw new IllegalArgumentException("addChild:  Child name '" +
child.getName() +
"' is not unique");
child.setParent(this);  // May throw IAE
children.put(child.getName(), child);
// Start child
if (started && startChildren && (child instanceof Lifecycle)) {
boolean success = false;
try {
((Lifecycle) child).start();
success = true;
} catch (LifecycleException e) {
log.error("ContainerBase.addChild: start: ", e);
throw new IllegalStateException
("ContainerBase.addChild: start: " + e);
} finally {
if (!success) {
children.remove(child.getName());
}
}
}
fireContainerEvent(ADD_CHILD_EVENT, child);
}
}
 由第十步可知,方法的参数为StandardContext对象,因此,会调用StandardContext的start方法。  12,调用StandardContext的start方法

public synchronized void start() throws LifecycleException {
....
try {
if (ok) {                                
// Start our child containers, if any
Container children[] = findChildren();
for (int i = 0; i < children.length; i++) {
if (children instanceof Lifecycle)
((Lifecycle) children).start();
}
// Start the Valves in our pipeline (including the basic),
// if any
if (pipeline instanceof Lifecycle) {
((Lifecycle) pipeline).start();
}
// Notify our interested LifecycleListeners
lifecycle.fireLifecycleEvent(START_EVENT, null);                               
}
}
....
}
 在调用lifecycle.fireLifecycleEvent(START_EVENT, null)时,会调用先前注册的LifecycleListener, 注册方法在前面的组装过程中,代码如下
   digester.addRule(prefix + "Context",
new LifecycleListenerRule
("org.apache.catalina.startup.ContextConfig",
"configClass"));
 

13,调用ContextConfig的lifecycleEvent方法, 代码如下

if (event.getType().equals(Lifecycle.START_EVENT)) {
start();
} else if (event.getType().equals(StandardContext.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(StandardContext.AFTER_START_EVENT)) {
// Restore docBase for management tools
if (originalDocBase != null) {
String docBase = context.getDocBase();
context.setDocBase(originalDocBase);
originalDocBase = docBase;
}
} else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
if (originalDocBase != null) {
String docBase = context.getDocBase();
context.setDocBase(originalDocBase);
originalDocBase = docBase;
}
stop();
} else if (event.getType().equals(Lifecycle.INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.DESTROY_EVENT)) {
destroy();
}
 在start方法中,最终会调用processDefaultWebConfig方法,主要代码如下
protected void processDefaultWebConfig(Digester digester, InputStream stream,
InputSource source) {
// Process the default web.xml file
synchronized (digester) {
try {
source.setByteStream(stream);
if (context instanceof StandardContext)
((StandardContext) context).setReplaceWelcomeFiles(true);
digester.setClassLoader(this.getClass().getClassLoader());
digester.setUseContextClassLoader(false);
digester.push(context);
digester.setErrorHandler(new ContextErrorHandler());
digester.parse(source);
if (parseException != null) {
ok = false;
}
....
} finally {
digester.reset();
parseException = null;
try {
if (stream != null) {
stream.close();
}
} catch (IOException e) {
log.error(sm.getString("contextConfig.defaultClose"), e);
}
}
}
}
 上面的方法通过解析 conf/web.xml来加载org.apache.catalina.servlets.DefaultServlet 和 org.apache.jasper.servlet.JspServlet。

运维网声明 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-330776-1-1.html 上篇帖子: 【Tomcat配置】window启动方式 下篇帖子: Tomcat启动的Tomcat程序不一定是Tomcat自身所在的目录
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

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

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

扫描微信二维码查看详情

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


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


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


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



合作伙伴: 青云cloud

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