zoukankan      html  css  js  c++  java
  • IO(七)

    计划一周时间,理解Tomcat的设计思想,为什么这么设计,以及Tomcat设计带来的特点是什么。

    今天是第一天,tomcat源码阅读。其实很奇怪的事情是什么,目前我平时的工作中,其实对Tomcat的使用很少,就是单纯自己去配置它。现在集成在SpringBoot中,好像只是单纯的作为一个工具在使用,但是我依然还是秉持着自己的复习路线。凡是日常开发中需要使用到的东西,至少要知道怎么用,以及它的设计原理。架构是一件很有艺术的事情,而这些Tomcat,Sping包,包括Java的核心包的设计,就是学习设计思想上的经典之作。

    怎么看源码,这其实是一个很细致的问题。像Tomcat这种,我自己不会选择同SpringBoot一样的看法。我会选择它重要的组件,去进行阅读。

      

     首先这张顶级架构图,可以看出我们主要需要看的几个部分。先看完组件,知道组件是为什么的,然后再看启动流程,知道他们之间是如何进行关联的。我一般是选择这种方式来进行源码的阅读。

    先看下Connector。在这个类的Doc上,有个很简短的一句话:"Implementation of a Coyote connector."翻译过来,这是一个土狼连接器。然后看下这个类的继承图

     看下这些都是什么作用。先从最顶层的Lifecycle是管理生命周期用的,Catalina组件可以实现这个接口用来管理自己的生命周期。

     

     根据Doc里面给的流程,差不多就是以下几个状态之间的转换,主要是auto是如何实现的。在代码中,定义了很多具体的常量

     剩余的代码:

     // --------------------------------------------------------- Public Methods
    
    
        /**
         * 将LifecycleEvent侦听器添加到此组件。
         * 侦听器的作用:在发生关联状态更改之后,将触发该侦听器。
         * 侦听器里面有方法 public void lifecycleEvent(LifecycleEvent event);
         * void反过来说就是侦听器里面大概率会有一个成员变量来暂存状态
         * @param listener The listener to add
         */
        public void addLifecycleListener(LifecycleListener listener);
    
    
        /**
         * 获取与此生命周期相关联的生命周期侦听器
         * @return An array containing the life cycle listeners associated with this
         *         life cycle. If this component has no listeners registered, a
         *         zero-length array is returned.
         */
        public LifecycleListener[] findLifecycleListeners();
    
    
        /**
         * Remove a LifecycleEvent listener from this component.
         *
         * @param listener The listener to remove
         */
        public void removeLifecycleListener(LifecycleListener listener);
    
    
        /**
         * 准备要启动的组件。此方法应执行对象创建后所需的任何初始化。
         * The following {@link LifecycleEvent}s will be fired in the following order:
         * <ol>
         *   <li>INIT_EVENT: On the successful completion of component initialization.</li>
         * </ol>
         *
         * @exception LifecycleException if this component detects a fatal error
         *  that prevents this component from being used
         */
        public void init() throws LifecycleException;
    
        /**
         * Prepare for the beginning of active use of the public methods other than
         * property getters/setters and life cycle methods of this component. This
         * method should be called before any of the public methods other than
         * property getters/setters and life cycle methods of this component are
         * utilized. The following {@link LifecycleEvent}s will be fired in the
         * following order:
         * <ol>
         *   <li>BEFORE_START_EVENT: At the beginning of the method. It is as this
         *                           point the state transitions to
         *                           {@link LifecycleState#STARTING_PREP}.</li>
         *   <li>START_EVENT: During the method once it is safe to call start() for
         *                    any child components. It is at this point that the
         *                    state transitions to {@link LifecycleState#STARTING}
         *                    and that the public methods other than property
         *                    getters/setters and life cycle methods may be
         *                    used.</li>
         *   <li>AFTER_START_EVENT: At the end of the method, immediately before it
         *                          returns. It is at this point that the state
         *                          transitions to {@link LifecycleState#STARTED}.
         *                          </li>
         * </ol>
         *
         * @exception LifecycleException if this component detects a fatal error
         *  that prevents this component from being used
         */
        public void start() throws LifecycleException;
    
    
        /**
         * Gracefully terminate the active use of the public methods other than
         * property getters/setters and life cycle methods of this component. Once
         * the STOP_EVENT is fired, the public methods other than property
         * getters/setters and life cycle methods should not be used. The following
         * {@link LifecycleEvent}s will be fired in the following order:
         * <ol>
         *   <li>BEFORE_STOP_EVENT: At the beginning of the method. It is at this
         *                          point that the state transitions to
         *                          {@link LifecycleState#STOPPING_PREP}.</li>
         *   <li>STOP_EVENT: During the method once it is safe to call stop() for
         *                   any child components. It is at this point that the
         *                   state transitions to {@link LifecycleState#STOPPING}
         *                   and that the public methods other than property
         *                   getters/setters and life cycle methods may no longer be
         *                   used.</li>
         *   <li>AFTER_STOP_EVENT: At the end of the method, immediately before it
         *                         returns. It is at this point that the state
         *                         transitions to {@link LifecycleState#STOPPED}.
         *                         </li>
         * </ol>
         *
         * Note that if transitioning from {@link LifecycleState#FAILED} then the
         * three events above will be fired but the component will transition
         * directly from {@link LifecycleState#FAILED} to
         * {@link LifecycleState#STOPPING}, bypassing
         * {@link LifecycleState#STOPPING_PREP}
         *
         * @exception LifecycleException if this component detects a fatal error
         *  that needs to be reported
         */
        public void stop() throws LifecycleException;
    
        /**
         * Prepare to discard the object. The following {@link LifecycleEvent}s will
         * be fired in the following order:
         * <ol>
         *   <li>DESTROY_EVENT: On the successful completion of component
         *                      destruction.</li>
         * </ol>
         *
         * @exception LifecycleException if this component detects a fatal error
         *  that prevents this component from being used
         */
        public void destroy() throws LifecycleException;
    
    
        /**
         * Obtain the current state of the source component.
         *
         * @return The current state of the source component.
         */
        public LifecycleState getState();
    
    
        /**
         * Obtain a textual representation of the current component state. Useful
         * for JMX. The format of this string may vary between point releases and
         * should not be relied upon to determine component state. To determine
         * component state, use {@link #getState()}.
         *
         * @return The name of the current component state.
         */
        public String getStateName();
    
    
        /**
         * Marker interface used to indicate that the instance should only be used
         * once. Calling {@link #stop()} on an instance that supports this interface
         * will automatically call {@link #destroy()} after {@link #stop()}
         * completes.
         */
        public interface SingleUse {
        }
    Lifecycle这个接口主要是定义了监听和生命周期。实现这个接口,就有了组件的生命周期概念。
    然后看一下MBeanRegistration接口的作用,MBean是一个什么pojo?Mean一般是MeanServer,实现了MBeanServerConnection接口,MBeanServerConnection接口中定义了一些创建MeanServer的方法。
    也就是说MeanServer这个接口主要是用来生成对象的,这个对象的作用是:MBean Server是一个集中的管理MBean对象实例的地方。通过MBeanServer,你可以查询、管理、操作Agent应用发布的特定的MBean。
    而MBean是一种规范的JavaBean,通过集成和实现一套标准的Bean接口,这种叫MBean,Mbean注册到MBeanServer中。之后将被MBeanServer中注册过的Adapter(比如渲染为HTML的HtmlAdapter)渲染为直观的页面将MBean的属性和方法展示给用户。
    这边我看网上的一些介绍,要牵涉到JMX架构。那就顺带把这个先给看了。
    JMX,全称为Java Management Extensions

    JMX(Java Management Extensions)是一个为应用程序植入管理功能的框架。JMX是一套标准的代理和服务,实际上,用户可以在任何Java应用程序中使用这些代理和服务实现管理。这是官方文档上的定义,我看过很多次也无法很好的理解。我个人的理解是JMX让程序有被管理的功能,例如你开发一个WEB网站,它是在24小时不间断运行,那么你肯定会对网站进行监控,如每天的UV、PV是多少;又或者在业务高峰的期间,你想对接口进行限流,就必须去修改接口并发的配置值。

      应用场景:中间件软件WebLogic的管理页面就是基于JMX开发的,而JBoss则整个系统都基于JMX构架。

    对于一些参数的修改,网上有一段描述还是比较形象的:

    1、程序初哥一般是写死在程序中,到要改变的时候就去修改代码,然后重新编译发布。

    2、程序熟手则配置在文件中(JAVA一般都是properties文件),到要改变的时候只要修改配置文件,但还是必须重启系统,以便读取配置文件里最新的值。

    3、程序好手则会写一段代码,把配置值缓存起来,系统在获取的时候,先看看配置文件有没有改动,如有改动则重新从配置里读取,否则从缓存里读取。

    4、程序高手则懂得物为我所用,用JMX把需要配置的属性集中在一个类中,然后写一个MBean,再进行相关配置。另外JMX还提供了一个工具页,以方便我们对参数值进行修改。


    那么Connector至少先具备了生命周期,被JMX拓展的特点,然后看看它自身是有什么方法。
    首先它是默认默认为使用HTTP / 1.1 NIO实现。同时它也支持HTTP/1.1,AJP/1.3。
    看它的构造函数
    public Connector(String protocol) {
            boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
                    AprLifecycleListener.getUseAprConnector();
    
            if ("HTTP/1.1".equals(protocol) || protocol == null) {
                if (aprConnector) {
                    protocolHandlerClassName = "org.apache.coyote.http11.Http11AprProtocol";
                } else {
                    protocolHandlerClassName = "org.apache.coyote.http11.Http11NioProtocol";
                }
            } else if ("AJP/1.3".equals(protocol)) {
                if (aprConnector) {
                    protocolHandlerClassName = "org.apache.coyote.ajp.AjpAprProtocol";
                } else {
                    protocolHandlerClassName = "org.apache.coyote.ajp.AjpNioProtocol";
                }
            } else {
                protocolHandlerClassName = protocol;
            }
    
            // Instantiate protocol handler
            ProtocolHandler p = null;
            try {
                Class<?> clazz = Class.forName(protocolHandlerClassName);
                p = (ProtocolHandler) clazz.getConstructor().newInstance();
            } catch (Exception e) {
                log.error(sm.getString(
                        "coyoteConnector.protocolHandlerInstantiationFailed"), e);
            } finally {
                this.protocolHandler = p;
            }
    
            // Default for Connector depends on this system property
            setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
        }
    

     首先是确定协议,然后反射根据名字来创建出处理类。protocolHandler的作用是抽象协议实现,包括线程等,这边重要的是,这玩意里面有很多的细节配置,例如连接中Post,Get请求的最大数据量,异步时间等。

    Connector组件其中包含Protocol组件、Mapper组件和CoyoteAdapter组件;这三个是最关键的,可以这样理解,Connector其实只是一个外壳,真正的内在驱动力还是要靠这三个组件。

    例如Protocol组件,封装了Netty

    public class Http11NioProtocol extends AbstractHttp11JsseProtocol<NioChannel> {
    
        private static final Log log = LogFactory.getLog(Http11NioProtocol.class);
    
    
        public Http11NioProtocol() {
            super(new NioEndpoint());
        }
    
    
        @Override
        protected Log getLog() { return log; }
    
    
        // -------------------- Pool setup --------------------
    
        /**
         * NO-OP.
         *
         * @param count Unused
         */
        public void setPollerThreadCount(int count) {
        }
    
        public int getPollerThreadCount() {
            return 1;
        }
    
        public void setSelectorTimeout(long timeout) {
            ((NioEndpoint)getEndpoint()).setSelectorTimeout(timeout);
        }
    
        public long getSelectorTimeout() {
            return ((NioEndpoint)getEndpoint()).getSelectorTimeout();
        }
    
        public void setPollerThreadPriority(int threadPriority) {
            ((NioEndpoint)getEndpoint()).setPollerThreadPriority(threadPriority);
        }
    
        public int getPollerThreadPriority() {
          return ((NioEndpoint)getEndpoint()).getPollerThreadPriority();
        }
    
    
        // ----------------------------------------------------- JMX related methods
    
        @Override
        protected String getNamePrefix() {
            if (isSSLEnabled()) {
                return "https-" + getSslImplementationShortName()+ "-nio";
            } else {
                return "http-nio";
            }
        }
    }
    

    CoyoteAdapter组件是一个适配器,它负责将Connector组件和Engine容器适配连接起来。把接收到的客户端请求报文解析生成的请求对象Request和响应对象Response传递到Engine容器,交由容器处理; 

    Mapper组件可以称为路由器,它提供了对客户端请求URL的映射功能,即可以通过它将请求转发到对应的Host组件、Context组件、Wrapper组件以进行处理并响应客户端,也就是我们说的将某客户端请求发送到某虚拟机上的某个Web应用的某个Servlet。

     



  • 相关阅读:
    基于Python的人脸动漫转换
    let 与 var的区别
    【LeetCode】汇总
    【HDU】4632 Palindrome subsequence(回文子串的个数)
    【算法】均匀的生成圆内的随机点
    【LeetCode】725. Split Linked List in Parts
    【LeetCode】445. Add Two Numbers II
    【LeetCode】437. Path Sum III
    【LeetCode】222. Count Complete Tree Nodes
    【LeetCode】124. Binary Tree Maximum Path Sum
  • 原文地址:https://www.cnblogs.com/SmartCat994/p/14060028.html
Copyright © 2011-2022 走看看