tomcat学习步骤,附带打破双亲委派模型企业应用…

2018-12-13 08:55:31来源:博客园 阅读 ()

新老客户大回馈,云服务器低至5折

1. tomcat入门

入门模块仅做学习大纲梳理,忽略了具体操作指引。

Tomcat的三种部署模式:

简单架构模型

 

连接器的非阻塞模式(NIO)

                                                                                                                                              通道(Channel)、缓冲区(Buffer)、选择器(Selector)

 

容器container的责任链模式:

1.请求被Connector组件接收,创建Request和Response对象。

2.Connector将Request和Response交给Container,先通过Engine的pipeline组件流经内部的每个Valve。

3.请求流转到Host的pipeline组件中,并且经过内部Valve的过。

4.请求流转到Context的pipeline组件中,并且经过内部的Valve的过滤。

5.请求流转到Wrapper的pipeline组件中,并且经过内部的Valve的过滤。

6.Wrapper内部的WrapperValve创建FilterChain实例,调用指定的Servlet实例处理请求。

7.返回。

 

 

2.tomcat架构进阶

 

 

tomcat顶层结构

 

 

 

 

Server:服务器的意思,代表整个tomcat服务器,一个tomcat只有一个Server;

Service:Server中的一个逻辑功能层, 一个Server可以包含多个Service;

Connector:称作连接器,是Service的核心组件之一,一个Service可以有多个Connector,主要是连接客户端请求;

Container:Service的另一个核心组件,按照层级有Engine,Host,Context,Wrapper四种,一个Service只有一个Engine,其主要作用是执

行业务逻辑;
Jasper:JSP引擎;
Session:会话管理;

 

Connector解析

 

 

 

Connector使用ProtocolHandler来处理请求的Endpoint用来处理底层Socket的网络连接。

Processor用于将Endpoint接收到的Socket封装成Request。

Adapter充当适配器,用于将Request转换为ServletRequest交给Container进行具体的处理。

ProtocolHandler由包含了三个部件:Endpoint、Processor,Adapter

Container解析

 

 

service中的name是catalina,engine中的name也是catalina,这个就证明一个service就只有一个engine, 一个engine可以有多个host主机

host,站点,虚拟主机。

Engine:引擎、只有一个定义了一个名为Catalina的Engine

Host:站点、虚拟主机一个Engine包含多个Host的设计,使得一个服务器实例可以承担多个域名的服务,是很灵活的设计

Context:一个应用默认配置下webapps下的每个目录都是一个应用

Wrapper:一个Servlet

 

结合tomcat目录结构理解上面的4个容器之间的关系

3.tomcat类加载机制

引用自:https://www.cnblogs.com/ghoster/p/7602158.html

 

1.主流的Java Web服务器,如Tomcat、Jetty、WebLogic、WebSphere等都实现了自己定义的类加载器(一般都不止一个)。因为一个功能健全的web服务器要解决一下几个问题:

    1)部署在一个服务器上的两个web应用程序所使用的Java类库可以实现互相隔离。这是最基本的需求,两个不同的应用程序可能会依赖同一个第三方类库的不同版本,不能要求一个类库在一个服务器中只有一份,服务器应当保证两个应用程序的类库可以互相独立使用

    2)部署在同一个服务器上的两个Web应用程序所使用的Java类库可以互相共享。这个需求也非常很常见,如,用户可能有10各使用Spring组织的应用程序部署在同一台服务器上,如果把10份Spring分别存放在各个应用程序的隔离目录中,将会是很大的资源浪费-这主要不是浪费磁盘空间的问题,而是指类库在使用时都要被加载到服务器内存,如果类库不能共享,虚拟机的方法区就会很容易出现过度膨胀的风险

    3)服务器需要尽可能地保证自身的安全不受部署的Web应用程序影响。目前,有许多主流的Java Web服务器自身也是使用Java语言来实现的。因此,服务器本身也有类库依赖的问题,一般来说,基于安全考虑,服务器所使用的类库应该与应用程序的类库相互独立

    4)支持jsp应用的Web服务器,大多数都需要支持HotSwap功能。我们知道,jsp文件最终要编译成Java Class才能由虚拟机执行,但jsp文件由于其纯文本存储的特性,运行时修改的概率远远大于第三方类库或程序自身的Class文件。而且ASP、PHP和JSP这些网页应用也把修改后无需重启作为一个很大的“优势”来看待,因此,“主流”的Web服务器都会支持JSP的热替换,当然也有“非主流”的,如运行在生产模式下的WebLogic服务器默认就不会处理JSP文件的变化

  由于存在上述问题,在部署Web应用时,单独的一个ClassPath就无法满足需求了,所以各种Web服务器都“不约而同”地提供了好几个ClassPath路径供用户存放第三方类库,这些路径一般都以lib或classes命名,被放置到不同路径中的类库,具备不同的访问范围和服务对象,通常,每一个目录都会有一个相应的自定义类加载器去加载放置在里面的Java类库。

  2.Tomcat对用户类库与类加载器的规划

    在其目录结构下有三组目录(“/common/*”、“/server/*”、“/shared/*”)可以存放Java类库,另外还可以加上Web应用程序本身的目录“/WEB-INF/*”,一共4组,把Java类库放置在这些目录中的含义分别如下:

    1)放置在/commom目录中:类库可被Tomcat和所有的Web应用程序共同使用

    2)放置在/server目录中:类库可被Tomcat使用,对所有的Web应用程序都不可见

    3)放置在/shared目录中:类库可被所有的Web应用程序所共同使用,但对Tomcat自己不可见

    4)放置在/WebApp/WEB-INF目录中:类库仅仅可以被此Web应用程序使用,对Tomcat和其他Web应用程序都不可见

    为了支持这套目录结构,并对目录里面的类库进行加载和隔离,Tomcat自定义了多个类加载器,这些类加载器按照经典的双亲委派模型来实现,所下图:

    

    最上面的三个类加载器是JDK默认提供的类加载器,这三个加载器的的作用之前也说过,这里不再赘述了,可以在http://www.cnblogs.com/ghoster/p/7594224.html简单了解。而CommonClassLoader、CatalinaClassLoader、SharedClassLoader和WebAppClassLoader则是Tomcat自己定义的类加载器,他们分别加载/common/*、/server/*、/shared/*和/WebApp/WEB-INF/*中的Java类库。其中WebApp类加载器和jsp类加载器通常会存在多个实例,每一个Web应用程序对应一个WebApp类加载器,每一个jsp文件对应一个Jsp类加载器

    从上图的委派关系可以看出,CommonClassLoader能加载的类都可以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader自己能加载的类则与对方相互隔离。WebAppClassLoader可以使用SharedClassLoader加载到的类,但各个WebAppClassLoader实例之间相互隔离。而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的哪一个Class,它出现的目的就是为了被丢弃:当服务器检测到JSP文件被修改时,会替换掉目前的JasperLoader的实例,并通过在建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能

 

4.打破双亲委派模型企业级应用实战

需求背景:在使用多数据源的情况下,同时连接某大厂魔改的数据库mppdb和pgsql数据库的时候。出现了无法同时加载mppdb和pgsql的驱动的问题。

 

原因分析:spring在启动时会调用应用类加载器加载org目录下的.class文件,同时spring自定义类加载器分别加载BOOT-INF,META-INF 目录下的文件,mppdb 与pgsql驱动的本质都是.jar文件,所以会放在META-INF目录下的lib目录下。

又因为mppdb是某国内大厂以pgsql为蓝本魔改的,遗留下一个问题,mppdb与mysql驱动的类名是完全一致的。而两驱动都放在lib目录下,由同一个类加载器来加载,就会出现以下情况:当第一个驱动pgsql加载时,使用双亲机制正常加载,

当第二个驱动mppdb再次加载时,会判断该类是否加载过,此时,由于类加载器一致,类名一致,类加载器会误认为mppdb驱动已经加载过了,实际是之前加载的pgsql驱动,所以spring不会再去加载mppdb,而是从缓冲中把pgsql的驱动抛出,

从而导致了,spring在启动时无法同时加载mppdn驱动和pgsql驱动。

 

解决方案:

方案一:遵循双亲委派模型

在spring容器启动后(此时spring已经加载到其中一个驱动,假设是pgsql驱动),新开一个jvm进程,在该进程中使用扩展类加载器加载mppdb驱动,根据双亲委派模型,扩展类加载器是应用类加载器的父类,当需要加载驱动时会优先委派父类加载,

所以同一个类在父类加载器和子类加载器都能加载的情况下,父类加载器总能优先加载的。这样就保证了,新起的进程总是能加载到扩展类加载器加载的mppdb驱动。

方案二:打破双亲委派模型

在spring容器启动后(此时spring已经加载到其中一个驱动,假设是pgsql驱动),不必新开进程,还是在该spring的进程内,使用应用类加载器XMLClassLoader去加载mppdb驱动,如果此时使用双亲委派模型,会加载到spring初始化时加载到的驱动,即pgsql驱动,

故而 需要将XMLClassLoader 的父加载器设置为null,禁止其父加载器加载,而由其自己加载,即可以成功加载mppdb驱动,具体实现涉及公司信息安全,在此仅提供问题解决思路。

 

 

 

 

 

 

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:面试题1

下一篇:ActiveMQ_4SpringBoot整合