zoukankan      html  css  js  c++  java
  • Tomcat专题(二)-----Tomcat源码、嵌入式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:会话管理;

    Server

    Server是Tomcat最顶层的容器,代表着整个服务器

    Tomcat中其标准实现: org.apache.catalina.core.StandardServer类

    StandardServer继承结构类图如下图: 除了实现Server接口还需要继承Lifecycle

    这里要强调一点,整个Tomcat中的设计方式,我们讲的都是一个抽象,抽象在源码中都是接口,具体的实现一般都是StandardXXXX之类的。

    Lifecycle这个概念重点讲下:像Tomcat这么大的系统,必要要对生命周期进行统一的管理,所以基本上大部分的组件都会去继承这个接口,Lifecycle把所有的启动、停止、关闭等都组织在了一起。
    补充点:MBeanRegistration这个类是完成JMX的功能,就是我们俗称的监控管理功能,之前我们讲的使用jconsole查看Tomcat也就是通过JMX玩的。

    好处:生命周期统一接口Lifecycle把所有的启 动、停止、关闭等都放在一起统一管理

    Service

    Service:一个Tomcat包含多个Service

    Tomcat中其标准实现: org.apache.catalina.core.StandardServic类

    StandardService继承结构类图如下图: 除了实现Service接口还需要继承Lifecycle

    好处:生命周期统一接口Lifecycle把所有的启 动、停止、关闭等都放在一起统一管理

    Service拓展出Tomcat原型

    Service中请求监听和请求处理分开为两个模块: Connector负责处理请求监听; Container负责处理请求处理;

    一个Service可以有多个Connector,但只能有 一个Container。

    任何容器都有启动start()和关闭stop()方法。

    Connector解析

    Connector使用ProtocolHandler来处理请求的。

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

    Endpoint用来处理底层Socket的网络连接。 Processor用于将Endpoint接收到的Socket封装 成Request。 Adapter充当适配器,用于将Request转换为 ServletRequest交给Container进行具体的处理。

    Container解析

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

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

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

    Wrapper:一个Servlet

    在理解下面的4个容器之间的关系:

    Tomcat启动流程

    Tomcat启动两大流程:init流程和start流程

    1、init流程:分别在Bootstrap、Catalina、StandardServer、StandardService 的init方法加入断点和输出日志

    2、start流程:分别在Bootstrap、Catalina、StandardServer、StandardService 的start方法加入断点和输出日志

    Lifecycle与模板方法模式

    模板方法就是为多种类似业务提供一个算法执行的统一框 架,把这些业务中共同的部分抽取出来进行具体实现,而 某些业务中特定的需求推迟到子类中进行重写实现

    案例:

    Tomcat的启动过程中Catalina调用StandardService中的start() 方法,但是StandardService自身没有start()方法!

    分析:

    原来StandardService继承了抽象类LifecycleBase,它有start() 并且它在此方法中调用了一个未实现的抽象方法startInternal() ,Catalina调用StandardService中的start()最后会调用至 startInternal()

    优点:

    这种模式使得StandardService等这些类抽出一个共同的start() 在LifecycleBase中进行实现(方便统一生命周期管理)。如果它 需进行特殊的业务处理的话可以在startInternal()中处理

    嵌入式Tomcat

    嵌入式Tomcat: 非传统的部署方式,将Tomcat嵌入到主程序中进行运行。

    优点: 灵活部署、任意指定位置、通过复杂的条件判断。

    发展趋势: Springboot默认集成的是Tomcat容器。

    Maven中Springboot引入Tomcat:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <scope>provided</scope>
    </dependency>    

    Maven集成Tomcat插件

    Tomcat 7 Maven插件:tomcat7-maven-plugin

    <dependency>
        <groupId>org.apache.tomcat.maven</groupId>
        <artifactId>tomcat7-maven-plugin</artifactId>
        <version>2.2</version>
    </dependency>

    插件运行 选择pom.xml文件,击右键——>选择 Run As——> Maven build 在Goals框加加入以下命令: tomcat7:run

    Maven集成Tomcat 插件启动Tomcat是常用的Tomcat嵌入式启动

    pom.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>tomcat-maven</groupId>
      <artifactId>tomcat-maven</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    
      <name>tomcat-maven</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <!-- 引入Maven的Tomcat -->
        <dependency>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
          </plugins>
        </pluginManagement>
      </build>
    </project>

    Maven集成Tomcat插件启动分析

    分析它的启动

    Tomcat7RunnerCli是引导类

    @SuppressWarnings( "static-access" )
    public class Tomcat7RunnerCli
    {
        public static void main( String[] args )
            throws Exception
        {
            .
            .
            .
            // here we go
            tomcat7Runner.run();
        }
    }

    进一步分析

    Tomcat7RunnerCli主要依靠Tomcat7Runner。重要代码如下:

    public class Tomcat7Runner
    {
        public void run() throws Exception {
            // start with a server.xml
            if ( serverXmlPath != null || useServerXml() )
            {
                container = new Catalina();
                container.setUseNaming( this.enableNaming() );
                if ( serverXmlPath != null && new File( serverXmlPath ).exists() )
                {
                    container.setConfig( serverXmlPath );
                }
                else
                {
                    container.setConfig( new File( extractDirectory, "conf/server.xml" ).getAbsolutePath() );
                }
                container.start();
            }
            else
            {
                tomcat = new Tomcat()
                {
                    public Context addWebapp( Host host, String url, String name, String path )
                    {
    
                        Context ctx = new StandardContext();
                        ctx.setName( name );
                        ctx.setPath( url );
                        ctx.setDocBase( path );
    
                        ContextConfig ctxCfg = new ContextConfig();
                        ctx.addLifecycleListener( ctxCfg );
    
                        ctxCfg.setDefaultWebXml( new File( extractDirectory, "conf/web.xml" ).getAbsolutePath() );
    
                        if ( host == null )
                        {
                            getHost().addChild( ctx );
                        }
                        else
                        {
                            host.addChild( ctx );
                        }
    
                        return ctx;
                    }
                };
    
    
                tomcat.getHost().setAppBase( new File( extractDirectory, "webapps" ).getAbsolutePath() );
    
                String connectorHttpProtocol = runtimeProperties.getProperty( HTTP_PROTOCOL_KEY );
    
                if ( httpProtocol != null && httpProtocol.trim().length() > 0 )
                {
                    connectorHttpProtocol = httpProtocol;
                }
    
                debugMessage( "use connectorHttpProtocol:" + connectorHttpProtocol );
    
                if ( httpPort > 0 )
                {
                    Connector connector = new Connector( connectorHttpProtocol );
                    connector.setPort( httpPort );
    
                    if ( httpsPort > 0 )
                    {
                        connector.setRedirectPort( httpsPort );
                    }
                    connector.setURIEncoding( uriEncoding );
    
                    tomcat.getService().addConnector( connector );
    
                    tomcat.setConnector( connector );
                }
    
                // add a default acces log valve
                AccessLogValve alv = new AccessLogValve();
                alv.setDirectory( new File( extractDirectory, "logs" ).getAbsolutePath() );
                alv.setPattern( runtimeProperties.getProperty( Tomcat7Runner.ACCESS_LOG_VALVE_FORMAT_KEY ) );
                tomcat.getHost().getPipeline().addValve( alv );
    
                // create https connector
                if ( httpsPort > 0 )
                {
                    Connector httpsConnector = new Connector( connectorHttpProtocol );
                    httpsConnector.setPort( httpsPort );
                    httpsConnector.setSecure( true );
                    httpsConnector.setProperty( "SSLEnabled", "true" );
                    httpsConnector.setProperty( "sslProtocol", "TLS" );
                    httpsConnector.setURIEncoding( uriEncoding );
                    .
                    .
                    .
                    tomcat.getService().addConnector( httpsConnector );
    
                    if ( httpPort <= 0 )
                    {
                        tomcat.setConnector( httpsConnector );
                    }
                }
    
                tomcat.start();
    
                Runtime.getRuntime().addShutdownHook( new TomcatShutdownHook() );
            }
    
            waitIndefinitely();
        }
    }

    分析结论

    原来嵌入式启动就是调用了Tomcat的API来实现的

    Tomcat API接口

    实现嵌入式Tomcat的基础:

    Tomcat本身提供了外部可以调用的API

    Tomcat类:

    1.位置:org.apache.catalina.startup.Tomcat

    2.该类是public的。

    3.该类有Server、Service、Engine、Connector、Host等属性。

    4.该类有init()、start()、stop()、destroy()等方法。

    分析结论: Tomcat类是外部调用的入口

    手写Tomcat思路分析

    分析

    Tomcat单独启动时的流程

    结论

    使用Tomcat的API来实现:

    1.新建一个Tomcat对象

    2.设置Tomcat的端口号

    3.设置Context目录

    4.添加Servlet容器

    5.调用Tomcat对象Start()

    6.强制Tomcat等待

    手写嵌入式Tomcat-----开发流程

    1.准备好一个简单的Servlet项目

    2.新建一个手写嵌入式Tomcat工程

    3.Tomcat工程中使用一个类完成手写嵌入式Tomcat的功能

    4.调用该类的main方法执行

    5.效果演示和分析

    代码:

    1、代码结构

    2、pom.xml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
    
      <groupId>tomcat-maven</groupId>
      <artifactId>tomcat-maven</artifactId>
      <version>0.0.1-SNAPSHOT</version>
    
      <name>tomcat-maven</name>
      <!-- FIXME change it to the project's website -->
      <url>http://www.example.com</url>
    
      <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
      </properties>
    
      <dependencies>
        <dependency>
          <groupId>junit</groupId>
          <artifactId>junit</artifactId>
          <version>4.11</version>
          <scope>test</scope>
        </dependency>
        <!-- 引入Maven的Tomcat -->
        <dependency>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
        </dependency>
      </dependencies>
    
      <build>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
          <plugins>
          </plugins>
        </pluginManagement>
      </build>
    </project>

    3、servlet

    public class DemoServlet extends HttpServlet {
    
        private static final long serialVersionUID = 1L;
    
        @Override
        public void doGet(HttpServletRequest request,
                          HttpServletResponse response)
            throws IOException, ServletException
        {
            response.setContentType("text/html");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
    
            out.println("<!DOCTYPE html><html>");
            out.println("<head>");
            out.println("<meta charset="UTF-8" />");
            out.println("<title></title>");
            out.println("</head>");
            out.println("<body bgcolor="white">");
            out.println("<h1>阿里-马云 V5!</h1>");
            out.println("</body>");
            out.println("</html>");
        }
    }

    4、EmbeddedTomcatServer-----服务器主类

    public class EmbeddedTomcatServer {
    
        public static void main(String[] args) throws Exception{
            //把目录的绝对的路径获取到
            String classpath = System.getProperty("user.dir");
            System.out.println(classpath);
            //D:workspace-tomcat	omcat-maven
            //我们new一个Tomcat
             Tomcat tomcat = new Tomcat();
             
             //设置Tomcat的端口tomcat.setPort(9091)。两种写法都可以设置端口
             Connector connector = tomcat.getConnector();
             connector.setPort(9091);
             //设置Host
             Host host = tomcat.getHost();
             //我们会根据xml配置文件来
             host.setName("localhost");
             host.setAppBase("webapps");
             //前面的那个步骤只是把Tomcat起起来了,但是没啥东西
             //要把class加载进来,把启动的工程加入进来了
             Context context =tomcat.addContext(host, "/", classpath);
    
             if(context instanceof StandardContext){
                 StandardContext standardContext = (StandardContext) context;
                 standardContext.setDefaultContextXml("E:/utils/Tomcat/apache-tomcat-8.0.53/conf/web.xml");
                 //我们要把Servlet设置进去
                 Wrapper wrapper =  tomcat.addServlet("/", "DemoServlet", new DemoServlet());
                 wrapper.addMapping("/king");
             }
             //Tomcat跑起来
             tomcat.start();
             //强制Tomcat server等待,避免main线程执行结束后关闭
             tomcat.getServer().await();
        }
    }

    5、运行主类,启动成功。

    浏览器中访问:http://localhost:9091/king 

  • 相关阅读:
    c++ 编译时检测结构体大小的的宏定义写法
    文本格式转换的网站,云转换
    chm格式文件,win7下用c:/windows/hh.exe打开
    visual paradigm 自动解析代码生成 UML图
    proxifiler 代理神器
    linux下设置 git ssh 代理
    一直出现 Enter passphrase for key '/root/.ssh/gitkey12.pub'
    connect-proxy rpm文件的安装
    [转] ssh免密码登录服务器
    [转] 公司局域网中代码访问 github.com
  • 原文地址:https://www.cnblogs.com/alimayun/p/12354704.html
Copyright © 2011-2022 走看看