号外:2016 最流行的是哪一种 Java 应用服务器呢?

通过从部署的 1240 个 JVM 中得到的数据,我们能够确定出现了 862 个容器供应商,或者说是占到了运行环境的 70% 左右。这些容器的供应商分布如下:

Tomcat 的安装基数已经连续两年超过排行榜的 50%。占到总份额的 58.22% 无疑使其成为赢家。

免费、开源、跨平台的Tomcat无疑是我们开始学习Java EE的第一个服务器,会用对于日常开发完全够用了,但是还是要学一下Tomcat相关的原理和设计思想等,对于以后相关的扩展和优化有着重要的作用。下边是学习Tomcat,翻看Tomcat源码的一些总结和感悟,作为笔记查看。

1、Tomcat服务器顶层结构

俗话说,站在巨人的肩膀上看世界,一般学习的时候也是先总览一下整体,然后逐个部分个个击破,最后形成思路,了解具体细节。Tomcat 的结构很复杂,但是 Tomcat 也非常的模块化,找到了 Tomcat 最核心的模块,问题才可以游刃而解。先上一张Tomcat的顶层结构图,如下:

Tomcat中最顶层的容器是Server,代表着整个服务器,从上图中可以看出,一个Server可以包含至少一个Service,用于具体提供服务。

Service主要包含两个部分:Connector和Container。从上图中可以看出 Tomcat 的心脏就是这两个组件,他们的作用如下:

  1. Connector用于处理链接相关的事情,并提供Socket与Request和Response相关的转化;

  2. Container用于封装和管理Servlet,以及具体处理Request请求;

一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但是可以有多个Connectors,这是因为一个服务可以有多个连接,如同时提供http和https链接,也可以提供向相同协议不同端口的连接。

多个 Connector 和一个 Container 就形成了一个 Service,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命、掌握其生死大权,那就非 Server 莫属了。所以整个 Tomcat 的生命周期由 Server 控制。

另外,上述的包含关系或者说是父子关系,都可以在tomcat的conf目录下的server.xml配置文件中看出,下图是删除了注释内容之后的一个完整的server.xml配置文件(Tomcat版本为8.0)

详细的配置文件文件内容可以到Tomcat官网查看:http://tomcat.apache.org/tomcat-8.0-doc/index.html

上边的配置文件,还可以通过下边的一张结构图更清楚的理解:

Server标签设置的端口号为8005,shutdown=”SHUTDOWN” ,表示在8005端口监听“SHUTDOWN”命令,乳沟接收到了就会关闭Tomcat。一个Server有一个Service,当然还可以进行配置,一个Service有多个,Service左边的内容都属于Container的,Service下边是Connector。

总结:

Tomcat中只有一个Server,一个Server可以有多个Service,一个Service可以有多个Connector和一个Container。

2、Tomcat的启动过程

首先看一张Tomcat启动的时序图,如下:

Tomcat的启动入口main方法是在Bootstrap类里,但具体的执行过程是在Catalina里边,这样做可以使得把启动的入口和具体的管理类进行分开,从而可以方便的创建多种启动的方式。

Catalina是整个Tomcat的管理类,他有三个方法load、start、stop分别用来管理整个服务器的生命周期。load方法用于加载tomcat/conf目录下的server.xml配置文件,用来创建Server并调用Server的init方法进行初始化操作,start用于启动服务器器,stop用于停止服务器,start和stop方法在内部分别调用Server的start和stop方法,load方法在内部调用了 Server的init方法,这三个方法都会按层次分逐层调用相应的方法。

从上述的时序图,按着从上到下,从左到右的顺序,完全可以了解整个Tomcat的启动顺序。

上图中看到了几个陌生的名字,下边具体说明。

2.1、Bootstrap的启动过程

Bootstrap作为Tomcat的启动入口,其main方法如下:

public static void main(String args[]) {

//首先判断Bootstrap daemon 是否为空,就是创建一个Bootstrap实例daemon

if (daemon == null) { // Don't set daemon until init() has completed

Bootstrap bootstrap = new Bootstrap();

try {

bootstrap.init(); //初始化ClassLoader,并用ClassLoader创建了一个Catalina实例,并将这个实例赋值给了cataLinaDaemon

} catch (Throwable t) {

handleThrowable(t);

t.printStackTrace();

return;

}

daemon = bootstrap;

} else {

// When running as a service the call to stop will be on a new

// thread so make sure the correct class loader is used to prevent

// a range of class not found exceptions.

Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);

}

try {

String command = "start"; //指定默认的执行指令是start

if (args.length > 0) {

command = args[args.length - 1];

}

if (command.equals("startd")) {

args[args.length - 1] = "start";

daemon.load(args); //BootStrap的load方法,其实是调用Calatina的load方法

daemon.start();

} else if (command.equals("stopd")) {

args[args.length - 1] = "stop";

daemon.stop();

} else if (command.equals("start")) {

daemon.setAwait(true); //setWait方法 主要是为了设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入

daemon.load(args); //load方法用于加载配置文件,创建并初始化Server

daemon.start(); //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 t) {

// Unwrap the Exception for clearer error reporting

if (t instanceof InvocationTargetException &&

t.getCause() != null) {

t = t.getCause();

}

handleThrowable(t);

t.printStackTrace();

System.exit(1);

}

}

(1)上述执行的bootstrap.init()方法源代码如下:

(2)main方法里边,首先创建一个Bootstrap,并执行init方法进行初始化,然后出来main方法里传入的参数,如果参数为空,默认为start。

(3)在init方法里初始化ClassLoader,并用ClassLoader创建了Catalina实例,然后赋值给catalinaDaemon变量,后边对命令的操作都要使用catalinaDaemon来具体的执行;

(4)在start命令的处理调用的时候有三个方法:setAwait、load

、start。这三个方法在内部调用了Clatalina的相应的方法进行具体的执行,只不过是用反射来调用的。

2.2、Catalina的启动过程

Catalina的启动主要调用setAwait、load和start方法来实现,setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start用于启动服务器。

Catalina的load方法根据con/server.xml文件进行创建对象的,并赋值给server属性。

2.3、Server的启动过程

Server是一个接口,继承自Lifecycle ,接口定义如下:

public interface Server extends Lifecycle {}1212

看接口的结构图可以看出,其中包含了addService、findService、removeService三个主要的方法,用来增加、查找、删除Service。Server的init方法和start方法分别循环调用了每个Service方init方法和start方法来启动所有的Service。

Server 的默认实现是:org.apache.catalina.core.StandardServer,继承关系图如下:

StandardServer的initInternal和startInternal方法分别循环调用了每一个Service的start和init方法。

2.4、Service的启动过程

Service是一个接口,继承自Lifecycle ,接口定义如下:

public interface Service extends Lifecycle {}1212

Service 接口定义的方法和属性如下:

Service的默认实现是:org.apache.catalina.core.StandardService。StandardService和StandardServer有相似的继承关系,如下图:

StandardService的initInternal方法如下:

@Override

protected void initInternal() throws LifecycleException {

super.initInternal();

if (engine != null) {

engine.init();

}

// Initialize any Executors

for (Executor executor : findExecutors()) {

if (executor instanceof JmxEnabled) {

((JmxEnabled) executor).setDomain(getDomain());

}

executor.init();

}

// Initialize mapper listener

mapperListener.init();

// Initialize our defined Connectors

synchronized (connectorsLock) {

for (Connector connector : connectors) {

connector.init();

}

}

}

StandardService的startInternal方法如下:

@Override

protected void startInternal() throws LifecycleException {

if(log.isInfoEnabled())

log.info(sm.getString("standardService.start.name", this.name));

setState(LifecycleState.STARTING); // Start our defined Container first

if (engine != null) {

synchronized (engine) {

engine.start();

}

}

synchronized (executors) {

for (Executor executor: executors) {

executor.start();

}

}

mapperListener.start(); // Start our defined Connectors second

synchronized (connectorsLock) {

for (Connector connector: connectors) {

// If it has already failed, don't try and start it

if (connector.getState() != LifecycleState.FAILED) {

connector.start();

}

}

}

}

可以看出,StandardService的initInternal和startInternal方法主要调用container、executors、mapperListener、connectors的init和start方法。

mapperListener是Mapper的监听器,用来监听Container容器的变化;executors是用在connectors中管理线程的线程池,在server.xml文件中可以看到,默认是注释的:

3、总结

上述的几小节,大致记录了一下Tomcat的整体结构,在上边介绍的时候,也有一些没有写到的东西,比如说Tomcat生命周期管理的接口Lifecycle、Connector、Container以及Tomcat如何进行通信,如何解析和处理具体的Http请求,这些会在以后的学习中不断记录下来。

Tomcat服务器顶层结构和启动过程【转】的更多相关文章

  1. Tomcat结构、启动过程、关键组件简单分析

                            Tomcat   结构:   Tomcat最顶层容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务,Serv ...

  2. tomcat 解析(五)-Tomcat的核心组成和启动过程

    声明:源码版本为Tomcat 6.0.35 前面的文章中介绍了Tomcat的基本配置,每个配置项也基本上对应了Tomcat的组件结构,如果要用一张图来形象展现一下Tomcat组成的话,整个Tomcat ...

  3. 对于Tomcat服务器环境变量和启动配置的一点补充

    我们之前第一次使用Tomcat服务器运行jsp应用时,曾经给Tomcat配置过一个环境变量CATALINA_HOME,这个变量指定了Tomcat的安装位置,对于多个开发项目,我们一般会释放多个tomc ...

  4. Windows Phone-框架结构和启动过程

    上一篇文章介绍了Windows Phone的开发环境和一个简单的Windows Phone程序的演示和结构,这一篇文章要深入一点,介绍Windows Phone的框架结构和程序启动的过程. 一 Win ...

  5. Bootloader的结构和启动过程

    CPU上电后,会在某个地址开始执行,比如MIPS结构的CPU会从0xBFC00000取第一条指令,而ARM结构的CPU则从0x00000000开始,嵌入式开发板中,需要把存储器件ROM或Flash等映 ...

  6. 2018.4.1 Ubuntu16.04 下配置Tomcat服务器以及设置dingshi启动

    Tomcat自启动的设置技巧 以root用户登录系统: cd /etc/rc.d/init.d/ vi tomcat #!/bin/sh # # tomcat: Start/Stop/Restart ...

  7. Windows系统中IIS 6.0+Tomcat服务器环境的整合配置过程

    IIS6.0+Tomcat整合 1.首先准备工作 Windows IIS 6.0 apache-tomcat-7.0.26.exe tomcat-connectors-1.2.33-windows-i ...

  8. tomcat源码之connector启动过程

    connector源码部分 构造函数 生命周期启动 启动endPoint 启动accepter 线程执行方法 SocketProcessor启动

  9. 在Mac上关于tomcat服务器的安装、配置、启动、部署web详细流程

    之前在Mac上通过安装mamp来搭建PHP环境服务器,但是对于java来说,目前还是没有找到类似mamp这样强大的软件来构建及管理java环境服务器,所以目前也是通过命令行来进行tomcat服务器的安 ...

随机推荐

  1. sql语句的学习(2)

    7.统计:学号.姓名.语文.数学.英语.总分.平均成绩 8.列出各门课程的平均成绩.课程,平均成绩 9.列出数学成绩的排名(要求显示字段:学号,姓名,成绩,排名) 10.列出数学成绩在2-3名的学生( ...

  2. ZZNU 1988: Sn

    题目描述 给你两个数 n, p(0 < n,p <= 10^15); a1 = 1;  a2 = 1+2;  a3 = 1+2+3;  ... an = 1+2+3+...+n    Sn ...

  3. HDU 5758 Explorer Bo

    思维,树形$dp$. 首先选择一个度不为$0$的节点作为根节点,将树无根转有根. 这题的突破口就是要求瞬间移动的次数最少. 次数最少,必然是一个叶子节点走到另一个叶子节点,然后瞬间移动一次,再从一个叶 ...

  4. mysqldump 数据库备份命令及脚本

    mysql -uroot -phowhy@123 -e 'show databases;' | grep -ivE 'waring|database|mysql'|sed -r "s#^(. ...

  5. JavaScript详解

    JavaScript可以说是web开发中必备的一种技术.它具有灵活,简单,高效等特点.这次DRP中大量的用到了js,让自己对js有了更深的了解.看完这个以后还回去看了一下牛腩的js视频.把以前没看的看 ...

  6. 分布式日志收集系统Apache Flume的设计详细介绍

    问题导读: 1.Flume传输的数据的基本单位是是什么? 2.Event是什么,流向是怎么样的? 3.Source:完成对日志数据的收集,分成什么打入Channel中? 4.Channel的作用是什么 ...

  7. Codeforces Round #367 (Div. 2) C. Hard problem

    题目链接:Codeforces Round #367 (Div. 2) C. Hard problem 题意: 给你一些字符串,字符串可以倒置,如果要倒置,就会消耗vi的能量,问你花最少的能量将这些字 ...

  8. MySQL字符串类型

    VARCHAR类型用于存储变长字符串,它会删除末尾的所有空格,它比定长字符串更省空间,因为它仅使用必要的空间(越短的字符串占用越少),VARCHAR会用1或2个额外字节记录字符串长度(如果字符串长度不 ...

  9. 记2016商大ACM省赛

    比赛前三天才得到省赛的非正式参赛名额,总有点哭笑不得,笑的是是我的终究是我的,跑不掉…… 哭的是现在就剩三天了,虽然最近也一直在参加训练赛,但一直是断断续续的,对自己现在的水平并没有太大的信心…… 虽 ...

  10. chapter 13_2 关系类、库定义的元方法

    元表还可以指定关系操作符的含义,元方法为__eq ,__lt(小于) ,__le(小于等于). 而其它3个关系操作符则没有单独的元方法,Lua会 把a ~= b 转化为not(a == b) 将a&g ...