浅读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. stressapptest工具

    1.在H桌面V7B04上运行stressapptest工具编译报错.请研发协助! 2.因为stressapptest工具在兆芯和龙芯都能正常运行,所以我怀疑是工具stressapptest未在H桌面V ...

  2. python中的threading模块使用说明

    这段时间使用python做串口的底层库,用到了多线程,对这部分做一下总结.实际用完了后再回过头去看python的官方帮助文档,感觉受益匪浅,把里面的自己觉得有用的一些关键点翻译出来,留待后续查验.th ...

  3. STM32串口编程易错点

    注意   串口发送函数 使用STM官方的LIB 中的库函数发送之后 加一点延时   否则会错误  接收数据不正常 正确做法是  加上等待发送完成

  4. JQuery 基础之基本选择器

    1.什么是jQuery选择器: jQuery选择器继承了CSS与Path语言的部分语法,允许通过标签名.属性名或内容对DOM元素进行快速.准确的选择,而不必担心浏览器的兼容性,通过jQuery选择器对 ...

  5. unity中UI坐标转3d世界坐标

    方法: public static Vector3 UIScreenToWorldPoint(Vector3 uiPostion) { uiPostion = UICamera.mainCamera. ...

  6. Hadoop 基石HDFS 一文了解文件存储系统

    @ 目录 前言:浅谈Hadoop Hadoop的发展历程 1.1 Hadoop产生背景 1.引入HDFS设计 1.1 HDFS主要特性 2.HDFS体系结构 HDFS工作流程机制 1.各个节点是如何互 ...

  7. DHCP与DHCP中继

    DHCP原理与配置 1. DHCP应用场景 2. DHCP报文类型 3. DHCP工作原理 4. IP地址获取与释放 5. DHCP中继配置 1. DHCP应用场景 在大型企业网络中,会有大量的主机或 ...

  8. Redis 内存大小限制+键值淘汰策略配置

    限制最大内存 windows 的 maxmemory-policy 策略可能会少一些 # 指定 Redis 最大内存限制,Redis 在启动时会把数据加载到内存中,达到最大内存后,Redis 会先尝试 ...

  9. checkbox,select,radio 选取值,设定值,回显值

    获取一组radio被选中项的值var item = $('input[@name=items][@checked]').val();获取select被选中项的文本var item = $(" ...

  10. Django(50)drf异常模块源码分析

    异常模块源码入口 APIView类中dispatch方法中的:response = self.handle_exception(exc) 源码分析 我们点击handle_exception跳转,查看该 ...