Tomcat组件梳理--Server
Tomcat组件梳理--Server
1.Server组件的定义和功能概述
定义:
Server组件用于描述一个启动的Tomcat实例,一个Tocmat被启动,在操作系统中占用一个进程号,提供web服务的功能,那个这个整个服务用Server来表示。
功能
Server作为描述一个Tomcat服务的组件,需要有对应的启动,停止方法,请求接收和处理方法等。所有的方法都是Server组件内部的一个子组件来实现。
总结就是,Server代表Tomcat服务实例,Tomcat所有提供的功能,都由Server组件中的子组件来实现。
2.Server组件的具体形式
明白了Server是干嘛的,那么这个组件到底长什么样子呢?我们可以从代码里来看这个组件到底长什么样子,由什么组成。Server从抽象层面来看,代表了一个Tomcat实例,但落在代码中还是一个Java的Class类。因此,我们先来看看Server类到底有哪些属性,就知道这个组件长了什么样子。
把Server类的代码中的属性抽取出来看如下:
public final class StandardServer{
//1.java命令和地址服务相关
/**
* JNDI的context
*/
private javax.naming.Context globalNamingContext = null;
/**
* JNDI的resource
*/
private NamingResourcesImpl globalNamingResources = null;
/**
* 作用与web application的JDNI监听器
*/
private final NamingContextListener namingContextListener;
//2.shutdown服务相关
/**
* 用于等待shutdown命令的端口号
*/
private int port = 8005;
/**
* 用于等待shutdown命令的地址
*/
private String address = "localhost";
/**
* shutdown服务在等待的命令
*/
private String shutdown = "SHUTDOWN";
/**
* 执行await()的线程
*/
private volatile Thread awaitThread = null;
/**
* 用于等待shutdown的socket对象
*/
private volatile ServerSocket awaitSocket = null;
/**
* stopAwait的标记位
*/
private volatile boolean stopAwait = false;
//3.子组件Service相关
/**
* Server下的service集合
*/
private Service services[] = new Service[0];
/**
* Service用到的锁
*/
private final Object servicesLock = new Object();
//4.父类加载器
//父类加载器
private ClassLoader parentClassLoader = null;
//5.catalina相关的
//catalinaHome的地址
private File catalinaHome = null;
//catalinaBase的地址
private File catalinaBase = null;
}
如代码里展现出来的,长的样子主要从以下5个点来描述:
- 1.JNDI相关
- 2.停止公告shutdown相关
- 3.子组件Service相关
- 4.类加载器相关
- 5.Catalina相关(Catalina作为Tomcat的启动组件,从这个类中启动Server)
以上,Server的重要属性和特点都在这里了,当然还有很多功能都是在Service组件中进行定义的。这些等我们来做Service组件分析时,再详细说明。
3.Server组件的具体功能
在上面中,我们知道了Server的作用和重要属性,现在我们就来看看Server的重要功能。
3.1.如何启动Server
启动Server需要调用两个方法,在真正的start()方法之前还需要执行init()方法,init方法是一个pre-start()方法。
看一下在Server中,这两个方法的具体表现。
首先是init的方法。init方法里需要做5个逻辑处理,分别是:
- 1.调用父类的init()方法
- 2.注册全局的String cache,作用是什么现在还不太清楚
- 3.注册MBean,使通过JMX能够监控
- 4.调用JNDI的init方法
- 5.对common和shared目录下的jar包进行校验,如果给出的jar文件包含MANIFEST,将被添加到container的manifest资源中.
- 6.执行Service的init方法
protected void initInternal() throws LifecycleException {
//1.先调用父类的init方法
super.initInternal();
//2.注册全局的String cache,作用是什么现在还不清楚
onameStringCache = register(new StringCache(), "type=StringCache");
//3.注册JMX的MBean
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
//4.调用JNDI的init方法
globalNamingResources.init();
// Populate the extension validator with JARs from common and shared class loaders
//5.验证
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
// Walk the class loader hierarchy. Stop at the system class loader.
// This will add the shared (if present) and common class loaders
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File (url.toURI());
if (f.isFile() &&
f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException e) {
// Ignore
} catch (IOException e) {
// Ignore
}
}
}
}
cl = cl.getParent();
}
}
//6.执行Service的init方法
for (int i = 0; i < services.length; i++) {
services[i].init();
}
}
然后是start()方法,该方法主要处理以下3个逻辑:
- 1.更新生命周期的状态,并发送生命周期时间,用来触发监听器
- 2.启动JNDI
- 3.启动Server中的所有Service组件。
@Override
protected void startInternal() throws LifecycleException {
//1.发送生命周期时间
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
//更改生命周期的状态为starting
setState(LifecycleState.STARTING);
//2.启动JDNI
globalNamingResources.start();
//3.调用Service的start()方法,启动Service
synchronized (servicesLock) {
for (int i = 0; i < services.length; i++) {
services[i].start();
}
}
}
3.2.如何关闭Server
对Server的关闭,先提两个问题:1.应该在什么时候进行关闭,2.要如何关闭。
第一个问题,Tomcat的解决方法时,Tomcat的main线程在启动完所有的组件后,自己开一个socket服务端,在指定的端口上进行监听,一直到有shutdown命令发送过来,就退出socket的等待,开始执行关闭方法。
第二个问题,Server的关闭依旧放在Tomcat的生命周期中的stop方法和destroy方法中进行处理。
虽然上面两个问题有解决方案了,但是应该还是有不清楚的地方,先看一下这两个问题在Catalina类中的调用顺序吧:
//org.apache.catalina.startup.Catalina#start
if (await) {
await();
stop();
}
如上,即判断一下是不是要await,如果需要,就调用Server的awati()方法开始循环等待socket连接,直到有一个匹配的命令行进来,然后结束await()方法的执行,开始执行stop()方法,开始处理停止相关的动作。
先看第一个问题即await()在Server中的具体实现,该方法主要处理逻辑分4步:
- 1.处理port等于-1或-2的情况。
- 2.在指定的端口上开启一个socket的服务端
- 3.在socket上进行等待连接,并对连接进行接收处理,与shutdown命令进行匹配,如果相等,就跳出循环。
- 4.清理打开的资源。
public void await() {
//1.处理port等于-1或-2的情况。
// Negative values - don't wait on port - tomcat is embedded or we just don't like ports
if( port == -2 ) {
// undocumented yet - for embedding apps that are around, alive.
return;
}
if( port==-1 ) {
try {
awaitThread = Thread.currentThread();
while(!stopAwait) {
try {
Thread.sleep( 10000 );
} catch( InterruptedException ex ) {
// continue and check the flag
}
}
} finally {
awaitThread = null;
}
return;
}
//2.设置一个socket的服务端在指定端口上进行等待
try {
awaitSocket = new ServerSocket(port, 1,
InetAddress.getByName(address));
} catch (IOException e) {
return;
}
try {
//当前线程设置为等待线程
awaitThread = Thread.currentThread();
//3.轮询等待,直到shutdown命令行进来,如果跟设置的匹配就结束循环
while (!stopAwait) {
ServerSocket serverSocket = awaitSocket;
if (serverSocket == null) {
break;
}
// Wait for the next connection
Socket socket = null;
StringBuilder command = new StringBuilder();
try {
InputStream stream;
long acceptStartTime = System.currentTimeMillis();
//3.1.等待连接,并拿到输入流inputSteam
socket = serverSocket.accept();
socket.setSoTimeout(10 * 1000);
stream = socket.getInputStream();
//3.2.有连接进来时,从socket中读取字节,转成字符串
int expected = 1024; // Cut off to avoid DoS attack
while (expected < shutdown.length()) {
if (random == null)
random = new Random();
expected += (random.nextInt() % 1024);
}
while (expected > 0) {
int ch = -1;
try {
ch = stream.read();
} catch (IOException e) {
log.warn("StandardServer.await: read: ", e);
ch = -1;
}
// Control character or EOF (-1) terminates loop
if (ch < 32 || ch == 127) {
break;
}
command.append((char) ch);
expected--;
}
} finally {
// Close the socket now that we are done with it
try {
if (socket != null) {
socket.close();
}
} catch (IOException e) {
// Ignore
}
}
// Match against our command string
//3.3.把传进来的字符串跟内置的进行匹配,如果如果一样就结束等待
boolean match = command.toString().equals(shutdown);
if (match) {
log.info(sm.getString("standardServer.shutdownViaPort"));
break;
}
}
//4.清理资源
} finally {
ServerSocket serverSocket = awaitSocket;
awaitThread = null;
awaitSocket = null;
// Close the server socket and return
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
// Ignore
}
}
}
}
await()方法执行完之后,就自动开始调用stop()方法了,我们来开始看stop()方法里面的内容,注意,此时是在Catalina类中的stop方法
// org.apache.catalina.startup.Catalina#stop
// Shut down the server
try {
Server s = getServer();
LifecycleState state = s.getState();
if (LifecycleState.STOPPING_PREP.compareTo(state) <= 0
&& LifecycleState.DESTROYED.compareTo(state) >= 0) {
// Nothing to do. stop() was already called
} else {
//调用Server的stop方法
s.stop();
//调用Server的destroy方法
s.destroy();
}
} catch (LifecycleException e) {
log.error("Catalina.stop", e);
}
这里应该比较容易看懂,Catalina的stop方法会调用Server的stop()方法和destroy()方法。
此时再依次看Server的stop()方法,该方法主要处理4个逻辑:
- 1.设置生命周期的转态,并推送生命周期的事件
- 2.调用所有的service的stop方法
- 3.停止JNDI
- 4.清理扥带线程和socket。
protected void stopInternal() throws LifecycleException {
//1.设置生命周期的状态,
setState(LifecycleState.STOPPING);
fireLifecycleEvent(CONFIGURE_STOP_EVENT, null);
// 2.调用所有的Service的stop方法
for (int i = 0; i < services.length; i++) {
services[i].stop();
}
//3.停止JDNI
globalNamingResources.stop();
//4.清理等待线程和socket
stopAwait();
}
然后是destroy方法,该方法中有4个处理逻辑,
- 1.调用所有的service的destroy()方法
- 2.调用JNDI的destroy()
- 3.解绑MBean,和String Cache,对应init方法中的注册。
- 4.调用父类的方法
protected void destroyInternal() throws LifecycleException {
// 1.调用所有的service的destroy()方法
for (int i = 0; i < services.length; i++) {
services[i].destroy();
}
//2.调用JNDI的destroy()
globalNamingResources.destroy();
//3.解绑MBean,和String Cache,对应init方法中的注册。
unregister(onameMBeanFactory);
unregister(onameStringCache);
//4.调用父类的方法
super.destroyInternal();
}
经过上面这些步骤,整个Server的服务就被停止下来了。还有很多子组件,也会被停止,不过都是调用相同的生命周期,会在每个子组件中详细说。
4.Server组件跟其他组件的联系
现在,我们明白了Server组件自己的特有属性和自己的功能,但是他跟其他组件的联系是怎么样的?其实在上面两个动作中,已经能看到,主要有:Service组件,JDNI组件,JMX组件,类加载器组件。
针对Service组件来说,一个Server组件包含多个Service,其实也可以看到,Server的主要功能,就是管理好所有的Service,逻辑都在Service中。
JNDI组件现在不详细探索,先探索Tomcat主要组件,然后再解决这个问题。JMX组件同样。
针对类加载器,Server只提供了一个设置和获取父类加载器的方法和属性,并没有提供自己的类加载器属性和方法,这里其实是有点不太懂的。
5.总结
以上,通过分析,我们知道Server主要是Tomcat服务的承担体,自己主要承担的是启动它下面的所有组件以及停止,以及对Service组件的管理。
Tomcat组件梳理--Server的更多相关文章
- Tomcat组件梳理—Service组件
Tomcat组件梳理-Service组件 1.组件定义 Tomcat中只有一个Server,一个Server可以用多个Service,一个Service可以有多个Connector和一个Contain ...
- Tomcat组件梳理—Digester的使用
Tomcat组件梳理-Digester的使用 再吐槽一下,本来以为可以不用再开一个篇章来梳理Digester了,但是发现在研究Service的创建时,还是对Digester的很多接口或者机制不熟悉,简 ...
- Tomcat组件梳理--Catalina
Tomcat组件梳理--Catalina 1.定义和功能 Catalina是Tomcat的核心组件,是Servlet容器,Catalina包含了所有的容器组件,其他模块均为Catalina提供支撑.通 ...
- 1.Tomcat组件梳理—Bootstrap启动器
Tomcat组件梳理-Bootstrap启动器 一开始是直接从Server开始做梳理的,但是发现有很多东西是从Catalina传输过来的,Catalina又是从Bootstrap启动的,所以还是回过头 ...
- web服务器专题:tomcat(二)模块组件与server.xml 配置文件
web服务器专题:tomcat(二)模块组件与server.xml 配置文件 回顾: Web服务器专题:tomcat(一) 基础模块 一个Server.xml的实例 <?xml version= ...
- 【转】Tomcat组件生命周期管理
Tomcat组件生命周期管理 Tomcat中Server,Service,Connector,Engine,Host,Context,它们都实现了org.apache.catalina.Lifecyc ...
- Tomcat 组件介绍
用了好长时间tomcat,但是其实自己只是反复听了这个名字,对Tomcat并不了解 1.Tomcat组件 Catalina Coyote Jasper Cluster 2.组件介绍 Tomcat Co ...
- Tomcat系列(4)——Tomcat 组件及架构详细部分
核心部分 1. 定义 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta ...
- Tomcat系列(3)——Tomcat 组件及架构核心部分 4类主要组件(顶层,连接器,容器,嵌套)
1.架构图 2. 定义 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,Tomcat是Apache 软件基金会(Apache Software Foundation)的Jakarta ...
随机推荐
- ThreadPoolExecutor的坑
ExecutorService executorService = new ThreadPoolExecutor(0, MAX_THREAD_NUM, 60, TimeUnit.SECONDS, ne ...
- MySQL百万级数据分页查询及优化
方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...
- Java 字符集编码
一.字符编码实例1.NioTest13_In.txt文件内容拷贝到NioTest13_Out.txt文件中 public class NioTest13 { public static void ma ...
- python lambda表达式简单用法【转】
python lambda表达式简单用法 1.lambda是什么? 看个例子: g = lambda x:x+1 看一下执行的结果: g(1) >>>2 g(2) >>& ...
- Kibana数据可视化
Kibana数据可视化 1,3.1使用logstash导入数据的问题 会出现错误提示: [location] is defined as an object in mapping [doc] but ...
- Metasploit使用内网跳板, 扫描局域网主机
最近,拿到一台内网机器, 苦于无法使用nmap扫描改主机的内网, 所以才有此文 在跳板机子获取一定权限后,需要积极的向内网主机权限发展,获取指定的目标信息,探查系统漏洞,借助msf已经得到的meter ...
- easyui datagrid的行编辑器editor 如何实现新增时可修改,编辑时,不可修改
项目出现一个需求,要求用户界面的用户名,新增时,可自由输入,编辑时,不可修改 html页面 <table id="gridlist" data-bind="data ...
- Maven依赖包导入错误(IntelliJ IDEA):java.lang.OutOfMemoryError: GC overhead limit exceeded
一.问题背景 最近用IntelliJ IDEA 打开一个老应用,一直加载依赖不成功,主POM中存在如下错误. java.lang.OutOfMemoryError:GC overhead limit ...
- elementUI vue this.$confirm 和el-dialog 弹出框 移动
调试了好久, 还能凑合用, 请直接看DOME 示例,复制就能用: <!DOCTYPE html> <html lang="zh"> <head> ...
- 创建Observable序列
1. just()方法 该方法通过传入一个默认值来初始化 下面样例我们显示地标注出了observable的类型为Observable, 即指定了这个Observable所发出的事件携带的数据类型必须是 ...