StandardEngine分析-tomcat6.x源码阅读

2013-09-16

StandardEngine是什么

standardEngine是org.apache.catalina.Engine接口的标准实现,继承自ContainerBase,具备容器的功能。Engine的上级领导是Service,角色是负责管理虚拟主机,一个Engine就是一个Servlet容器,能够完成处理请求的任务,Engine从Connector连接器接收请求,然后分发到响应的虚拟主机处理。当需要tomcat需要使用多个虚拟机(配置jvmRoute)来实现负载均衡时,就需要用到Engine。在tomcat中,Engine不是必须的,但是tomcat都会启用Engine组件。Engine的功能:从Connector连接中接收请求,为请求选择虚拟机,分发到虚拟主机中处理请求。

StandardEngine()
StandardEngine在默认构造器中准备所需资源的常量配置。调用super()方式初始化父类,也即ContainerBase;然后为pipeline阀门链(类似FilterChain)设置基准Valve,再者设置jvmRoute虚拟机路由,最后设置Engine后台处理线程的时间间隔。StandardEngine需要使用到这些内容来完成Engine的功能。

    /**
     * Create a new StandardEngine component with the default basic Valve.
     */
    public StandardEngine() {

        super();
        pipeline.setBasic(new StandardEngineValve());
        /* Set the jmvRoute using the system property jvmRoute */
        try {
            //虚拟机路由,集群使用
            setJvmRoute(System.getProperty("jvmRoute"));
        } catch (Exception ex) {
        }
        // By default, the engine will hold the reloading thread
        backgroundProcessorDelay = 10;

    }

ContainerBase
是tomcat的基容器,实现Container容器接口,具有容器的特点和功能。StandardEngine继承了ContainerBase,Engine是容器,具备容器所具备的功能,Engine的父容器是Service,子容器是Host。

StandardEngineValve
Valve在tomcat中蛮重要的,因为所有的请求和响应都必需通过它来过滤,负责传递请求和响应信息,多个Valve组成Pipeline Valve链,每个容器都具有一个Pipeline链,同时有一个BasicValve,BasicValve的作用非常重要,BasicValve在Pipeline链的尾部,负责调用传递请求信息,将控制转到子容器中的Pipeline中。每个容器都有自己的BasicValve,StandardEngineValve就是Engine的BasicValve,继承自ValveBase。StandardEngineValve的方法invoke(Request, Response)负责将请求传递到Host组件中。

    /**
     * Select the appropriate child Host to process this request, based on the
     * requested server name. If no matching Host can be found, return an
     * appropriate HTTP error.
     * 
     * @param request
     *            Request to be processed
     * @param response
     *            Response to be produced
     * @param valveContext
     *            Valve context used to forward to the next Valve
     * 
     * @exception IOException
     *                if an input/output error occurred
     * @exception ServletException
     *                if a servlet error occurred
     */
    public final void invoke(Request request, Response response)
            throws IOException, ServletException {

        // Select the Host to be used for this Request
        Host host = request.getHost();//选择host
        if (host == null) {
            response.sendError(
                    HttpServletResponse.SC_BAD_REQUEST,
                    sm.getString("standardEngine.noHost",
                            request.getServerName()));
            return;
        }

        // Ask this Host to process this request
        host.getPipeline().getFirst().invoke(request, response);

    }

Valve
过滤Request/Response的接口,类似于Filter,两者功能类似,但是作用阶段不一样。Valve由tomcat控制,多个Valve组成一个Pipeline Valve链,每一个容器组件由于一个Pipeline,Filter是程序员定义逻辑,作用范围比Valve小。方法setNext(Valve)是设置本Valve的下一个Valve,Valve自己本身就是一个单向链表的节点,方法backgroundProcess()是后台任务处理,与容器的backgroundProcess()功能相同,方法invoke(Request, Response)是Valve的核心,也即它的功能点所在,负责过滤Request/Response或者传递控制。

ValveBase
是Valve接口的实现,同时实现了Contained,MBeanRegistration接口,具备粘附容器和添加JMX监控的能力,方法backgroundProcess(),invoke(Request, Response)提供空实现,不做任何逻辑操作,将逻辑定义放置到子类中去。本类所完成的功能是Valve的next,保存Valve的下一个Valve,以及Contained接口的方法功能,依附容器。

addChild(Container)
添加子容器,在方法内部是调用父类的方法添加子容器。容器嵌套是容器的基本功能之一。

init()
负责StandardEngine容器的初始化。主要完成以下几步操作:

  • 设置以及初始化标记
  • 注册组件,添加MBServer监控
  • 对父容器Service组件状态的判断
    /**
     * 初始化
     */
    public void init() {
        if (initialized)
            return;
        initialized = true;//标记初始化

        if (oname == null) {
            // not registered in JMX yet - standalone mode
            try {
                if (domain == null) {
                    domain = getName();
                }
                if (log.isDebugEnabled())
                    log.debug("Register " + domain);
                oname = new ObjectName(domain + ":type=Engine");
                controller = oname;
                //登记组件
                Registry.getRegistry(null, null).registerComponent(this, oname,
                        null);
            } catch (Throwable t) {
                log.info("Error registering ", t);
            }
        }

        //MBean监控
        if (mbeansFile == null) {
            String defaultMBeansFile = getBaseDir()
                    + "/conf/tomcat5-mbeans.xml";
            File f = new File(defaultMBeansFile);
            if (f.exists())
                mbeansFile = f.getAbsolutePath();
        }
        if (mbeansFile != null) {
            readEngineMbeans();
        }
        if (mbeans != null) {
            try {
                Registry.getRegistry(null, null).invoke(mbeans, "init", false);
            } catch (Exception e) {
                log.error("Error in init() for " + mbeansFile, e);
            }
        }

        // not needed since the following if statement does the same thing the
        // right way
        // remove later after checking
        // if( service==null ) {
        // try {
        // ObjectName serviceName=getParentName();
        // if( mserver.isRegistered( serviceName )) {
        // log.info("Registering with the service ");
        // try {
        // mserver.invoke( serviceName, "setContainer",
        // new Object[] { this },
        // new String[] { "org.apache.catalina.Container" } );
        // } catch( Exception ex ) {
        // ex.printStackTrace();
        // }
        // }
        // } catch( Exception ex ) {
        // log.error("Error registering with service ");
        // }
        // }

        if (service == null) {//如果服务没有创建
            // for consistency...: we are probably in embeded mode
            try {
                //创建标准服务
                service = new StandardService();
                //设在容器所属
                service.setContainer(this);
                //初始化
                service.initialize();
                // Use same name for Service
                service.setName(getName());
            } catch (Throwable t) {
                log.error(t);
            }
        }

    }

start()
负责StandardEngine容器的启动任务,在start()方法中主要完成以下几步操作:

  • 判定是否已经启动和初始化
  • 启动realm权限管理组件,注册到MBServer
  • 调用基类的start()方法,启动容器,在父容器的start()方法中主要完成事件触发,logger组件,manager组件,realm组件,cluster组件,resource组件,pipeline组件,后台处理任务的启动
    /**
     * Start this Engine component.
     * 启动Engine
     * 
     * @exception LifecycleException
     *                if a startup error occurs
     */
    public void start() throws LifecycleException {
        if (started) {
            return;
        }
        if (!initialized) {
            init();//初始化
        }

        // Look for a realm - that may have been configured earlier.
        // If the realm is added after context - it'll set itself.
        if (realm == null) {
            ObjectName realmName = null;
            try {
                realmName = new ObjectName(domain + ":type=Realm");
                if (mserver.isRegistered(realmName)) {
                    mserver.invoke(realmName, "init", new Object[] {},
                            new String[] {});
                }
            } catch (Throwable t) {
                log.debug("No realm for this engine " + realmName);
            }
        }

        // Log our server identification information
        // System.out.println(ServerInfo.getServerInfo());
        if (log.isInfoEnabled())
            log.info("Starting Servlet Engine: " + ServerInfo.getServerInfo());
        if (mbeans != null) {
            try {
                Registry.getRegistry(null, null).invoke(mbeans, "start", false);
            } catch (Exception e) {
                log.error("Error in start() for " + mbeansFile, e);
            }
        }

        // Standard container startup
        super.start();//启动

    }

stop()
负责停止Engine,主要完成以下几步:

  • 调用父类的stop()方法
  • 停止mbserver
    /**
     * 停止engine
     */
    public void stop() throws LifecycleException {
        super.stop();//停止
        if (mbeans != null) {
            try {
                Registry.getRegistry(null, null).invoke(mbeans, "stop", false);
            } catch (Exception e) {
                log.error("Error in stop() for " + mbeansFile, e);
            }
        }
    }

destroy()
负责Engine销毁,清理占用资源。主要完成以下几个步骤:

  • 注销组件在mbserver,销毁组件。
    /**
     * 销毁Engine
     */
    public void destroy() throws LifecycleException {
        if (!initialized)
            return;
        initialized = false;//标记未初始化

        // if we created it, make sure it's also destroyed
        // this call implizit this.stop()
        ((StandardService) service).destroy();//销毁服务

        if (mbeans != null) {
            try {
                Registry.getRegistry(null, null).invoke(mbeans, "destroy",
                        false);
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardEngine.unregister.mbeans.failed", mbeansFile),
                        e);
            }
        }
        //
        if (mbeans != null) {
            try {
                for (int i = 0; i < mbeans.size(); i++) {
                    Registry.getRegistry(null, null).unregisterComponent(
                            (ObjectName) mbeans.get(i));
                }
            } catch (Exception e) {
                log.error(sm.getString(
                        "standardEngine.unregister.mbeans.failed", mbeansFile),
                        e);
            }
        }

        // force all metadata to be reloaded.
        // That doesn't affect existing beans. We should make it per
        // registry - and stop using the static.
        Registry.getRegistry(null, null).resetMetadata();

    }

logAccess(Request, Response, long, boolean)
日志记录方法,负责记录请求信息和响应信息,对于监控和调试很有帮助。

AccessLogListener
负责监听请求,如果有请求则触发事件通知日志记录。

StandardEngine写的马马虎虎,认识程度还不够深。Engine作为连接Connector和Host的角色,Connector负责监听网络端口,接收请求信息,Engine负责将请求信息转到指定的Host中处理。Engine管理一个或者多个Host,可以在一个Engine中配置多个Host,不同的Host应该由系统中配置的jvmRouteId来运行。Engine作为tomcat中标准的容器,衔接Service和Host,但是也可以没有Engine组件,可以直接将请求转到Servlet中处理,Engine的存在使得多个Host成为可能,而且层次结构更清晰,管理和维护组件更加容易。

该死的拖延症,我恨你

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