一、Bootstrap.start()
(1)tomcat启动时会先调用脚本,在脚本运行时会启动Bootstrap的main方法,mian方法中会调用load方法进行初始化操作,通过责任链模式将所有结点初始化后,会调用start方法-该方法是tomcat启动的核心方法,即启动tomcat的核心线程。
在上一篇 - tomcat-超详细的启动流程(init)中提过这里的daemon实际上为catalina,因此调用catalina的start方法。
main方法中的源码如下:
代码语言:javascript复制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")) { //都会走进该if分支
daemon.setAwait(true);
daemon.load(args);
daemon.start();
} 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 {
log.warn("Bootstrap: command "" command "" does not exist.");
}
} catch (Throwable var4) {
Throwable t = var4;
if (var4 instanceof InvocationTargetException && var4.getCause() != null) {
t = var4.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
二、Catalina.start()
本质上与init()方法没啥区别,委派给下一个standardServer结点来进行start方法。
代码语言:javascript复制public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal(sm.getString("catalina.noServer"));
return;
}
long t1 = System.nanoTime();
try { //standardServer的start方法 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;
} //... //在Bootstrap的main方法中 设置了为true,因此为调用await方法,保证主线程一直处于运行。
if (await) {
await();
stop();
}
}
这里的start方法也一样,由lifecycleBase来负责统一管理生命周期,并且子类会重新这个start方法,具体源码如下:
代码语言:javascript复制@Override
public final synchronized void start() throws LifecycleException {
//... 上面不重要
try { //设置状态 setStateInternal(LifecycleState.STARTING_PREP, null, false);
//该方法由子类重写
startInternal();
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
handleSubClassException(t, "lifecycleBase.startFail", toString());
}
}
找到standardServer对应的startInternal方法。
三、StandardServer.startInternal()
在StandardServer.startInternal()中会继续通过责任链模式向后传递,进行StandardService的start方法调用。
代码语言:javascript复制@Override
protected void startInternal() throws LifecycleException {
//触发监听fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
//责任链继续向后传递,进行standardService的start方法 synchronized (servicesLock) {
for (int i = 0; i < services.length; i ) {
services[i].start();
}
}//...
}
四、StandardService.startInternal()
从下图源码中可以看出,StandardService.startInternal(),先进行engine的start调用,再进行connector的start调用。
代码语言:javascript复制@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// engine的start
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
//server.xml中无配置executors,所以不会执行这段代码 synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
//connector的start
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
(1)StandardEngine.startInternal()
本质上调用了父类ContainerBase.startInternal()。核心源码如下,在代码中主要作用为将tomcat容器以线程池的方式来启动,这个startStopExecutor是在tomcat的init中被构造出来的。
代码语言:javascript复制@Override
protected synchronized void startInternal() throws LifecycleException {
// 前面代码我跳过了,不重要
Container children[] = findChildren();
List<Future<Void>> results = new ArrayList<>();
for (int i = 0; i < children.length; i ) {
results.add(startStopExecutor.submit(new StartChild(children[i])));
}
代码语言:javascript复制 //...
}
tomcat容器的关系为:Engine——>Host——>Context——>Wrapper——>Servlet。
进入StartChild线程中可以看到线程实现方式为Callable,并且在child中又使用了start()。即Engine——>Host。
代码语言:javascript复制private static class StartChild implements Callable<Void> {
private Container child;
public StartChild(Container child) {
this.child = child;
}
@Override
public Void call() throws LifecycleException {
child.start();
return null;
}
}
(2)StandardHost.startInternal()
StandardHost.startInternal()中又递归调用了父类ContainerBase.startInternal方法。即下一个线程池需要启动的子类为StandardContext。
代码语言:javascript复制@Override
protected synchronized void startInternal() throws LifecycleException { // 前面代码... super.startInternal();
}
(3)StandardContext.startInternal()
StandardContext.startInternal()这里的触发监听,比较核心,在内部会解析web.xml。
代码语言:javascript复制protected synchronized void startInternal() throws LifecycleException {
// 前面代码... try { // 前面代码... // 触发监听 - 解析web.xml
fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
// 这里child为Context的子类 Wrapper的start的调用
for (Container child : findChildren()) {
if (!child.getState().isAvailable()) {
child.start();
}
}//后面代码...}
触发监听,核心代码:
代码语言:javascript复制@Override
public void lifecycleEvent(LifecycleEvent event) {
// 前面代码...
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { //触发监听的type为这一行 configureStart();
} else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
beforeStart();
} else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
if (originalDocBase != null) {
context.setDocBase(originalDocBase);
}
} else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
configureStop();
} else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
init();
} else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
destroy();
}
}
代码语言:javascript复制protected synchronized void configureStart() {
//前面代码...
webConfig();
// 后面代码...
代码语言:javascript复制}
代码语言:javascript复制**
* Scan the web.xml files that apply to the web application and merge them
* using the rules defined in the spec. For the global web.xml files,
* where there is duplicate configuration, the most specific level wins. ie
* an application's web.xml takes precedence over the host level or global
* web.xml file.
*///看英文注释可以知道,这个方法是用来解析tomcat的web.xmlprotected void webConfig() {
在webConfig中也可以找到我们经常会配置的一些解析方法,比如servlet、filter等。
下图为filter的解析。
代码语言:javascript复制for (FilterDef filter : webxml.getFilters().values()) {
if (filter.getAsyncSupported() == null) {
filter.setAsyncSupported("false");
}
context.addFilterDef(filter);
}
for (FilterMap filterMap : webxml.getFilterMappings()) {
context.addFilterMap(filterMap);
}
下图为servlet的解析。
代码语言:javascript复制for (ServletDef servlet : webxml.getServlets().values()) {
Wrapper wrapper = context.createWrapper();
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName()); // ...}
下图为listener的解析。
代码语言:javascript复制for (String listener : webxml.getListeners()) {
context.addApplicationListener(listener);
}
(4)StandardWrapper.startInternal()
代码语言:javascript复制protected synchronized void startInternal() throws LifecycleException {
//本质上也一样 调用父类的startInternal() 线程池启动
代码语言:javascript复制 super.startInternal();
}
这一步为止,tomcat容器(Engine—>Host—>Context—>Wrapper)启动完毕。
五、mapperListern.startInternal()
回到StandardService,会进行mapperListern的启动。找到子类重写的startInernal(),源码如下:
代码语言:javascript复制@Override
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
// 获取engine Engine engine = service.getContainer();
if (engine == null) {
return;
}
findDefaultHost();
// 添加engine中的listerner addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
//遍历host-注册host
registerHost(host);
}
}
}
代码语言:javascript复制private void registerHost(Host host) {
String[] aliases = host.findAliases();
mapper.addHost(host.getName(), aliases, host);
for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {//注册host下的contextregisterContext((Context) container);
}
}
}
代码语言:javascript复制private void registerContext(Context context) {
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
Host host = (Host)context.getParent();
WebResourceRoot resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles();
List<WrapperMappingInfo> wrappers = new ArrayList<>();
for (Container container : context.findChildren()) { //遍历contxt-注册wrapper prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerWrapper",
container.getName(), contextPath, service));
}
}
//最后将host名 host端口 context wrapper即servlet 添加到mapper中。
mapper.addContextVersion(host.getName(), host, contextPath,
context.getWebappVersion(), context, welcomeFiles, resources,
wrappers);
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerContext",
contextPath, service));
}
}
在mapper中会注册engine、host、context、wrapper,组成host名 host端口 context路径 context应用名 servlet添加到mapper中,因此当接受到http请求时,如果tomcat有多个工程,多个host情况下,可以根据url拆分然后然后根据mapper来进行匹配。
六、Connector.startInternal()
责任链模式委托protocolHandler.start(),实际上未NioEndpoint.startInternal()
代码语言:javascript复制protected void startInternal() throws LifecycleException {
if (getPortWithOffset() < 0) {
throw new LifecycleException(sm.getString(
"coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
}
setState(LifecycleState.STARTING);
try { //protocolHandler的启动 protocolHandler.start();
} catch (Exception e) {
throw new LifecycleException(
sm.getString("coyoteConnector.protocolHandlerStartFailed"), e);
}
}
七、NioEndpoint.startInternal()
(1)在NioEndpoint.startInternal()中会创建工作线程池、clientPoller线程池、acceptor线程池。
代码语言:javascript复制public void startInternal() throws Exception {
if (!running) {
//...
if ( getExecutor() == null ) { //启动工作线程, createExecutor();
}
initializeConnectionLatch();
pollers = new Poller[getPollerThreadCount()];
for (int i=0; i<pollers.length; i ) {
pollers[i] = new Poller();
Thread pollerThread //启动clientPoller线程
= new Thread(pollers[i], getName() "-ClientPoller-" i);
pollerThread.setPriority(threadPriority);
pollerThread.setDaemon(true);
pollerThread.start();
}
//创建acceptor线程
startAcceptorThreads();
}
}
最小线程数量默认为10,最大线程数量默认为200,也可读取server.xml中配置的线程数量。
代码语言:javascript复制public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue();
TaskThreadFactory tf = new TaskThreadFactory(getName() "-exec-", daemon, getThreadPriority());
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);
}
(2)acceptor线程
在下面方法中会启动,acceptor线程,并创建acceptor对象。
代码语言:javascript复制protected void startAcceptorThreads() {
int count = getAcceptorThreadCount();
acceptors = new ArrayList<>(count);
for (int i = 0; i < count; i ) {
Acceptor<U> acceptor = new Acceptor<>(this);
String threadName = getName() "-Acceptor-" i;
acceptor.setThreadName(threadName);
acceptors.add(acceptor);
Thread t = new Thread(acceptor, threadName);
t.setPriority(getAcceptorThreadPriority());
t.setDaemon(getDaemon());
t.start();
}
}
启动线程后,查询acceptor线程的run方法,发现acceptor中会建立socket连接,可以接收http请求。
代码语言:javascript复制@Override
public void run() {
while (endpoint.isRunning()) {
try {
U socket = null;
try {
//接收socket
socket = endpoint.serverSocketAccept();
} catch (Exception ioe) {
//...
}
} catch (Throwable t) {
//...//...//...
代码语言:javascript复制 }
}
}
到这一步tomcat完全启动成功,并且开启了对应的线程,可以接受http请求,处理http请求。
八、总结