2013-09-14
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中添加容器,需要以下几步完成:
经过上面几步完成了为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()中主要完成以下几个步骤
/**
* 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在启动过程过程中要完成下列几个步骤:
/**
* 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()方法中需要完成以下几个步骤:
/**
* 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托了一天才出来,有严重的拖延症啊,希望以后能去除
活在我们自己的世界里,视野很小