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托了一天才出来,有严重的拖延症啊,希望以后能去除

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

转载于:https://my.oschina.net/douglas/blog/161607

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值