Tomcat组件梳理--Catalina

1.定义和功能

Catalina是Tomcat的核心组件,是Servlet容器,Catalina包含了所有的容器组件,其他模块均为Catalina提供支撑。通过Coyote模块提供连接通信,Jasper模块提供JSP引擎,Naming提供JNDI服务,Juli提供日志服务。结构如下:

主要的功能包括接收请求,处理请求,返回结果。但是这些具体的实现是在catalina里面的子容器里面,我们在对应的文章里面讲解,此处聚焦在Catalina的源代码提供的功能上。

处理上面这些,Catalina还提供启动入口,关闭入口等。

2.属性

//org.apache.catalina.startup.Catalina
/**
* 用于await的flag
*/
protected boolean await = false; /**
* Server配置的文件路
*/
protected String configFile = "conf/server.xml"; /**
* The shared extensions class loader for this server.
* 此server的shared 类加载器
*/
protected ClassLoader parentClassLoader =
Catalina.class.getClassLoader(); /**
* Server组件
*/
protected Server server = null; /**
* 使用shutdown钩子的flag
*/
protected boolean useShutdownHook = true; /**
* Shutdown钩子实例
*/
protected Thread shutdownHook = null; /**
* 默认需要开启Naming
*/
protected boolean useNaming = true; /**
* 预防重复加载的标记字段
*/
protected boolean loaded = false;

有几个主要的属性。

  • Catalina的子组件Server,通过digster工具解析server.xml文件构造该对象。
  • 用户shutdown时的钩子,是否使用以及调用的线程
  • 是否需要启动JNDI的标识
  • Server.xml配置文件的地址
  • stop用的await标记
  • 父类加载器。

3.操作

Catalina的操作有比较明显的区分,因为主要是处理来自shell的不同命令,所以,根据shell的传入的命令行,我们可以看到Catalina主要处理来自shell的start和stop命令。下面来解析start命令和stop命令的背后,以及Tomcat中提供的一个对xml解析很有用的库Digester。

再看一下Bootstrap类中的main方法中对shell命令的处理,可以看到start时主要调用load(args)和start()方法,stop时主要调用stopServer(args)方法。

//3.判断shell传入的值,执行对应的动作
if (command.equals("startd")) {
//执行start方法的内容,主要为执行Catalina的load()和start()方法
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();
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 {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}

3.1.执行shell的start命令

3.1.1.调用load(args)方法

现在我们知道Shell的start命令交给Catalina处理时,实际上调用了load(args)和start()方法。我们先看load(args)方法

//org.apache.catalina.startup.Catalina#load(java.lang.String[])
/*
* 使用参数进行load
*/
public void load(String args[]) { try {
//1.根据传入的参数设置Catalina的一些属性的值
if (arguments(args)) {
//2.调用无参的load()方法
load();
}
} catch (Exception e) {
e.printStackTrace(System.out);
}
}

可以看到这里有两个业务逻辑:

  • 1.根据传入的参数设置Catalina的一些属性,这些属性主要就是Naming等一些值。
  • 2.调用午餐的load()方法

再看load()方法,去掉里面的异常和环境检查,可以看到主要逻辑如下:

//org.apache.catalina.startup.Catalina#load()

/**
* 准备好环境,解析好server.xml文件生成好对象。server对象也准备好,然后调用server的init方法
*/
public void load() { //1.检查java.io.tmpdir是有有效
initDirs(); //2.设置catalina.useNaming的系统参数
initNaming(); //3.用digester解析server.xml文件,把配置文件中的配置解析成java对象
//3.1.准备好用来解析server.xml文件需要用的digester。
Digester digester = createStartDigester();
//3.2.server.xml文件作为一个输入流传入
File file = configFile();
InputStream inputStream = new FileInputStream(file);
//3.3.使用inputStream构造一个sax的inputSource
InputSource inputSource = new InputSource(file.toURI().toURL().toString());
inputSource.setByteStream(inputStream);
//3.4.把当前类压入到digester的栈顶,用来作为digester解析出来的对象的一种引用
digester.push(this);
//3.5.调用digester的parse()方法进行解析。
digester.parse(inputSource); //4.为子组件Server设置一些值
getServer().setCatalina(this);
getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); //执行server的init方法,start方法的准备方法
getServer().init(); }

分析一下主要逻辑

  • 1.检查java.io.tmpdir是有有效,这个没啥好说的。

  • 2.设置catalina.useNaming的系统参数,这个也没啥好多的,等主要流程梳理完,咱们做一个JDNI的源码解析。

  • 3.用digester解析server.xml文件,把配置文件中的配置解析成java对象。该过程需要经过5个步骤才可以。

    • 3.1.准备好用来解析server.xml文件需要用的digester。
    • 3.2.读取server.xml文件作为一个输入流。
    • 3.3.使用inputStream构造一个sax的inputSource,因为digester底层用的是sax去解析的。
    • 3.4.把当前类压入到digester的栈顶,用来作为digester解析出来的对象的一种引用,digester自带一个栈的结构。
    • 3.5.调用digester的parse()方法进行解析。前面几步都是在准备环境,这里才是正真的去解析了。
  • 4.为子组件Server设置一些值,并调用server的init方法,start方法的准备方法。

这里面最值得说的应该就是第3步,通过Digester去解析xml文件,每遇到一个匹配的节点,都可以添加一个对应的事件。对Digester的操作都是一些普通的API操作,这里就不解释了,可以查看官方文档,或者查看博客,API挺简单的。

3.1.2.调用start()方法

start()方法主要作用就是调用Server的start()方法,并将一个shutdown的钩子加到JVM中。

主要的逻辑如下:

  • 1.执行Server的start()方法,如果执行失败,就调用Server的destroy()方法
  • 2.注册一个shutdown的钩子
  • 3.等待处理如何停止的问题

比较有意思的是3,如何停止一个服务,这种方法比较有意思,不过我们放在Server组件中去讲,因为实现是放在Server中的。

//org.apache.catalina.startup.Catalina#start

public void start() {

    //1.执行Server的start()方法,如果执行失败,就调用Server的destroy()方法
try {
//执行server的start方法
getServer().start();
} catch (LifecycleException e) {
try {
//如果start失败,就调用server的destroy方法
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
} //2.注册一个shutdown的钩子
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook); } //3.等待处理如何停止的问题
if (await) {
await();
stop();
}
}

在第2步的逻辑中,new了一个类去注册,该类的主要处理逻辑如下:

protected class CatalinaShutdownHook extends Thread {

    @Override
public void run() {
try {
if (getServer() != null) {
//1.调用Catalina的stop()方法
Catalina.this.stop();
}
} catch (Throwable ex) {
ExceptionUtils.handleThrowable(ex);
log.error(sm.getString("catalina.shutdownHookFail"), ex);
} finally {
// If JULI is used, shut JULI down *after* the server shuts down
// so log messages aren't lost
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).shutdown();
}
}
}
}

可以看到,只是去调用Catalina的stop()方法。不过这总调用方法也是比较奇特的,通过Catalina.this.stop()方法,不知道这种是不是可以跨线程调用一个类的实例,如果能,那将是一个很棒。

[补充]:Catalina.this方法是内部类调用外部类的方法。可以用来解决线程之间传递示例的问题。

3.2.执行shell的stop命令

shell的stop命令落在Catalina上只是去调用stopServer(args)方法,具体方法实现如下:

public void stopServer(String[] arguments) {

    //1.参数设置
if (arguments != null) {
arguments(arguments);
} Server s = getServer();
//2.如果Server存在,就调用Server的stop和destroy方法进行关闭,
if (s == null) {
// Create and execute our Digester
Digester digester = createStopDigester();
File file = configFile();
try (FileInputStream fis = new FileInputStream(file)) {
InputSource is =
new InputSource(file.toURI().toURL().toString());
is.setByteStream(fis);
digester.push(this);
digester.parse(is);
} catch (Exception e) {
log.error("Catalina.stop: ", e);
System.exit(1);
}
} else {
// Server object already present. Must be running as a service
try {
s.stop();
s.destroy();
} catch (LifecycleException e) {
log.error("Catalina.stop: ", e);
}
return;
} // 3.如果Server不存在,就重新解析server.xml文件构造server,然后通过socket发送shutdown命令关闭
s = getServer();
if (s.getPort()>0) {
try (Socket socket = new Socket(s.getAddress(), s.getPort());
OutputStream stream = socket.getOutputStream()) {
String shutdown = s.getShutdown();
for (int i = 0; i < shutdown.length(); i++) {
stream.write(shutdown.charAt(i));
}
stream.flush();
} catch (Exception ce) {
ce.printStackTrace();
} } else {
log.error(sm.getString("catalina.stopServer"));
System.exit(1);
}
}

主要的业务逻辑有三个:

  • 1.参数设置
  • 2.如果Server存在,就调用Server的stop和destroy方法进行关闭
  • 3.如果Server不存在,就重新解析server.xml文件构造server,然后通过socket发送shutdown命令关闭

关闭Server的具体实现,我们放在Server组件中。

4.总结

我们根据用户的调用,梳理了Catalina对start和stop命令行的相应方法。其中Digester对xml文件的解析时值得注意的,停止Server的方式也比较有意思,但是我们放在Server中解析。

Tomcat组件梳理--Catalina的更多相关文章

  1. Tomcat组件梳理—Service组件

    Tomcat组件梳理-Service组件 1.组件定义 Tomcat中只有一个Server,一个Server可以用多个Service,一个Service可以有多个Connector和一个Contain ...

  2. Tomcat组件梳理—Digester的使用

    Tomcat组件梳理-Digester的使用 再吐槽一下,本来以为可以不用再开一个篇章来梳理Digester了,但是发现在研究Service的创建时,还是对Digester的很多接口或者机制不熟悉,简 ...

  3. Tomcat组件梳理--Server

    Tomcat组件梳理--Server 1.Server组件的定义和功能概述 定义: Server组件用于描述一个启动的Tomcat实例,一个Tocmat被启动,在操作系统中占用一个进程号,提供web服 ...

  4. 1.Tomcat组件梳理—Bootstrap启动器

    Tomcat组件梳理-Bootstrap启动器 一开始是直接从Server开始做梳理的,但是发现有很多东西是从Catalina传输过来的,Catalina又是从Bootstrap启动的,所以还是回过头 ...

  5. 【转】Tomcat组件生命周期管理

    Tomcat组件生命周期管理 Tomcat中Server,Service,Connector,Engine,Host,Context,它们都实现了org.apache.catalina.Lifecyc ...

  6. Tomcat 组件介绍

    用了好长时间tomcat,但是其实自己只是反复听了这个名字,对Tomcat并不了解 1.Tomcat组件 Catalina Coyote Jasper Cluster 2.组件介绍 Tomcat Co ...

  7. Tomcat系列(4)——Tomcat 组件及架构详细部分

    核心部分   1. 定义 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta ...

  8. Tomcat系列(3)——Tomcat 组件及架构核心部分 4类主要组件(顶层,连接器,容器,嵌套)

    1.架构图 2. 定义 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta ...

  9. Tomcat组件

    Tomcat组件 tomcat常用组件 Tomcat的组织结构 Tomcat是一个基于组件的服务器,它的构成组件都是可配置的,其中最外层的给件是CATALINA SERVLET容器,其他的组件按照一定 ...

随机推荐

  1. JDBC Api详解

    一.什么是JDBC JDBC(Java Data Base Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Jav ...

  2. 完全卸载Oracle(亲身体验完整版)

    完全卸载Oracle 用Oracle自带的卸载程序不能从根本上卸载Oracle,从而为下次的安装留下隐患,那么怎么才能完全卸载Oracle呢? 那就是直接注册表清除,步骤如下: 开始->设置-& ...

  3. Python数据分析学习(一)

    转摘:https://segmentfault.com/a/1190000015440560 一.数据初探 首先导入要使用的科学计算包numpy,pandas,可视化matplotlib,seabor ...

  4. 【spring源码学习】spring事务中的嵌套事务中的保存点相关知识

    JDBC事务保存点(setSavepoint, releaseSavepoint )实例 以下是使用事务教程中描述的setSavepoint和回滚的代码示例. 此示例代码是基于前面章节中完成的环境和数 ...

  5. pom.xml activatedProperties --spring.profiles.active=uat 对应

    <profiles> <profile> <id>dev</id> <properties> <!-- 环境标识,需要与配置文件的名称 ...

  6. 使用innobackupex进行mysql的差异备份还原和延迟复制

    使用innobackupex进行mysql的差异备份还原和延迟复制 背景: 有同事执行update语句没有添加where条件,导致大量脏数据,需要将这张表恢复到前一天 数据库上有备份,每周一次完整备份 ...

  7. debian8 vga 文本模式下出现闪屏

    这种问题是因为 grub 里面关于 分辨率大小不对的问题. 在 debian 里面,在文件 /boot/grub/grub.cfg 里面可以添加 vga 参数配置. 如下: 在 kernel 启动参数 ...

  8. 配置cisco设备记录用户命令

    R1(config)# aaa new-model R1(config)# aaa accounting commands 0 default start-stop group local R1(co ...

  9. ByteUtil

    import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOExceptio ...

  10. OpenStack(一)——OpenStack的相关概念

    (1).OpenStack概述 OpenStack是一个由NASA(美国国家航空航天局)和Rackspace合作研发并发起的,以Apache许可证授权的自由软件和开放源代码项目. OpenStack是 ...