龙空技术网

tomcat源码分析(一)启动服务

西二旗的哥 423

前言:

此时同学们对“pid控制算法源码”都比较重视,咱们都需要剖析一些“pid控制算法源码”的相关内容。那么小编在网络上搜集了一些有关“pid控制算法源码””的相关文章,希望朋友们能喜欢,咱们一起来了解一下吧!

从startup.sh入手

os400=falsecase "`uname`" inOS400*) os400=true;;esacPRG="$0"while [ -h "$PRG" ] ; do  ls=`ls -ld "$PRG"`  link=`expr "$ls" : '.*-> \(.*\)$'`  if expr "$link" : '/.*' > /dev/null; then    PRG="$link"  else    PRG=`dirname "$PRG"`/"$link"  fidonePRGDIR=`dirname "$PRG"`EXECUTABLE=catalina.shif $os400; then  evalelse  if [ ! -x "$PRGDIR"/"$EXECUTABLE" ]; then    echo "Cannot find $PRGDIR/$EXECUTABLE"    echo "The file is absent or does not have execute permission"    echo "This file is needed to run this program"    exit 1  fifiexec "$PRGDIR"/"$EXECUTABLE" start "$@" 

整个脚本核心就是最后一句代码, EXECUTABLE变量是catalina.sh, 代表执行catalina.sh, 参数是start, 再去对比了shutdown.sh, 两个脚本的核心都是调用catalina.sh传递的变量不同。

浏览catalina.sh脚本

整个脚本很长,我这里之截图了我们关心的脚本内容。 这段代码里, 除了能看到参数传递start, 最后会输出Tomcat started外,能看到调用了org.apache.catalina.startup.Bootstrap, 也就是说找到我们的程序入口,或者说找到了我们的程序的main函数。

    shift    eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \      -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \      -classpath "\"$CLASSPATH\"" \      -Djava.security.manager \      -Djava.security.policy=="\"$CATALINA_BASE/conf/catalina.policy\"" \      -Dcatalina.base="\"$CATALINA_BASE\"" \      -Dcatalina.home="\"$CATALINA_HOME\"" \      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \      org.apache.catalina.startup.Bootstrap "$@" start \      >> "$CATALINA_OUT" 2>&1 "&"  else    eval $_NOHUP "\"$_RUNJAVA\"" "\"$CATALINA_LOGGING_CONFIG\"" $LOGGING_MANAGER "$JAVA_OPTS" "$CATALINA_OPTS" \      -D$ENDORSED_PROP="\"$JAVA_ENDORSED_DIRS\"" \      -classpath "\"$CLASSPATH\"" \      -Dcatalina.base="\"$CATALINA_BASE\"" \      -Dcatalina.home="\"$CATALINA_HOME\"" \      -Djava.io.tmpdir="\"$CATALINA_TMPDIR\"" \      org.apache.catalina.startup.Bootstrap "$@" start \      >> "$CATALINA_OUT" 2>&1 "&"  fi  if [ ! -z "$CATALINA_PID" ]; then    echo $! > "$CATALINA_PID"  fi  echo "Tomcat started."

看到这里我们做个小小的总结:Tomcat本质上也是一个java程序,因此startup.sh会启动一个jvm来运行tomcat的启动类Bootstrap.java。

Bootstrap类核心功能静态构造器部分, 主要初始化了CATALINA_HOME和CATALINA_BASE两个变量内容main函数方法部分一,创建和初始化daemon, 创建三个类加载器main函数方法部分二,控制tomcat的启动和停止从Bootstrap.main方法开始

开始main方法之前,首先看两个关键属性.

/*************守护进程对象**********/private static volatile Bootstrap daemon = null;/***守护程序用的catalina对象***/private Object catalinaDaemon = null;
Bootstrap#main
 public static void main(String args[]) {	synchronized (daemonLock) {		if (daemon == null) {			//初始化完成之前,不要对daemon赋值			Bootstrap bootstrap = new Bootstrap();			try {			    //调用初始化方法, 完成加载器的配置和初始化器的准备				bootstrap.init();			} catch (Throwable t) {				handleThrowable(t);				t.printStackTrace();				return;			}			daemon = bootstrap;		} else {			//当作为服务正在运行时,如果调用停止方法,这将在一个新线程上进行,以确保使用正确的类加载器,防止出现未找到类的异常			Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);		}	}	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);        //Bootstrap加载		daemon.load(args);        //Bootstrap启动		daemon.start();		if (null == daemon.getServer()) {			System.exit(1);		}	} else if (command.equals("stop")) {		daemon.stopServer(args);	} else if (command.equals("configtest")) {		daemon.load(args);		if (null == daemon.getServer()) {			System.exit(1);		}		System.exit(0);	} else {			}      }
//Bootstrap.initpublic void init() throws Exception {    //初始化类的三个加载器	initClassLoaders();	//设置线程类加载器, 将容器的加载器传入	Thread.currentThread().setContextClassLoader(catalinaLoader);	//加载安全类加载器	SecurityClassLoad.securityClassLoad(catalinaLoader);	//通过反射加载catalina	Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");	//创建对象	Object startupInstance = startupClass.getConstructor().newInstance();	String methodName = "setParentClassLoader";	Class<?> paramTypes[] = new Class[1];	//将类加载器作为参数传递	paramTypes[0] = Class.forName("java.lang.ClassLoader");	Object paramValues[] = new Object[1];	paramValues[0] = sharedLoader; //共享加载器	Method method = startupInstance.getClass().getMethod(methodName, paramTypes); //对类加载器进行初始化赋值	//调用catalina类内部的setParentClassLoader方法对catalina类内部的类加载赋值	method.invoke(startupInstance, paramValues);	//将创建好的startupInstance对象赋值给catalinaDaemon	catalinaDaemon = startupInstance;}
Catalina#load

Catalina类的load方法核心就解析config/server.xml并创建Server组件实例, 也就是我们在tomcat整体架构章节里了解的一个tomcat只有一个Server实例。 这部分代码块,我删掉了注释代码,try...catch, 只留下了核心业务代码。

public void load() {	loaded = true;	long t1 = System.nanoTime();	initDirs();	initNaming();    //利用digester类解析server.xml,得到容器的配置	Digester digester = createStartDigester();	InputSource inputSource = null;	InputStream inputStream = null;	File file = null;		file = configFile();	inputStream = new FileInputStream(file);	inputSource = new InputSource(file.toURI().toURL().toString());		if (inputStream == null) {		inputStream = getClass().getClassLoader().getResourceAsStream(getConfigFile());		inputSource = new InputSource(getClass().getClassLoader().getResource(getConfigFile()).toString());		}	if (inputStream == null) {		inputStream = getClass().getClassLoader().getResourceAsStream("server-embed.xml");		inputSource = new InputSource(getClass().getClassLoader().getResource("server-embed.xml").toString());	}	if (inputStream == null || inputSource == null) {		return;	}	try {		inputSource.setByteStream(inputStream);		digester.push(this);		digester.parse(inputSource);	} catch (SAXParseException spe) {		return;	} catch (Exception e) {		return;	}		getServer().setCatalina(this);	getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());	getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());	initStreams();    //服务器执行初始化 开始调用的Server的初始化方法注意Server是一个接口	getServer().init();}
Catalina#start
public void start() {	if (getServer() == null) {		load();	}	if (getServer() == null) {		return;	}	long t1 = System.nanoTime();	//开始一个Server实例	try {		getServer().start();	} catch (LifecycleException e) {		log.fatal(sm.getString("catalina.serverStartFail"), e);		try {			getServer().destroy();		} catch (LifecycleException e1) {			log.debug("destroy() failed for failed Server ", e1);		}		return;	}	long t2 = System.nanoTime();	if(log.isInfoEnabled()) {		log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");	}	if (useShutdownHook) {		if (shutdownHook == null) {			shutdownHook = new CatalinaShutdownHook();		}		Runtime.getRuntime().addShutdownHook(shutdownHook);		LogManager logManager = LogManager.getLogManager();		if (logManager instanceof ClassLoaderLogManager) {			((ClassLoaderLogManager) logManager).setUseShutdownHook(false);		}	}	if (await) {		await();		stop();	}}

从Bootstrap#createStartDigester方法中可以看到Server接口的实现类是StandardServer.

digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");

Server接口继承了Lifecycle接口

StandardServer类继承了抽象类LifecycleMBeanBase,同时实现了Server接口

LifecycleMBeanBase抽象类又继承了抽象类LifecycleBase, 而LifecycleBase抽象类又实现了Lifecycle接口

通过前面的调用链看出来Catalina.start会调用Server接口的start方法,而StandardServer实现类的start方法就追溯到了LifeCycleBase抽象的start方法, 这个类里定义了抽象方法startInternal让子类去实现。 在start方法中也调用了startInternal方法。

StandardServer类图StandardServer#startInternal

protected void startInternal() throws LifecycleException {	fireLifecycleEvent(CONFIGURE_START_EVENT, null);	setState(LifecycleState.STARTING);	globalNamingResources.start();	synchronized (servicesLock) {	    //这里启动定义的多个service		for (Service service : services) {			service.start();		}	}}

根据Server的实现类StandardServer类,我顺便查看了其所在包, 看到了整个tomcat用到的核心组件的实现类都在这里了,比如StandardEngine, StandardService,StandardHost, 可以查看其他的实现类结构。

总结

结合上面的两张图片,以及上述的源码分析,我们就能总结出来整个startup.sh过程中完成的任务

Tomcat本质上是一个java程序,因此startup脚本会启动一个jvm来运行tomcat的启动类Bootstrap.Bootstrap的主要任务就是初始化tomcat的类加载器,并且创建Catalina.Catalina是一个启动类,通过解析Server.xml创建相应组件,通过调用Server接口实现类去启动Server.StandardServer通过调用父类LifecycleBase的start方法,并且重写startInternal方法来启动Server和启动Service.Service组件的职责就是管理连接器和顶层容器,他会调用连接器和顶层容器的start方法.容器组件负责启动管理子容器,并且调用Host的start方法, 将各层容器启动起来。参考资料

标签: #pid控制算法源码