Tomcat组件梳理—Service组件
Tomcat组件梳理—Service组件
1.组件定义
Tomcat中只有一个Server,一个Server可以用多个Service,一个Service可以有多个Connector和一个Container。
Server掌握着整个Tomcat的生死大权。
Service是对外提供服务的。一个Server可以有多个Service,只不过Cataina中只添加了一个,这一个就代表了Tomcat的所有服务。
Connector用于接收请求并将请求封装成Request和Response来具体处理
Container用于封装和管理Servlet,以及具体处理reqeust请求

如上图,一个Service包含多个Connector和一个Engine,两者的关联关系使用Mapper来做映射,还有一个可选的线程池Executor。
2.属性
先把Service的属性代码摆出来:
/**
* service的名称
*/
private String name = null;
/**
* Service所属的Server
*/
private Server server = null;
/**
* 组件对属性改变的支持
*/
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
/**
* 跟这个Service相关联的Connector集合
*/
protected Connector connectors[] = new Connector[0];
/**
* Connector的锁
*/
private final Object connectorsLock = new Object();
/**
* 线程池
*/
protected final ArrayList<Executor> executors = new ArrayList<>();
/**
* Servlet的引擎
*/
private Engine engine = null;
/**
* 类加载器
*/
private ClassLoader parentClassLoader = null;
/**
* Mapper.
*/
protected final Mapper mapper = new Mapper();
/**
* Mapper 监听器
*/
protected final MapperListener mapperListener = new MapperListener(this);
解释一下这里面关键的几个点:
Connector connectors[]:多个连接器,一个Servlet服务接受两个不同的协议连接,只不过不同的协议通过对应的Connector都被处理成了一个Request对象,这样对于Engine来说,都是一样的请求。
Engine engine:Servlet引擎,就是专门用来处理请求的,其他的都不管。
Mapper mapper:mapper保存了一个映射关系,不同请求路径对应哪一个Servlet的API。
PropertyChangeSupport support:JDK自带的观察者模式,主要是观察Java Bean对象的属性更改的,等会拿出来单独说。
在Service属性中,主要就是这四个东西,主要的架构关系,在上面的图中有解析,就不再多介绍。
3.动作
Service的方法比较简单,因为只是包装,自己没有太多的一个功能,所有主要功能有:1.监听Service属性变化,2.启动,3.关闭。没了,就这三个,其他的都是对属性的setter和getter的具体实现,就不管了。
3.1.启动
在Server组件的分析中,我们已经知道了Server会调用Service的init()方法和start()方法来完成启动操作,那我们分别来看一下Service组件的init()和start()。
首先是init()方法:
protected void initInternal() throws LifecycleException {
//1.父类执行init
super.initInternal();
//2.执行servlet的引擎engine的init
if (engine != null) {
engine.init();
}
// 3.执行Executors的init
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// 4.mapper监听器的init
mapperListener.init();
// 5.connect的init
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString(
"standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new LifecycleException(message);
}
}
}
}
}
好像没有太多可说的,自己没有做啥事情,就是调用自己的子组件的init()方法。主要包括Engine,Executor,Mapper,Connector这四个。
再看一个start()方法:
protected void startInternal() throws LifecycleException {
//1.设置生命周期的状态
setState(LifecycleState.STARTING);
//2.执行engine的start
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
//3.执行executor的start
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
//4.执行mapper监听器的start
mapperListener.start();
// 5.执行connect的start
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.startFailed",
connector), e);
}
}
}
}
同init()方法一样,没啥好说的。
3.2.关闭
Service的关闭操作主要调用两个方法,分别是stop(),destroy()。这两个代码里的业务逻辑跟启动里面的是一样的,就是去调用子组件里对应的方法。没啥好说的,不过还是把代码放一下看看。
首先是stop()方法
protected void stopInternal() throws LifecycleException {
//先暂停或者关闭Connector
synchronized (connectorsLock) {
for (Connector connector: connectors) {
try {
//暂停
connector.pause();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.pauseFailed",
connector), e);
}
//如果有绑定socket,就关闭掉
connector.getProtocolHandler().closeServerSocketGraceful();
}
}
setState(LifecycleState.STOPPING);
// 调用engine的stop()方法
if (engine != null) {
synchronized (engine) {
engine.stop();
}
}
// 调用Connector的stop()方法
synchronized (connectorsLock) {
for (Connector connector: connectors) {
if (!LifecycleState.STARTED.equals(
connector.getState())) {
// Connectors only need stopping if they are currently
// started. They may have failed to start or may have been
// stopped (e.g. via a JMX call)
continue;
}
try {
connector.stop();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.stopFailed",
connector), e);
}
}
}
// 调用mapperListener的stop()方法
if (mapperListener.getState() != LifecycleState.INITIALIZED) {
mapperListener.stop();
}
//调用executor的stop()方法
synchronized (executors) {
for (Executor executor: executors) {
executor.stop();
}
}
}
然后是destroy()方法
protected void destroyInternal() throws LifecycleException {
//1.调用mapper的destroy()方法
mapperListener.destroy();
//2.调用每个Connector的destroy()方法
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.destroy();
} catch (Exception e) {
log.error(sm.getString(
"standardService.connector.destroyFailed", connector), e);
}
}
}
//3.调用Executor的destroy()的方法
for (Executor executor : findExecutors()) {
executor.destroy();
}
//4.调用engine的destroy()方法
if (engine != null) {
engine.destroy();
}
super.destroyInternal();
}
如上,真的没啥好说的,就不说了。
3.3.监听Service属性变化
这里是一个比较有意思的操作,就是你怎么去监听一个java对象的属性被改变了?
我想大部分都有思路,使用监听器设计模式,是的。但是JDK已经提供好了这种使用方法,并在Tomcat里有了比较好的应用。这里只看Tomcat中是怎么用的。介绍会比较简单,详细的可以查看下面的 "参考文章"。
Tomcat中使用JDK中的PropertyChangeSupport类来实现监听的需求,使用该类需要按照如下要求:
1.在Service中构造一个PropertyChangeSupport类,并将这个Java Bean传入。
//org.apache.catalina.core.StandardService
/**
* 属性改变的监听管理
*/
protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
2.需要在Service中添加对应的添加监听器方法和删除监听器方法,对应如下:
/**
* Add a property change listener to this component.
* @param listener The listener to add
*/
public void addPropertyChangeListener(PropertyChangeListener listener) {
support.addPropertyChangeListener(listener);
}
/**
* Remove a property change listener from this component.
* @param listener The listener to remove
*/
public void removePropertyChangeListener(PropertyChangeListener listener) {
support.removePropertyChangeListener(listener);
}
3.最后,如果需要实现自己的监听器,只需要实现void propertyChange(PropertyChangeEvent evt);方法即可。
public interface PropertyChangeListener extends java.util.EventListener {
/**
* This method gets called when a bound property is changed.
* @param evt A PropertyChangeEvent object describing the event source
* and the property that has changed.
*/
void propertyChange(PropertyChangeEvent evt);
}
这样,就可以来完成对Service属性的监听了。非常好的方法,之前从来不知道。
5.总结
Service的动作不多,主要是对Connector和Engine的包装成一个组件,方便统一管理和映射。但是Service里面有一个监听Java Bean属性变化的使用还是挺有意思的。具体可以参考下面的文章
参考文章:
- 《Java架构师必读源码之Tomcat8》https://www.jianshu.com/p/2ca506449b90
- 《PropertyChangeSupport类》https://blog.csdn.net/todebug/article/details/1776324
Tomcat组件梳理—Service组件的更多相关文章
- Android成长日记-Android四大组件之Service组件的学习
1.什么是Service? Service是Android四大组件中与Activity最相似的组件,它们都代表可执行的程序,Service与Activity的区别在于:Service一直在后台运行,它 ...
- android4.4组件分析--service组件
6 Service 6.1 service介绍 6.1.1. 基本介绍 Service是Android四大组件之中的一个(其余的是activit ...
- android4.4组件分析--service组件-bindService源代码分析
6.1.1. bindService 由于有前面分析startService的代码实现过程,则对于bindService的代码分析就不用那么具体介绍,在介绍流程的同一时候更关注一些细节上的部分. ...
- Tomcat组件梳理—Digester的使用
Tomcat组件梳理-Digester的使用 再吐槽一下,本来以为可以不用再开一个篇章来梳理Digester了,但是发现在研究Service的创建时,还是对Digester的很多接口或者机制不熟悉,简 ...
- Tomcat组件梳理--Server
Tomcat组件梳理--Server 1.Server组件的定义和功能概述 定义: Server组件用于描述一个启动的Tomcat实例,一个Tocmat被启动,在操作系统中占用一个进程号,提供web服 ...
- Tomcat组件梳理--Catalina
Tomcat组件梳理--Catalina 1.定义和功能 Catalina是Tomcat的核心组件,是Servlet容器,Catalina包含了所有的容器组件,其他模块均为Catalina提供支撑.通 ...
- 1.Tomcat组件梳理—Bootstrap启动器
Tomcat组件梳理-Bootstrap启动器 一开始是直接从Server开始做梳理的,但是发现有很多东西是从Catalina传输过来的,Catalina又是从Bootstrap启动的,所以还是回过头 ...
- How tomcat works 读书笔记十四 服务器组件和服务组件
之前的项目还是有些问题的,例如 1 只能有一个连接器,只能处理http请求,无法添加另外一个连接器用来处理https. 2 对容器的关闭只能是粗暴的关闭Bootstrap. 服务器组件 org.apa ...
- Tomcat源码分析 (二)----- Tomcat整体架构及组件
前言 Tomcat的前身为Catalina,而Catalina又是一个轻量级的Servlet容器.在美国,catalina是一个很美的小岛.所以Tomcat作者的寓意可能是想把Tomcat设计成一个优 ...
随机推荐
- 【Beta】Scrum Meeting 7 & 与助教谈话
前言 Beta阶段第7次会议在5月12日22:00由PM在大运村一公寓三层召开, 时长30min. 任务分配 姓名 今日任务 明日任务 困难 周博闻 修复修改密码问题#54添加主页公告栏 #57(调整 ...
- 《微信小程序商城开发实战》唐磊,全网真实评价截图,不吹不黑,全部来自网友的真实评价
偶尔看了下网友的销量和评价,感觉还不错,因为市面上大多关于小程序的书籍可能写的不够全面,要么只是点到为止的大致罗列,要么就是只简单介绍一下小程序的 界面设计这块.这样很难给学习小程序开发的人一个完成的 ...
- Loadrunner查询博客列表并循环删除博客列表中的所有博客
Loadrunner查询博客列表并循环删除博客列表中的所有博客,在博客列表请求中使用关联,获取出列表中博客的数量,并找出博客列表请求的必要参数.关联使用Ordinal=All 找出所有匹配值 查找出所 ...
- 运维笔记--Debian/Ubuntu系统离线安装pymssql,连接SqlServer
场景描述: 开始之前,先对Debian和Ubuntu操作系统做个了解,两者都是Linux阵营中很有影响力的发行版本,可以简单理解成“Ubuntu源自Debian,两者系统操作命令基本相同,相比Ubun ...
- Spring+SpringMVC+Spring Data JPA完美整合
使用Maven实现SSS框架的整合. 方便记录,专门建了一个pom项目用来整合SSS框架所用的jar包 1.POM项目,作为父级项目,记录整个整合中的依赖jar包pom文件 <project x ...
- (原)理解码率控制模式(x264,x265,vpx)
理解码率控制模式(x264,x265,vpx) 原文链接:https://slhck.info/video/2017/03/01/rate-control.html 翻译:lihaiping1603@ ...
- 【翻译】Flink Table Api & SQL — Hive —— 读写 Hive 表
本文翻译自官网:Reading & Writing Hive Tables https://ci.apache.org/projects/flink/flink-docs-release-1 ...
- ejs不能读取js变量??????
一.问题描述 用express搭了一个nodejs服务端,为了测试接口数据是否能够正常输出,用ejs作为模版引擎的html文件写js发请求. 1.请求正常,能在network看到,但是没有输出cons ...
- javascript下載csv檔案
參考自: https://dotblogs.com.tw/shihgogo/2017/05/31/090831 function createCsvFile(){ var fileName = &qu ...
- SonarQube - 常用配置与操作
1 - SonarQube服务器中的数据库配置 2019年4月10号,SonarQube发文称在7.9之后,所有的SonarQube的版本(CE.DE.EE和DCE)中将停止对MySQL的支持. 建议 ...