• 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。

     



  • 相关阅读:
    软件工程实践总结-黄紫仪
    beta冲刺总结附(分工)-咸鱼
    beta冲刺总结-咸鱼
    beta冲刺7-咸鱼
    beta冲刺用户测评-咸鱼
    beta冲刺6-咸鱼
    beta冲刺5-咸鱼
    beta冲刺4-咸鱼
    beta冲刺3-咸鱼
    beta冲刺2-咸鱼
  • 原文地址:https://www.cnblogs.com/SmartCat994/p/14060028.html
Copyright © 2020-2023  润新知