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的更多相关文章

  1. Tomcat组件梳理—Service组件

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

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

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

  3. Tomcat组件梳理--Catalina

    Tomcat组件梳理--Catalina 1.定义和功能 Catalina是Tomcat的核心组件,是Servlet容器,Catalina包含了所有的容器组件,其他模块均为Catalina提供支撑.通 ...

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

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

  5. web服务器专题:tomcat(二)模块组件与server.xml 配置文件

    web服务器专题:tomcat(二)模块组件与server.xml 配置文件 回顾: Web服务器专题:tomcat(一) 基础模块 一个Server.xml的实例 <?xml version= ...

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

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

  7. Tomcat 组件介绍

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

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

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

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

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

随机推荐

  1. ThreadPoolExecutor的坑

    ExecutorService executorService = new ThreadPoolExecutor(0, MAX_THREAD_NUM, 60, TimeUnit.SECONDS, ne ...

  2. MySQL百万级数据分页查询及优化

    方法1: 直接使用数据库提供的SQL语句 语句样式: MySQL中,可用如下方法: SELECT * FROM 表名称 LIMIT M,N 适应场景: 适用于数据量较少的情况(元组百/千级) 原因/缺 ...

  3. Java 字符集编码

    一.字符编码实例1.NioTest13_In.txt文件内容拷贝到NioTest13_Out.txt文件中 public class NioTest13 { public static void ma ...

  4. python lambda表达式简单用法【转】

    python lambda表达式简单用法 1.lambda是什么? 看个例子: g = lambda x:x+1 看一下执行的结果: g(1) >>>2 g(2) >>& ...

  5. Kibana数据可视化

    Kibana数据可视化 1,3.1使用logstash导入数据的问题 会出现错误提示: [location] is defined as an object in mapping [doc] but ...

  6. Metasploit使用内网跳板, 扫描局域网主机

    最近,拿到一台内网机器, 苦于无法使用nmap扫描改主机的内网, 所以才有此文 在跳板机子获取一定权限后,需要积极的向内网主机权限发展,获取指定的目标信息,探查系统漏洞,借助msf已经得到的meter ...

  7. easyui datagrid的行编辑器editor 如何实现新增时可修改,编辑时,不可修改

    项目出现一个需求,要求用户界面的用户名,新增时,可自由输入,编辑时,不可修改 html页面 <table id="gridlist" data-bind="data ...

  8. Maven依赖包导入错误(IntelliJ IDEA):java.lang.OutOfMemoryError: GC overhead limit exceeded

    一.问题背景 最近用IntelliJ IDEA 打开一个老应用,一直加载依赖不成功,主POM中存在如下错误. java.lang.OutOfMemoryError:GC overhead limit ...

  9. elementUI vue this.$confirm 和el-dialog 弹出框 移动

    调试了好久, 还能凑合用, 请直接看DOME 示例,复制就能用: <!DOCTYPE html> <html lang="zh"> <head> ...

  10. 创建Observable序列

    1. just()方法 该方法通过传入一个默认值来初始化 下面样例我们显示地标注出了observable的类型为Observable, 即指定了这个Observable所发出的事件携带的数据类型必须是 ...