浅读tomcat架构设计和tomcat启动过程(1)

  https://www.cnblogs.com/piaomiaohongchen/p/14977272.html

  浅读tomcat架构设计之tomcat生命周期(2)

  https://www.cnblogs.com/piaomiaohongchen/p/14982770.html

  Container是tomcat容器的接口,接口位置org.apache.catalina.Container

  Container一共有4个子接口,分别是:Engine,Host,Context,Wrapper和一个默认实现类ContainerBase:

  截图看下就会清晰很多:

    不知道为啥,截图后画质那么的马赛克...    

  每个子接口都是一个容器,这四个子容器都有一个对应的Standard***实现类:

    

  红线标出来的Standard***对应的是具体子容器的的实现类,并且这些子容器实现类都继承自ContainerBase类,以其中一个子容器实现类为例:

  

  而ContainerBase又继承自LifecycleMBeanBase

    

  所以四个子容器都是tomcat生命周期管理模式,看完子容器和子容器的接口实现类后,给我的感觉就是无限套娃

  下面讲讲四个子容器的作用含义,它们和我们的tomcat部署项目/发布项目息息相关,具有很强的关联性

    Engine:引擎,用于管理多个站点,一个Service最多只能有一个Engine

      Host:代表一个站点,也可以叫虚拟主机,通过配置host就可以添加站点  www.xxx.com就是Host,代表我们的站点,oa.xxx.com是我们的另外一个Host,他指向了另一个站点

    Context:代表一个应用程序,对应着我们开发的系统,或者是WEB-INF目录及下面的web.xml文件,通俗易懂点来说www.xxx.com/xxx,xxx应用就是Context

    Wrapper:每个Wrapper封装着一个Servlet  其实就是web.xml中配置的Servlet  

   4种容器的配置方法:

    Engine和Host的配置都在tomcat conf/server.xml中,server.xml是tomcat最重要的配置文件,tomcat大部分功能都可以在这个文件中配置

    

<Engine name="Catalina" defaultHost="localhost">

    

  我安装tomcat,默认Engine的设置是Catalina,默认的Host站点使用是localhost,这里可以自己设置成别的Host站点.

  Context有三种配置方法 (1)通过文件配置 (2)war包上传放到Host目录下,tomcat会自动查找自解压 (3)将应用的文件夹放在Host目录下,tomcat会自动查找并添加到Host中

  tocmat部署不想长篇大论讲了, tomcat部署参考:https://www.cnblogs.com/ysocean/p/6893446.html ,里面讲了好几种方法.

    Wrapper的配置就是我们web.xml中配置的Servlet,一个Servlet对应一个Wrapper.

  

  Container的启动是通过init和start方法来完成的,Container的四个子容器的共同父类都是ConatinerBase,ConatinerBase类定义了两个方法,分别是:startInternal和initInternal

  先聊聊ContainerBase类的这两个方法的具体实现:

    org.apache.catalina.core.ContainerBase:

protected void initInternal() throws LifecycleException {
BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue();
this.startStopExecutor = new ThreadPoolExecutor(this.getStartStopThreadsInternal(), this.getStartStopThreadsInternal(), 10L, TimeUnit.SECONDS, startStopQueue, new ContainerBase.StartStopThreadFactory(this.getName() + "-startStop-"));
this.startStopExecutor.allowCoreThreadTimeOut(true);
super.initInternal();
}

  initInternal方法主要是先是初始化,创建对象,然后创建一个线程池来管理启动和关闭内部线程

  ThreadPoolExecutor继承自Executor用于管理线程,一直往上回溯,会发现是Executor

  

  接着看startInternal方法:

protected synchronized void startInternal() throws LifecycleException {
this.logger = null;
this.getLogger();
Cluster cluster = this.getClusterInternal();
if (cluster != null && cluster instanceof Lifecycle) {
((Lifecycle)cluster).start();
} Realm realm = this.getRealmInternal();
if (realm != null && realm instanceof Lifecycle) {
((Lifecycle)realm).start();
} Container[] children = this.findChildren();
List<Future<Void>> results = new ArrayList(); for(int i = 0; i < children.length; ++i) {
results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[i])));
} boolean fail = false;
Iterator i$ = results.iterator(); while(i$.hasNext()) {
Future result = (Future)i$.next(); try {
result.get();
} catch (Exception var9) {
log.error(sm.getString("containerBase.threadedStartFailed"), var9);
fail = true;
}
} if (fail) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
} else {
if (this.pipeline instanceof Lifecycle) {
((Lifecycle)this.pipeline).start();
} this.setState(LifecycleState.STARTING);
this.threadStart();
}
}

    startInternal方法很长,拆分下代码讲解:

   

    Cluster cluster = this.getClusterInternal();
if (cluster != null && cluster instanceof Lifecycle) {
((Lifecycle)cluster).start();
} Realm realm = this.getRealmInternal();
if (realm != null && realm instanceof Lifecycle) {
((Lifecycle)realm).start();
}

  如果cluster和realm不为null,并且cluster/realm引用指向LIfecycle,就调用start方法

    继续往下拆分看:

        Container[] children = this.findChildren();
List<Future<Void>> results = new ArrayList(); for(int i = 0; i < children.length; ++i) {
results.add(this.startStopExecutor.submit(new ContainerBase.StartChild(children[i])));
}

  这部分代码是调用所有子容器的start方法来启动子容器,跟进ContainerBase.StartChild函数看看:

    

  继续往下拆分代码:

    

      if (fail) {
throw new LifecycleException(sm.getString("containerBase.threadedStartFailed"));
} else {
if (this.pipeline instanceof Lifecycle) {
((Lifecycle)this.pipeline).start();
} this.setState(LifecycleState.STARTING);
this.threadStart();
}

  这段代码大致就是调用管道中start方法,启动完成后将生命周期设置成LifecycleState.STARTING,最后启动后台线程:

  

  Engine:

    前面我们讲了,四大子容器的实现类是Standard***类.那么Engine的默认实现类是StandardEngine类:

    org.apache.catalina.core.StandardEngine:

   如果使用Engine就会调用Engine默认实现类StandardEngine的initInternal和startInternal方法:

   我们来看看:

    

  protected void initInternal() throws LifecycleException {
this.getRealm();
super.initInternal();
} protected synchronized void startInternal() throws LifecycleException {
if (log.isInfoEnabled()) {
log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo());
} super.startInternal();
}

 代码不是很多,发现他是调用他父类的方法,他的父类是ContainerBase,我们一开始就分析过ContainerBase类的initInternal和startInternal方法了.

   

  Host:

  Host接口的默认实现类是org.apache.catalina.core.StandardHost:

  跟进类,发现StandardHost没有重写父类的initInternal,只有startInternal方法被重写:

    如果他没有,初始化会默认会调用它父类的initInternal

  startInternal方法代码如下:

    

     protected synchronized void startInternal() throws LifecycleException {
String errorValve = this.getErrorReportValveClass();
if (errorValve != null && !errorValve.equals("")) {
try {
boolean found = false;
Valve[] valves = this.getPipeline().getValves();
Valve[] arr$ = valves;
int len$ = valves.length; for(int i$ = 0; i$ < len$; ++i$) {
Valve valve = arr$[i$];
if (errorValve.equals(valve.getClass().getName())) {
found = true;
break;
}
} if (!found) {
Valve valve = (Valve)Class.forName(errorValve).newInstance();
this.getPipeline().addValve(valve);
}
} catch (Throwable var8) {
ExceptionUtils.handleThrowable(var8);
log.error(sm.getString("standardHost.invalidErrorReportValveClass", new Object[]{errorValve}), var8);
}
} super.startInternal();
}

  代码也是好多一大块.  

    拆开一部分:

    截图的是重点,主要做的就是获取管道的值,然后判断管理里面是否有指定的Valve

    

  如果没找到管道值,就调用addValve方法,加入到管道中,代码如下:

  

      if (!found) {
Valve valve = (Valve)Class.forName(errorValve).newInstance();
this.getPipeline().addValve(valve);
}

  这是个重点,对我们后续讲解tomcat 内存马提供很大的帮助,valve的内存马注入是一定要调用addValue的,否则匹配不到!

  Host的启动除了调用StandardHost的startInternal方法,还会调用HostConfig中的一些方法:

    对应位置:org.apache.catalina.startup.HostConfig

    这个类主要作用就是Host站点的配置

    比较重要的就是部署应用了,deployApps方法,代码如下

  protected void deployApps() {
File appBase = this.host.getAppBaseFile();
File configBase = this.host.getConfigBaseFile();
String[] filteredAppPaths = this.filterAppPaths(appBase.list());
this.deployDescriptors(configBase, configBase.list());
this.deployWARs(appBase, filteredAppPaths);
this.deployDirectories(appBase, filteredAppPaths);
}

  上面介绍了三种方法来部署tomcat项目,

   (1)通过xml:

    

  (2):war包部署

  

 (3)文件夹部署:

   

  Context:

  Context的默认实现类是org.apache.catalina.core.StandardContext:

  startInternal方法如下:

    拆取比较重要部分,方法内容实在是太多了:    

      if (ok && !this.listenerStart()) {
log.error("Error listenerStart");
ok = false;
} if (ok) {
this.checkConstraintsForUncoveredMethods(this.findConstraints());
} try {
Manager manager = this.getManager();
if (manager != null && manager instanceof Lifecycle) {
((Lifecycle)manager).start();
}
} catch (Exception var19) {
log.error("Error manager.start()", var19);
ok = false;
} if (ok && !this.filterStart()) {
log.error("Error filterStart");
ok = false;
} if (ok && !this.loadOnStartup(this.findChildren())) {
log.error("Error loadOnStartup");
ok = false;
}

  主要含义就是调用了web.xml中定义的Listener,另外还初始化了filterStart和loadOnStartup

  filterStart:org.apache.catalina.core.StandardContext:

  

  使用filterConfigs存储键值对,前面简单看了hostConfig,filterConfig同理:

  他的实例类是:org.apache.catalina.core.ApplicationFilterConfig,等我讲Filter内存马的时候再详细解释它,这个先放一边.

  

  Wrapper:

    Wrapper是封装Servlet的包装器,Wrapper的默认实现类,没有重写initInternal,他默认调用父类的initInternal方法,startInternal方法代码如下:

    org.apache.catalina.core.StandardWrapper:

   protected synchronized void startInternal() throws LifecycleException {
Notification notification;
if (this.getObjectName() != null) {
notification = new Notification("j2ee.state.starting", this.getObjectName(), (long)(this.sequenceNumber++));
this.broadcaster.sendNotification(notification);
} super.startInternal();
this.setAvailable(0L);
if (this.getObjectName() != null) {
notification = new Notification("j2ee.state.running", this.getObjectName(), (long)(this.sequenceNumber++));
this.broadcaster.sendNotification(notification);
} }

  代码很简单,(1)使用broadcaster发送消息 (2)调用父类的startInternal (3)调用setAvailate方法,设置Servlet的有效时间

  

  

        

  

  

  

浅读tomcat架构设计之tomcat容器Container(3)的更多相关文章

  1. 浅读tomcat架构设计之tomcat生命周期(2)

    浅读tomcat架构设计和tomcat启动过程(1) https://www.cnblogs.com/piaomiaohongchen/p/14977272.html tomcat通过org.apac ...

  2. 浅读tomcat架构设计和tomcat启动过程(1)

    一图甚千言,这张图真的是耽搁我太多时间了: 下面的tomcat架构设计代码分析,和这张图息息相关. 使用maven搭建本次的环境,贴出pom.xml完整内容: <?xml version=&qu ...

  3. Tomcat详解系列(2) - 理解Tomcat架构设计

    Tomcat - 理解Tomcat架构设计 前文我们已经介绍了一个简单的Servlet容器是如何设计出来,我们就可以开始正式学习Tomcat了,在学习开始,我们有必要站在高点去看看Tomcat的架构设 ...

  4. Tomcat架构解析(一)-----Tomcat总体架构

    Tomcat是非常常用的应用服务器,了解Tomcat的总体架构以及实现细节,对于理解整个java web也是有非常大的帮助. 一.Server   1.最简单的服务器结构 最简单的服务器结构如图所示: ...

  5. Tomcat架构解析(五)-----Tomcat的类加载机制

    类加载器就是根据类的全限定名(例如com.ty.xxx.xxx)来获取此类的二进制字节流的代码模块,从而程序可以自己去获取到相关的类. 一.java中的类加载器   1.类加载器类别 java中的类加 ...

  6. 浅读tomcat架构设计之Pipeline-Valve管道(4)

    tomcat Container容器处理请求是使用Pipeline-Valve管道来处理的,后续写的tomcat内存马,和他紧密结合 Pipeline-Valve是责任链模式,责任链模式是指在一个请求 ...

  7. Tomcat 架构原理解析到架构设计借鉴

    Tomcat 发展这么多年,已经比较成熟稳定.在如今『追新求快』的时代,Tomcat 作为 Java Web 开发必备的工具似乎变成了『熟悉的陌生人』,难道说如今就没有必要深入学习它了么?学习它我们又 ...

  8. 理解Tomcat架构、启动流程及其性能优化

    PS:but, it's bullshit ! 备注:实话说,从文档上扒拉的,文档地址:在每一个Tomcat安装目录下,会有一个webapps文件夹,里面有一个docs文件夹,点击index.html ...

  9. tomcat架构分析-索引

    出处:http://gearever.iteye.com tomcat架构分析 (概览) tomcat架构分析 (容器类) tomcat架构分析 (valve机制) tomcat架构分析 (valve ...

随机推荐

  1. 创建用户(adduser和useradd)和删除用户(userdel)

    创建用户(adduser和useradd)和删除用户(userdel)及   一  用户创建命令: # adduser  用户名 # useradd  用户名 1) useradd 与 adduser ...

  2. php-round()四舍六入

    今天被问到了四舍六入的问题,好吧,第一次听说.后来查询之后说是银行家算法用的 摘自PHP官方文档.http://php.net/manual/zh/function.round.php (PHP 4, ...

  3. PHP相关session的知识

    由于http协议是一种无状态协议,所以没有办法在多个页面间保持一些信息.例如,用户的登录状态,不可能让用户每浏览一个页面登录一次.session就是为了解决一些需要在多页面间持久保持一种状态的机制.P ...

  4. C语言实现推箱子游戏完整代码

    C语言实现推箱子游戏完整代码 前言 自己做的,可能有些代码不够工整,或者有些小问题,但游戏的基本操作是可以实现的 代码效果 代码一共分为8个部分,4个控制上下左右移动,2个判断输赢,1个统计归为的个数 ...

  5. global nonlocal 闭包构建 变量名解析原则

  6. JS 反射机制及 Reflect 详解

    一.什么是反射机制 反射机制是在编译阶段不知道是哪个类被加载,而是在运行的时候才加载.执行. 也就是说,反射机制指的是程序在运行时能够获取自身的信息. js 中的 apply 就是反射机制. 二.Re ...

  7. Objective Evaluation Index of image

    图像质量客观评价指标 在做红外图像细节增强算法研究时,很重要一点就是要对经过算法处理的图像结果进行评价,分成两种评价方法.一种是视觉效果评价:主观的人眼观察,主要是通过观察者能否看到更多图像细节,给人 ...

  8. GO学习-(39) 优雅地关机或重启

    优雅地关机或重启 我们编写的Web项目部署之后,经常会因为需要进行配置变更或功能迭代而重启服务,单纯的kill -9 pid的方式会强制关闭进程,这样就会导致服务端当前正在处理的请求失败,那有没有更优 ...

  9. 在Go语言项目中使用Zap日志库

    在Go语言项目中使用Zap日志库 本文先介绍了Go语言原生的日志库的使用,然后详细介绍了非常流行的Uber开源的zap日志库,同时介绍了如何搭配Lumberjack实现日志的切割和归档. 在Go语言项 ...

  10. selenium模拟不同浏览器的方式

    from selenium import webdriver # 使用Firefox手机浏览器 user_agent = "Mozilla/5.0 (iPad; CPU OS 12_1 li ...