StandardService分析-tomcat6.x源码阅读

2013-09-14

StandardService是什么

StandardService标准实现Service接口,在tomcat的结构图中,Service位于Server的内部,类似公司的经理职务。从字面上可以看出,Service是一种服务,一种网络协议来对外提供服务的组件。从结构图上看出Service内部管理一个或者多个连接器Connector和一个容器Container(Engine),连接器负责接收和响应网络访问,容器(Engine)负责处理网络访问,将网络请求转到Servlet中处理。一句话,Service是通过连接器接收网络请求并通过调度Servlet处理请求寄居在Server提供服务的组件。注意,一个Server中可以有多少Service。

Lifecycle
与Server类似,通过Lifecycle可以监控Service组件的状态和在不同生命周期阶段修改Service组件的状态。

MBeanRegistration
实现该接口的目的是可以通过JMX来监控组件。

LifecycleSupport
是Service的属性,作用与Server中的一样,负责管理和维护注册在Service上面并实现LifecycleListener接口的监听类,监听跟生命周期有关的事件。

PropertyChangeSupport
是Service的属性,作用与Server中的一样,负责管理和维护注册在Service上面并实现PropertyChangeListener接口的监听类,监听跟Service属性更新有关的事件。

Server
是Service的属性,Service寄居的Server,负责管理Service,不同Service共享Server的数据。

Connector
StandardService的连接器,一个Service中可以有多个连接器,所以在StandardService中有一个Connector数组。Connector监听网络端口,接收网络请求,将请求转到容器中处理,具体是启动一个ServerSocket监听某个网络端口(默认port:8080),当请求到来时接收请求创建Socket对象,将对象分配给一个线程处理。在tomcat的配置文件server.xml中有如下配置:

        <!-- A "Connector" represents an endpoint by which requests are received 
            and responses are returned. Documentation at : Java HTTP Connector: /docs/config/http.html 
            (blocking & non-blocking) Java AJP Connector: /docs/config/ajp.html APR (HTTP/AJP) 
            Connector: /docs/apr.html Define a non-SSL HTTP/1.1 Connector on port 8080 -->
        <!-- 配置连接器,监听8080端口,协议类型是HTTP/1.1,连接timeout时间:20000,
                        当ssl传输请求后重定向的端口,默认协议处理器使用:org.apache.coyote.http11.Http11Protocol-->
        <Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
        <!-- A "Connector" using the shared thread pool -->
        <!-- <Connector executor="tomcatThreadPool" port="8080" protocol="HTTP/1.1" 
            connectionTimeout="20000" redirectPort="8443" /> -->
        <!-- Define a SSL HTTP/1.1 Connector on port 8443 This connector uses the 
            JSSE configuration, when using APR, the connector should be using the OpenSSL 
            style configuration described in the APR documentation -->
        <!-- <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true" maxThreads="150" 
            scheme="https" secure="true" clientAuth="false" sslProtocol="TLS" /> -->

        <!-- Define an AJP 1.3 Connector on port 8009 -->
        <!-- service之间请求的监听 -->
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

从server.xml得知tomcat默认支持的协议是HTTP/1.1,监听8080端口,连接timeout:20000,协议处理器类是org.apache.coyote.http11.Http11Protocol(看Connector源码),还启动了一个负责处理Service直接的请求的连接器AJP/1.3。Connector的具体内容在Connector小节分析。tomcat通过Digester库将Service的xml配置的信息生成对象,并设置属性和父子关系。

Container
StandardService的属性,角色是StandardService的容器,一般是Engine容器,作用是为连接器转来的请求选择路由转给特定Servlet处理,维护和管理容器中子容器,同时为子容器提供数据共享功能。在StandardService中添加容器,需要以下几步完成:

  • 保留原来的Container,用于属性更新事件通知
  • 判断原来Container类型,若为Engine,设置Engine的Service属性为null
  • 更新container值,判断新Container类型,若为Engine,设置Engine的Service属性为为当前Service对象引用
  • 判断Service是否已经启动,若启动,调用Container..start()启动容器,使之工作,提供服务。
  • 更新连接器管理的Container
  • 停止原来的Container
  • 触发Service属性变更事件通知

经过上面几步完成了为Service设置Container的任务

    /**
     * Set the <code>Container</code> that handles requests for all
     * <code>Connectors</code> associated with this Service.
     *  设置容器能否处理请求的所有连接器关联这个服务
     * @param container
     *            The new Container
     */
    public void setContainer(Container container) {

        //属性事件监听使用
        Container oldContainer = this.container;
        if ((oldContainer != null) && (oldContainer instanceof Engine))
            //移除旧容器的服务所属
            ((Engine) oldContainer).setService(null);
        this.container = container;
        if ((this.container != null) && (this.container instanceof Engine))
            //设置新容器的服务所属
            ((Engine) this.container).setService(this);
        if (started && (this.container != null)&& (this.container instanceof Lifecycle)) {//判断服务是否已经启动
            try {
                ((Lifecycle) this.container).start();//启动新容器
            } catch (LifecycleException e) {
                ;
            }
        }
        synchronized (connectors) {//同步
            for (int i = 0; i < connectors.length; i++)
                connectors[i].setContainer(this.container);//更新连接器所属容器
        }
        if (started && (oldContainer != null)
                && (oldContainer instanceof Lifecycle)) {
            try {
                ((Lifecycle) oldContainer).stop();//停止旧容器
            } catch (LifecycleException e) {
                ;
            }
        }

        // Report this property change to interested listeners
        //通知属性监听事件
        support.firePropertyChange("container", oldContainer, this.container);

    }

Executor
线程执行器,在StandardService有一个Executor数组,负责执行分配到Executor上的线程,从我的理解角度来看,他就是线程池。在StandardService中,负责执行连接器接收请求并分配到Executor上的线程。Executor数组可以为null,当为null时表示不使用线程池,实时生成线程运行器。tomcat中对于Executor的配置如下:

        <!--The connectors can use a shared executor, you can define one or more 
            named thread pools -->
        <!-- 连接器能被共享执行器,可以定义一个或者多个线程池,调优使用 -->
        <!-- <Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" 
            minSpareThreads="4"/> -->

从配置文件看出,tomcat默认不是使用线程池。为啥呢?开启线程池之后的性能是否有所提升,有时间验证一下。

init()
StandardService调用init()方法来初始化,在init()方法内部调用的是initialize(),StandardService启动之前需要先对StandardService初始化,在initialize()中主要完成以下几个步骤

  • 判断是否已经初始化
  • 设置已经初始化标记,initialized = true
  • 容器和线程池注册到JMX,通过管理页面监控和操作
  • 关联Server
  • 初始化连接器
    /**
     * Invoke a pre-startup initialization. This is used to allow connectors to
     * bind to restricted ports under Unix operating environments.
     * 预启动初始化,在unix操作环境下面通常允许连接器并定端口
     */
    public void initialize() throws LifecycleException {
        // Service shouldn't be used with embeded, so it doesn't matter
        //服务不能被用于内嵌到其他程序中,所以没有关系
        if (initialized) {//是否已经初始化
            if (log.isInfoEnabled())
                log.info(sm.getString("standardService.initialize.initialized"));
            return;
        }
        initialized = true;//标记初始化

        if (oname == null) {
            try {
                // Hack - Server should be deprecated...
                //获取容器引用
                Container engine = this.getContainer();
                domain = engine.getName();
                oname = new ObjectName(domain + ":type=Service,serviceName="
                        + name);
                this.controller = oname;
                //注册容器
                Registry.getRegistry(null, null).registerComponent(this, oname,
                        null);

                //注册执行器
                Executor[] executors = findExecutors();
                for (int i = 0; i < executors.length; i++) {
                    ObjectName executorObjectName = new ObjectName(domain
                            + ":type=Executor,name=" + executors[i].getName());
                    Registry.getRegistry(null, null).registerComponent(
                            executors[i], executorObjectName, null);
                }

            } catch (Exception e) {
                log.error(
                        sm.getString("standardService.register.failed", domain),
                        e);
            }

        }
        if (server == null) {
            // Register with the server
            // HACK: ServerFactory should be removed...

            //添加服务
            ServerFactory.getServer().addService(this);
        }

        // Initialize our defined Connectors
        synchronized (connectors) {//同步连接器
            for (int i = 0; i < connectors.length; i++) {
                try {
                    connectors[i].initialize();//初始化连接器
                } catch (Exception e) {
                    String message = sm.getString(
                            "standardService.connector.initFailed",
                            connectors[i]);
                    log.error(message, e);

                    if (Boolean
                            .getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                        throw new LifecycleException(message);
                }
            }
        }
    }

经过上面几个步骤后,StandardService完成初始化。

start()
StandardService完成初始化后,完成准备工作,调用start()方法启动Service,正常情况下,start()方法有StandardServer来触发调用。StandardService在启动过程过程中要完成下列几个步骤:

  • 判断是否已经启动
  • 判断是否初始化,若未初始化,调用initialize()方法初始化Service
  • 触发BEFORE_START_EVENT,START_EVENT事件通知,设置启动标记
  • 启动Container,一般是Engine,激活请求处理逻辑。
  • 启动连接池执行器,赋予运行请求处理线程能力
  • 启动连接器,监听网络端口
  • 触发AFTER_START_EVENT事件通知
    /**
     * Prepare for the beginning of active use of the public methods of this
     * component. This method should be called before any of the public methods
     * of this component are utilized. It should also send a LifecycleEvent of
     * type START_EVENT to any registered listeners.
     * 准备开始这个组件的公有方法,这个方法调用应该在这个组件被调用在之前,同时发送START_EVENT
     * 生命周期事件监听给所有已经登记的监听事件
     * 
     * @exception LifecycleException
     *                if this component detects a fatal error that prevents this
     *                component from being used
     */
    public void start() throws LifecycleException {

        // Validate and update our current component state
        if (started) {
            if (log.isInfoEnabled()) {
                log.info(sm.getString("standardService.start.started"));
            }
            return;
        }

        if (!initialized){//判断是否初始化
            init();//初始化
        }


        // Notify our interested LifecycleListeners
        //开始启动之前事件监听
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
        if (log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        //启动事件监听
        lifecycle.fireLifecycleEvent(START_EVENT, null);
        started = true;

        // Start our defined Container first
        if (container != null) {
            synchronized (container) {//同步
                if (container instanceof Lifecycle) {
                    ((Lifecycle) container).start();//启动容器
                }
            }
        }

        synchronized (executors) {//同步执行器
            for (int i = 0; i < executors.size(); i++) {
                executors.get(i).start();//启动执行器
            }
        }

        // Start our defined Connectors second
        synchronized (connectors) {//同步连接器
            for (int i = 0; i < connectors.length; i++) {
                try {
                    ((Lifecycle) connectors[i]).start();//启动连接器
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connectors[i]), e);
                }
            }
        }

        // Notify our interested LifecycleListeners
        //启动之后事件监听通知
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    }

Service组件启动顺序,第一步先启动Container,开启请求处理逻辑能力,第二部再启动连接池执行器,开启线程处理请求的能力,第三步再启动连接器,监听网络端口。组件的功能依赖关系决定了组件的启动顺序。

stop()

停止StandardService的方法stop(),需要停止服务时,需要清理tomcat所占用的资源,在stop()方法中需要完成以下几个步骤:

  • 判断是否已经启动
  • 触发BEFORE_STOP_EVENT事件通知
  • 暂停连接器,暂停接收新请求
  • 触发STOP_EVENT事件监听,设置停止运行标记started = false
  • 停止Container,停止处理请求逻辑,一般是Engine容器
  • 停止连接器,停止监听网络端口
  • 停止连接池执行器
  • 撤销JMX注册
  • 触发AFTER_STOP_EVENT事件通知
    /**
     * Gracefully terminate the active use of the public methods of this
     * component. This method should be the last one called on a given instance
     * of this component. It should also send a LifecycleEvent of type
     * STOP_EVENT to any registered listeners.
     * 停止服务的公有方法,应该在最后被调用,同时发送停止生命周期事件通知。
     * 
     * @exception LifecycleException
     *                if this component detects a fatal error that needs to be
     *                reported
     */
    public void stop() throws LifecycleException {

        // Validate and update our current component state
        if (!started) {
            return;
        }

        // Notify our interested LifecycleListeners
        //开始停止服务事件通知
        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

        // Stop our defined Connectors first
        //首先停止连接器
        synchronized (connectors) {//同步连接器
            for (int i = 0; i < connectors.length; i++) {
                try {
                    connectors[i].pause();//为何是pause(首先停止接收请求,给予时间缓冲处理已经接收但未完成处理的请求)
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.pauseFailed",
                            connectors[i]), e);
                }
            }
        }

        // Heuristic: Sleep for a while to ensure pause of the connector
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            // Ignore
        }

        //停止服务事件通知
        lifecycle.fireLifecycleEvent(STOP_EVENT, null);
        if (log.isInfoEnabled()){
            log.info(sm.getString("standardService.stop.name", this.name));
        }
        started = false;//标记服务停止

        // Stop our defined Container second
        //停止容器
        if (container != null) {
            synchronized (container) {//同步
                if (container instanceof Lifecycle) {
                    ((Lifecycle) container).stop();//停止
                }
            }
        }
        // FIXME pero -- Why container stop first? KeepAlive connetions can send
        // request!
        //为何先停止容器,确保KeeoAlive连接器能够发送请求
        // Stop our defined Connectors first
        //停止连接器
        synchronized (connectors) {//同步连接器队列
            for (int i = 0; i < connectors.length; i++) {
                try {
                    ((Lifecycle) connectors[i]).stop();//停止连接器
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.stopFailed",
                            connectors[i]), e);
                }
            }
        }

        //停止执行器
        synchronized (executors) {
            for (int i = 0; i < executors.size(); i++) {
                executors.get(i).stop();
            }
        }

        if (oname == controller) {
            // we registered ourself on init().
            // That should be the typical case - this object is just for
            // backward compat, nobody should bother to load it explicitely
            //开除注册
            Registry.getRegistry(null, null).unregisterComponent(oname);
            Executor[] executors = findExecutors();
            for (int i = 0; i < executors.length; i++) {
                try {
                    ObjectName executorObjectName = new ObjectName(domain
                            + ":type=Executor,name=" + executors[i].getName());
                    //移除执行器
                    Registry.getRegistry(null, null).unregisterComponent(
                            executorObjectName);
                } catch (Exception e) {
                    // Ignore (invalid ON, which cannot happen)
                }
            }
        }

        // Notify our interested LifecycleListeners
        //完成停止服务事件通知
        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

    }

从代码中可以看出,Service组件的停止顺序与启动顺序差不多刚好相反,首先连机器暂停接收新请求后,缓冲1s,处理已经接收到的请求,然后停止Container,紧接着停止连接器,最后停止连接池执行器。

destroy()
StandardService的销毁方法非常简单,直接调用stop()方法。

StandardService作为一个服务存在,寄居在Server中,维护和管理连接器和Container,实现了监听网络端口,并将网络请求转到Container中处理完成响应请求的任务。StandardService共享Server中的资源,同时也共享给在Service内部的组件,StandardService完成的功能很简练:寄居Server,启动连接器,启动Container,至于如何监听网络请求和处理网络请求则交给连接器和Container处理。

blog托了一天才出来,有严重的拖延症啊,希望以后能去除

活在我们自己的世界里,视野很小

你可能感兴趣的:(StandardService分析-tomcat6.x源码阅读)