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. Pandas学习整理与实践

    Part 1. Pandas初识 作为一款数据处理工具,Pandas本身集成了Numpy(数据计算处理)及matplotlib(绘图),其便捷的数据处理能力.方便的文件读写以及支持多维度的表示方式使其 ...

  2. Video标签动态修改src地址播放问题

    不管在React或Vue中,将一个变量赋值给src属性,当修改这个变量的值时,video播放的还是原来的视频. Vue中 <video id="root"> <s ...

  3. Spring Boot Controller单元测试

    一.创建Controller 一个方法是用传统IO来下载文件,一个是NIO下载文件 @Controller public class FileController { private Logger l ...

  4. 【PHP+nginx+php-fpm】探讨它们的运行机制和原理

    1.PHP+nginx+php-fpm的运行机制和原理 Nginx 是非阻塞IO & IO复用模型,通过操作系统提供的类似 epoll 的功能,可以在一个线程里处理多个客户端的请求.(非阻塞, ...

  5. latch - undo global data等待事件分析

    一环境跑压力测试的时候,标题所述等待事件在top N中.不用查,也知道是因为undo竞争的事件. 根据metalink文档解释,是由于undo表空间不足引起的. This implies that s ...

  6. JAVA | Java对象的内存分配过程是如何保证线程安全的?

    JAVA | Java对象的内存分配过程是如何保证线程安全的? 专注于Java领域优质技术,欢迎关注 作者 l Hollis 来源 l Hollis(ID:hollischuang) JVM内存结构, ...

  7. 蓝牙BLE: 蓝牙(BLE)协议栈

    蓝牙协议是通信协议的一种,一般而言,我们把某个协议的实现代码称为协议栈(protocol stack),BLE协议栈就是实现低功耗蓝牙协议的代码,理解和掌握BLE协议是实现BLE协议栈的前提.当前的蓝 ...

  8. tensorflow 笔记 16:tf.pad

    函数: tf.compat.v1.pad tf.pad 函数表达式如下: tf.pad(    tensor,    paddings,    mode='CONSTANT',    name=Non ...

  9. J-CUBE Appears at AVATAR Xprize at Geneva 2019

    2019年5月27日,瑞士日内瓦,Avatar Xprize发布会隆重举行.非常荣幸的是,J-CUBE也受邀参加此次大会. 关于Avatar Xprize项目的介绍 https://avatar.xp ...

  10. Jmetal设置Solution Variables

    Jmetal设置Solution Variables 觉得有用的话,欢迎一起讨论相互学习~Follow Me 首先每个solution都必须使用Problemset初始化 ProblemSet pro ...