vlambda博客
学习文章列表

手写tomacat ,tomcat底层分析


手写tomacat ,tomcat底层分析


本篇介绍:Tomcat底层架构,tomcat处理请求流程(http tcp socket)

Tomcat自定义类加载器-springboot怎么结合tomcat

Tomcat是一个servlet容器

 

 

tomcat简单介绍

public class YuxiHttpServlet extends HttpServlet{

//这个方法实在tomcat内部执行的

@override

protected void doGet(HttpServletRequest req ,HttpServletResponse resp)throws IOException{

resp.getWriter.println(“yuxi http”);

}

}

那么谁来实现HttpServletRequest呢

答案肯定是tomcat

Class Tomcat{

Connector connector;

List<Servlet>servlets;

}

 

public class YuxiHttpServlet extends HttpServlet{

@override

protected void doGet(HttpServletRequest req ,HttpServletResponse resp)throws IOException{

RequestFacade requestFacade=(RequestFacade )req;//

requestFacade.getsession();

这个类是属于tomcat的

resp.getWriter.println(“yuxi http”);

}

}

我们把war包放在tomcat里,tomcat有一个bootstrap类

我们知道serclet对象都会实现一些接口,我们上边的RequestFacade这个类就实现了HttpServletRequest接口

看这个类我们想到了哪个设计模式

门面模式(外观)。

进入RequestFacade.getsession();我们搜一下tomcat源码。只是在调用request,getSession()方法。

Tomcat处理请求的过程先解压war 然后去找自定义的一些servlet  然后再去调用doget()方法。

在源码里我们找到HostConfig

Protected void  deployApps(){

File appBase=appBase();

File configBase=configBase();

String [] filteredAppPaths=filterAppPaths(appBase.list());

deployDescriptors(configBase,configBase.list());

//war部署

DeployWars(appBase,filteredAppPaths);

//文件夹部署

DepolyDirectories(appBase,filteredAppPaths);

}

 手写tomacat ,tomcat底层分析

 

另外tomcat是使用异步多线程的方式部署应用的

我们看servlet.xml的时候 可以定义一个context节点,通过这种方式部署应用。

这种方式和war包或者文件夹部署方式是一样的。

了解下container

 

有四个类继承了container

Servlet

Context 代表应用 存储servlet

Host   代表主机(虚拟) localhost 应用是属于虚拟主机下的。

Engine  集群  tomcat定义的是虚拟主机,也是servlet应用。

如果项目很多,可以用engine

Wrapper  一个Wrapper代表一个servlet类

如果你写的servlet 实现了SingleThreadModel 意味着每个请求线程有一个servlet实例。

每个容器里都有一些管道 pipeline

Tomcat最终是生成一个request对象,然后交给servlet进行处理。

怎么把request对象交给servlet呢?

Loadservlet方法。

 

Tcp协议到底是由谁来实现的呢?

操作系统

http协议谁实现的?

浏览器

生成数据,建立tcp连接(操作系统提供一些接口)  发送数据

A服务器和B服务器通讯,首先A服务器向B服务器发送一个syn包

B服务器返回syn_ack

A再发给B一个ack

 手写tomacat ,tomcat底层分析

 

Tcp_connect() 是操作系统的一个方法。

这个方法会让第三方调用?

定义一个接口(软硬件级别) 看socket.c 文件。

 

然后tomcat调用socket.getInputStream() 获取应用数据(Bio nio)

 

取到数据如何解析?

 手写tomacat ,tomcat底层分析

 

建议使用nio

手写tomacat ,tomcat底层分析

 

tomcat怎么使用类加载器?

 


 

 

如何自定义类加载器?

因为系统的ClassLoader只会加载指定目录下的class文件,如果你想加载自己的class文件,那么就可以自定义一个ClassLoader.

而且我们可以根据自己的需求,对class文件进行加密和解密。

新建一个类继承自java.lang.ClassLoader,重写它的findClass方法

将class字节码数组转换为Class类的实例

 public class MyClassLoader extends ClassLoader {

     //指定路径

     private String path ;

   

  

      public MyClassLoader(String classPath){

         path=classPath;

     }

   

      /**

       * 重写findClass方法

      * @param name 是我们这个类的全路径

      * @return

       * @throws ClassNotFoundException

      */

      @Override

     protected Class<?> findClass(String name) throws ClassNotFoundException {

         Class log = null;

         // 获取该class文件字节码数组

         byte[] classData = getData();

   

         if (classData != null) {

            // 将class的字节码数组转换成Class类的实例

              log = defineClass(name, classData, 0, classData.length);

         }

          return log;

      }

   

     /**

       * 将class文件转化为字节码数组

       * @return

       */

      private byte[] getData() {

   

         File file = new File(path);

          if (file.exists()){

             FileInputStream in = null;

              ByteArrayOutputStream out = null;

              try {

                 in = new FileInputStream(file);

                 out = new ByteArrayOutputStream();

   

                  byte[] buffer = new byte[1024];

                 int size = 0;

                  while ((size = in.read(buffer)) != -1) {

                     out.write(buffer, 0, size);

                  }

  

             } catch (IOException e) {

                  e.printStackTrace();

              } finally {

                 try {

                     in.close();

                  } catch (IOException e) {

   

                     e.printStackTrace();

                 }

             }

              return out.toByteArray();

         }else{

              return null;

          }

   

   

      }

  }

怎么让tomcat使用我自定义的类加载器

tomcat 用的是 WebappClassLoader 加载 app的,不过没看到 server.xml 开放了这个接口。如果要替换,需要修改 tomcat 源码。

还有一个更好的解决方案,用 java agent 来动态替换 class,这样就可以做到对class的加解密。

tomacat热加载是如何实现的

对于Java应用程序来说,热部署就是在运行时更新Java类文件。在基于Java的应用服务器实现热部署的过程中,类装入器扮演着重要的角色。大多数基于Java的应用服务器,包括EJB服务器和Servlet容器,都支持热部署。类装入器不能重新装入一个已经装入的类,但只要使用一个新的类装入器实例,就可以将类再次装入一个正在运行的应用程序。

热加载其实我们在开发过程中经常使用,例如我们使用Idea开发时,我们在设置页面可以进行设置,当修改文件时,我们可以选择不重启项目,选择重新加载此文件。而在Tomcat中也能设置,Tomcat默认情况下是不开启热加载的。需要在Tomcat路径下的Context.xml中配置reloadable参数来开启这个功能。(context负责)

热部署Tomcat在启动的时候会将其目录下webapp中war包解压后然后封装为一个Context供外部访问。那么热部署就是在程序运行时,如果我们修改了War包中的东西。那么Tomcat就会删除之前的War包解压的文件夹,重新解压新的War包。(host负责)


热加载:

直接把项目web文件夹放在webapps里。

2、在tomcat\conf\server.xml中的<host></host>内部添加<context/>标签: <Context debug="0" docBase="D:\demo1\web" path="/demo1" privileged="true" reloadable="true"/>

 

第三种方式和第二种差不多,但是不是在Server.xml文件中添加Context标签,而是在 %tomcat_home%\conf\Catalina\localhost中添加一个XML文件 

 

所谓的热部署是指修改了一个类文件,类加载器只是重新加载这一个类文件,而不是重新加载所有的东西。

Tomcat 根本就不支持这样做

可通过加载其它插件让它支持或者

Classloader重写,通过自定义classloader加载相应的jsp编译后的class到JVM中。通过动态修改内存中的字节码,将修改过的class再次装载到JVM中。

 

 

而且热部署可能会带来的一个很严重后果,就是一个已经成熟的生产系统,因为一个2B程序员上传的一个不合格的模块,而导致整个系统瘫痪,这样的责任谁去承担,而且某些业务还正在进行中,在这期间损失的数据如何弥补回来?如果是冷部署,在运行状态下,不加载新模块,就可以有效避免这个问题。当然,你可以说,我通过充分的测试,这些模块值得信任,但是我想说,没有经过真正实际业务的模块,不能说值得信任,就算它真的值得信任,那么我想说的是,如果不停止服务而热部署的话,在部署期间,服务器仍旧有大量业务流动,那么新旧模块的体会,就很可能导致数据不准确与业务丢失。