`
uule
  • 浏览: 6305481 次
  • 性别: Icon_minigender_1
  • 来自: 一片神奇的土地
社区版块
存档分类
最新评论

StandardService分析-tomcat6.x源码阅读

阅读更多

来源:https://my.oschina.net/douglas/blog/161607

 

<Service name="Catalina">

    <!--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"/>
    -->


    <!-- 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
    -->
    <Connector connectionTimeout="20000" port="8090" protocol="HTTP/1.1" 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 -->
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443"/>


    <!-- An Engine represents the entry point (within Catalina) that processes
         every request.  The Engine implementation for Tomcat stand alone
         analyzes the HTTP headers included with the request, and passes them
         on to the appropriate Host (virtual host).
         Documentation at /docs/config/engine.html -->

    <!-- You should set jvmRoute to support load-balancing via AJP ie :
    <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
    -->
    <Engine defaultHost="localhost" name="Catalina">

      <!--For clustering, please take a look at documentation at:
          /docs/cluster-howto.html  (simple how to)
          /docs/config/cluster.html (reference documentation) -->
      <!--
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
      -->

      <!-- Use the LockOutRealm to prevent attempts to guess user passwords
           via a brute-force attack -->
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <!-- This Realm uses the UserDatabase configured in the global JNDI
             resources under the key "UserDatabase".  Any edits
             that are performed against this UserDatabase are immediately
             available for use by the Realm.  -->
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm" resourceName="UserDatabase"/>
      </Realm>

      <Host appBase="webapps" autoDeploy="true" name="localhost" unpackWARs="true">

        <!-- SingleSignOn valve, share authentication between web applications
             Documentation at: /docs/config/valve.html -->
        <!--
        <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
        -->

        <!-- Access log processes all example.
             Documentation at: /docs/config/valve.html
             Note: The pattern used is equivalent to using pattern="common" -->
        <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs" pattern="%h %l %u %t &quot;%r&quot; %s %b" prefix="localhost_access_log." suffix=".txt"/>

         </Host>
    </Engine>
  </Service>

 

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处理。

分享到:
评论

相关推荐

    tomcat启动的问题--apr

    2010-8-11 18:24:13 org.apache.catalina.core.StandardService start 信息: Starting service Catalina 2010-8-11 18:24:13 org.apache.catalina.core.StandardEngine start 信息: Starting Servlet Engine: Apache...

    how-tomcat-works

    20.5.6 BaseModelMBean 165 20.5.7 使用Modeler API 165 20.6 Catalian中的MBean 165 20.6.1 ClassNameMBean 165 20.6.2 StandardServerMBean 166 20.6.3 MBeanFactory 167 20.6.4 MBeanUtil 167 20.7 创建Catalian...

    myTomcat:WebServer + Tomcat源码分析总结

    来自《深入剖析Tomcat》 HTTP请求流程-&gt;初始日期一个HTTP请求: 前导工作: org.apache.catalina.startup.Bootstrap启动startup.sh/bat来启动其main(),main()调用Catalina的process() org.apache.catalina...

    信息: Deploying web application directory lx01

    at org.apache.catalina.core.StandardService.start(StandardService.java:519) at org.apache.catalina.core.StandardServer.start(StandardServer.java:710) at org.apache.catalina.startup.Catalina.start...

    struts2驱动包

    信息: The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: C:\Program Files\Java\jdk1.6.0_10\bin;C:\Program ...

    How Tomcat Works: A Guide to Developing Your Own Java Servlet Container

    20.5.6 BaseModelMBean 165 20.5.7 使用Modeler API 165 20.6 Catalian中的MBean 165 20.6.1 ClassNameMBean 165 20.6.2 StandardServerMBean 166 20.6.3 MBeanFactory 167 20.6.4 MBeanUtil 167 20.7 创建Catalian...

    struts-2.3.8+spring-3.2.1+mybatis-3.2.2架构

    九月 18, 2013 11:39:02 上午 org.apache.catalina.core.StandardService startInternal INFO: Starting service Catalina 九月 18, 2013 11:39:02 上午 org.apache.catalina.core.StandardEngine startInternal ...

    一条命令解决macmnsvc.exe占用8081端口的问题

    [2020-04-29 16:30:51.086] [main] [ERROR] [apache.catalina.core.StandardService:182 ] - Failed to start connector [Connector[HTTP/1.1-8081]] org.apache.catalina.LifecycleException: Failed to start ...

    android四大组件

    详细描叙了android四大组件和standardservice ,对android学习有很大的帮助。很有用的android资料

Global site tag (gtag.js) - Google Analytics