tomcat服务器解析(一)
httpservlet自己并不能独立运行,需要依赖于一个web容器才能够运行。维基百科中对httpservlet和web容器的关系做了简要说明 A web container (also known as a servlet container) is essentially the component of a web server that interacts with the s
·
httpservlet自己并不能独立运行,需要依赖于一个web容器才能够运行。维基百科中对httpservlet和web容器的关系做了简要说明
A web container (also known as a servlet container) is essentially the component of a web server that interacts with the servlets. The web container is responsible for managing the lifecycle of servlets, mapping a URL to a particular servlet and ensuring that the URL requester has the correct access rights.
apache的官网(http://tomcat.apache.org/tomcat-8.0-doc/config/index.html)上有相应的文档对容器的配置和线程的关系进行说明,在介绍HTTP Connector时,有如下的描述。
At server startup time, this Connector will create a number of request processing threads (based on the value configured for the
minSpareThreads
attribute).
Each incoming request requires a thread for the duration of that request. If more simultaneous requests are received than can be handled by the currently available request processing threads, additional threads will be created up to the configured maximum (the value of the
maxThreads
attribute). If still more simultaneous requests are received, they are stacked up inside the server socket created by the
Connector, up to the configured maximum (the value of the
acceptCount
attribute). Any further simultaneous requests will receive "connection refused" errors, until resources are available to process them.
注意其中飘红的字段部分,
每个到达这个Connector的请求都有一个对应的线程去处理,所以已经可以确认,每个请求是会有一个对应的线程去进行处理。现在只需要确认,线程的创建时机,调用时机即可。
The Executor represents a thread pool that can be shared between components in Tomcat.
Historically there has been a thread pool per connector created but this allows you to share a thread pool, between (primarly) connector but also other components when those get configured to support executors。
注意飘红的字,以前的版本是每个connector都有一个自己的线程池。这一点,在tomcat-5.5的http connector的文档中有相应的说明(http://tomcat.apache.org/tomcat-5.5-doc/config/http.html),原文为“At server startup time, this Connector will create a number of request processing threads”, 说明connector在启动的时候就会创建一个线程池,。但在tomcat-6.0的http connector的文档中,这句话被去掉了。同时相应的
https://tomcat.apache.org/tomcat-4.1-doc/catalina/docs/api/org/apache/catalina/connector/http/HttpConnector.html的文档中说明HttpConnector在后续的版本中被移除了。
一些新版本的tomcat代码在官网上可以找到,对于一些较早的版本,可以从这里(http://www.java2s.com/Code/Jar/c/Downloadcatalinasourcesjar.htm)找到(catalina-source.jar)相应的源代码 。通过catalian.jar的源代码上也能看出HttpConnector类的变化。
通过上面的对比可以猜测,线程池是在服务中配置的,在connector之间共享的。
OK,官网上的理论介绍到这里就差不多了,下面通过源代码来进行解读。官网上有提到Server的实现是org.apache.catalina.core.StandardServer
,通过这个类找到相应的jar包,catalina.jar。借用一个已有的maven工程引入catalina.jar包的maven依赖,相关的maven依赖如下
<
dependency
>
<
groupId
>
org.apache.tomcat
</
groupId
>
<
artifactId
>
tomcat-catalina
</
artifactId
>
<
version
>
8.0.20
</
version
>
</
dependency
>
ps: jetty的代码参考
org.eclipse.jetty.server.Server;
org.eclipse.jetty.server.Connector; ---- 可作为对比学习的参考
在详细看代码之前,首先得了解官网中关于tomcat的web服务器的几个概念 server、service、connector以及engine
server就代表一个web容器,代表一个服务器,其它的几个概念都是属于server的一部分
service表示web容器能够提供的某项服务。如果把server按照功能粒度进行细化,那么每个单元就是一个service
connector和engine是组成server的两个组成部分。engine是执行用户的请求的引擎,connector是请求执行的通道。connector本身并不做请求的处理,只是提供通道功能。
StandardServer代表一个服务器实例,它有一个Service数组的属性,为当前这个Server所拥有的service。这里省去了其它与本次讨论无关的属性
public
final
class
StandardServer
extends
LifecycleMBeanBase
implements
Server{
/**
* The set of Services associated with this Server.
*/
private
Service
services
[] =
new
Service[0];
private
final
Object
servicesLock
=
new
Object();
}
StandardService表示一项具体的服务能力,它有一个Connector数组的属性,是属于这个Service的连接器。
public
class
StandardService
extends
LifecycleMBeanBase
implements
Service {
/**
* The
<code>
Server
</code>
that owns this Service, if any.
*/
private
Server server =
null
;
/**
* The set of Connectors associated with this Service.
*/
protected
Connector connectors[] =
new
Connector[0];
private
final
Object connectorsLock =
new
Object();
protected
final
ArrayList<Executor> executors =
new
ArrayList<>();
/**
* The Container associated with this Service.
*/
protected
Container container =
null
;
}
上面列出了与我们讨论相关的一些Service的属性。server和connectors不用再多说,container可作为一个Engine来作为服务的执行引擎。executors代表为这个服务配置的线程池,可在多个Connector之间共享。
从代码结构上,已经大致与上面的结论对应起来。那么,具体线程池的初始化、调用,请求的解析又是如何完成的呢?
因为Connector(
org.apache.catalina.connector.Connector
)是请求传递的通道,并且在不同版本之间有过较大结构升级,我们以Connector为突破口来学习。
来看下构造函数和启动函数
public
Connector(String protocol) {
setProtocol(protocol);
// Instantiate protocol handler
ProtocolHandler p =
null
;
try
{
Class<?> clazz = Class.forName(protocolHandlerClassName);
p = (ProtocolHandler) clazz.newInstance();
}
catch
(Exception e) {
log.error(sm.getString(
"coyoteConnector.protocolHandlerInstantiationFailed"
), e);
}
finally
{
this
.protocolHandler = p;
}
if
(!Globals.STRICT_SERVLET_COMPLIANCE) {
URIEncoding =
"UTF-8"
;
URIEncodingLower = URIEncoding.toLowerCase(Locale.ENGLISH);
}
}
@
Override
protected
void
initInternal()
throws
LifecycleException {
super
.initInternal();
// Initialize adapter
adapter =
new
CoyoteAdapter(
this
);
protocolHandler.setAdapter(adapter);
// Make sure parseBodyMethodsSet has a default
if
(
null
== parseBodyMethodsSet ) {
setParseBodyMethods(getParseBodyMethods());
}
if
(protocolHandler.isAprRequired() &&
!AprLifecycleListener.isAprAvailable()) {
throw
new
LifecycleException(
sm.getString(
"coyoteConnector.protocolHandlerNoApr"
,
getProtocolHandlerClassName()));
}
try
{
protocolHandler.init();
}
catch
(Exception e) {
throw
new
LifecycleException
(sm.getString
(
"coyoteConnector.protocolHandlerInitializationFailed"
), e);
}
}
/**
* Begin processing requests via this Connector.
*
*
@exception
LifecycleException if a fatal startup error occurs
*/
@
Override
protected
void
startInternal()
throws
LifecycleException {
// Validate settings before starting
if
(getPort() < 0) {
throw
new
LifecycleException(sm.getString(
"coyoteConnector.invalidPort"
, Integer.valueOf(getPort())));
}
setState(LifecycleState.STARTING);
try
{
protocolHandler.start();
}
catch
(Exception e) {
String errPrefix =
""
;
if
(
this
.service !=
null
) {
errPrefix +=
"service.getName(): \""
+
this
.service.getName() +
"\"; "
;
}
throw
new
LifecycleException
(errPrefix +
" "
+ sm.getString
(
"coyoteConnector.protocolHandlerStartFailed"
), e);
}
}
构造函数,主要是初始化一个ProtocolHandler对象。初始化函数,对这个PrococolHandler对象进行初始化。启动函数,则主要是启动这个对象。
ProtocolHandler相关的信息如下
/**
* Coyote Protocol handler class name.
* Defaults to the Coyote HTTP/1.1 protocolHandler.
*/
protected
String protocolHandlerClassName =
"org.apache.coyote.http11.Http11NioProtocol"
;
到这里为止,还没有发现与处理http请求和调用线程池相关的地方。所以,继续去看Http11NioProtocol这个类。
Http11NioProtocol所处的类继承关系如下
Http11NioProtocol
extends
AbstractHttp11JsseProtocol<NioChannel>
public
abstract
class
AbstractHttp11JsseProtocol <S>
extends
AbstractHttp11Protocol<S>
public
abstract
class
AbstractHttp11Protocol<S>
extends
AbstractProtocol<S>
在ProtocolHandler上的init和start所执行的操作,通过AbstractProtocol这两个方法的实现来看
public
void
init()
throws
Exception {
// 这里省略了调试日志和rpc服务注册相关的代码。。。
String endpointName = getName();
endpoint.setName(endpointName.substring(1, endpointName.length()-1));
try
{
endpoint.init();
}
catch
(Exception ex) {
getLog().error(sm.getString(
"abstractProtocolHandler.initError"
,
getName()), ex);
throw
ex;
}
}
@
Override
public
void
start()
throws
Exception {
if
(getLog().isInfoEnabled())
getLog().info(sm.getString(
"abstractProtocolHandler.start"
,
getName()));
try
{
endpoint.start();
}
catch
(Exception ex) {
getLog().error(sm.getString(
"abstractProtocolHandler.startError"
,
getName()), ex);
throw
ex;
}
}
从代码上看AbstractProtocol本身并不做太多请求解析处理相关的事情,它是把事情委托给了一个AbstractEndpoint对象来完成。类似init和start生命周期,其它的几个生命周期如pause、resume、stop、destory也主要是通过AbstractEndpoint对象来完成。
更多推荐
已为社区贡献1条内容
所有评论(0)