Tomcat学习之Connector
时间:2014-12-30 13:26 来源:linux.it.net.cn 作者:IT网
如下图所示,Tomcat服务器主要有两大核心模块组成:连接器和容器,本节只分析连接器的实现。

连接器主要是接收用户的请求,然后封装请求传递给容器处理,tomcat中默认的连接器是Coyote.首先来看连接器的类图:

protocol
我们发现这个类里面有很多与protocol有关的属性和方法,tomcat中支持两种协议的连接器:HTTP/1.1与AJP/1.3,查看tomcat的配置文件server.xml可以看到如下配置:
-
<Connector port="8080" protocol="HTTP/1.1"
-
connectionTimeout="20000"
-
redirectPort="8443" URIEncoding="utf-8"/>
-
-
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
HTTP/1.1协议负责建立HTTP连接,web应用通过浏览器访问tomcat服务器用的就是这个连接器,默认监听的是8080端口;
AJP/1.3协议负责和其他HTTP服务器建立连接,监听的是8009端口,比如tomcat和apache或者iis集成时需要用到这个连接器。
协议上有三种不同的实现方式:JIO、APR、NIO。
JIO(java.io):用java.io纯JAVA编写的TCP模块,这是tomcat默认连接器实现方法;
APR(Apache Portable Runtime):有C语言和JAVA两种语言实现,连接Apache httpd Web服务器的类库是在C中实现的,同时用APR进行网络通信;
NIO(java.nio):这是用纯Java编写的连接器(Conector)的一种可选方法。该实现用java.nio核心Java网络类以提供非阻塞的TCP包特性。
ProtocolHandler接口是对这些协议的抽象,其类层次结构图如下呼所示:

前面提到tomcat默认采用的是Http11Protocol,要么要怎么更换默认的protocolHandler呢,先看看setProtocol方法的源码:
-
public void setProtocol(String protocol) {
-
-
if (AprLifecycleListener.isAprAvailable()) {
-
if ("HTTP/1.1".equals(protocol)) {
-
setProtocolHandlerClassName
-
("org.apache.coyote.http11.Http11AprProtocol");
-
} else if ("AJP/1.3".equals(protocol)) {
-
setProtocolHandlerClassName
-
("org.apache.coyote.ajp.AjpAprProtocol");
-
} else if (protocol != null) {
-
setProtocolHandlerClassName(protocol);
-
} else {
-
setProtocolHandlerClassName
-
("org.apache.coyote.http11.Http11AprProtocol");
-
}
-
} else {
-
if ("HTTP/1.1".equals(protocol)) {
-
setProtocolHandlerClassName
-
("org.apache.coyote.http11.Http11Protocol");
-
} else if ("AJP/1.3".equals(protocol)) {
-
setProtocolHandlerClassName
-
("org.apache.coyote.ajp.AjpProtocol");
-
} else if (protocol != null) {
-
setProtocolHandlerClassName(protocol);
-
}
-
}
-
}
从以上代码可以看出只要protocol不为HTTP/1.1也不为AJP/1.3就会调用setProtocolHandlerClassName来设置protocolHandler,也就是说要替换默认的protocolHandler,只需要修改server.xml文件Connector中的protocol属性即可!
Service
连接器和容器一起才能对外提供服务,Service里面包含了一个容器和多个连接器,连接器是怎么加入到Service中的,可以看一下StandardService中的代码:
-
public void addConnector(Connector connector) {
-
-
synchronized (connectors) {
-
connector.setService(this);
-
Connector results[] = new Connector[connectors.length + 1];
-
System.arraycopy(connectors, 0, results, 0, connectors.length);
-
results[connectors.length] = connector;
-
connectors = results;
-
-
if (getState().isAvailable()) {
-
try {
-
connector.start();
-
} catch (LifecycleException e) {
-
log.error(sm.getString(
-
"standardService.connector.startFailed",
-
connector), e);
-
}
-
}
-
-
// Report this property change to interested listeners
-
support.firePropertyChange("connector", null, connector);
-
}
-
-
}
StandardService中还有setContainer方法,正是Service把容器和连接器关联在一起的
mapperListener
在连接器初始化的时候会初始化mapperListener,mapperListener在初始化的时候会调用mapper对象的addXXX方法

Mapper对象在tomcat中存在于两个地方:
1、每个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;
2、connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪个host下的哪个context的哪个wrapper容器。
Servlet中forward跳转会用到第一种mapper,也就是说forward是服务器内部的重定向。
初始化与启动
Connector的初始化过程如下:
1、Tomcat初始化时会调用Bootstrap的Load方法,这里会解析XML文件,Digester解析的过程中,会调用Connector的构造方法
-
public Connector(String protocol) {
-
setProtocol(protocol);
-
// Instantiate protocol handler
-
try {
-
Class<?> clazz = Class.forName(protocolHandlerClassName);
-
this.protocolHandler = (ProtocolHandler) clazz.newInstance();
-
} catch (Exception e) {
-
log.error(sm.getString(
-
"coyoteConnector.protocolHandlerInstantiationFailed"), e);
-
}
-
}
这个构造方法首先设置protocol,然后初始化protocolhandler
2、紧接着初始化Server,一个Server可能有多个Service,在初始化Server的过程中会初始化多个Service.Service由一个容器和多个连接器组成,会先初始化容器,然后初始化化两个连接器,连接器的初始化是调用的Connector的initInternal方法
-
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());
-
}
-
-
try {
-
protocolHandler.init();
-
} catch (Exception e) {
-
throw new LifecycleException
-
(sm.getString
-
("coyoteConnector.protocolHandlerInitializationFailed"), e);
-
}
-
-
// Initialize mapper listener
-
mapperListener.init();
-
}
(1)首先为protocolHandler设置一个adapter,然后初始化
protocolHandler
(2)初始化mapperListener,啥都没做,只是调用了父类(LifecycleMBeanBase)的initInternal方法!
由于Tomcat的生命周期控制,连接器的启动过程和初始化过程几乎一样,也是由Catalina的start方法开始,Server启动,Service启动,Container启动,Connector启动。Connector启动调用了它的startInternal方法,这个方法只做了两件事:启动protocolHandler和mapperListener,连接器的启动过程就是这样!下面分别来看看protocolHandler和mapperListener启动时都做了哪些事?
protocolHandler的初始化是在其父类AbstractProtocol的start方法中完成的,
-
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;
-
}
-
}
调用了endpoint的start方法,这个方法里面做了以下几件事:
(1)设置接收线程数和最大连接数,创建socket
(2)创建线程池,启动监听的线程监听用户请求
(3)启动一个线程来负责异步请求
mapperListener也继承了LifecycleMBeanBase类,也是受生命周期控制的。所以它的启动是在startInternal方法中完成的
-
public void startInternal() throws LifecycleException {
-
-
setState(LifecycleState.STARTING);
-
-
// Find any components that have already been initialized since the
-
// MBean listener won't be notified as those components will have
-
// already registered their MBeans
-
findDefaultHost();
-
-
Engine engine = (Engine) connector.getService().getContainer();
-
addListeners(engine);
-
-
Container[] conHosts = engine.findChildren();
-
for (Container conHost : conHosts) {
-
Host host = (Host) conHost;
-
if (!LifecycleState.NEW.equals(host.getState())) {
-
// Registering the host will register the context and wrappers
-
registerHost(host);
-
}
-
}
-
}
(1)注册已初始化的组件
(2)为各种容器添加监听器
(3)为各种容器建立映射关系
(责任编辑:IT)
如下图所示,Tomcat服务器主要有两大核心模块组成:连接器和容器,本节只分析连接器的实现。
连接器主要是接收用户的请求,然后封装请求传递给容器处理,tomcat中默认的连接器是Coyote.首先来看连接器的类图:
protocol我们发现这个类里面有很多与protocol有关的属性和方法,tomcat中支持两种协议的连接器:HTTP/1.1与AJP/1.3,查看tomcat的配置文件server.xml可以看到如下配置:
AJP/1.3协议负责和其他HTTP服务器建立连接,监听的是8009端口,比如tomcat和apache或者iis集成时需要用到这个连接器。 协议上有三种不同的实现方式:JIO、APR、NIO。 JIO(java.io):用java.io纯JAVA编写的TCP模块,这是tomcat默认连接器实现方法; APR(Apache Portable Runtime):有C语言和JAVA两种语言实现,连接Apache httpd Web服务器的类库是在C中实现的,同时用APR进行网络通信; NIO(java.nio):这是用纯Java编写的连接器(Conector)的一种可选方法。该实现用java.nio核心Java网络类以提供非阻塞的TCP包特性。 ProtocolHandler接口是对这些协议的抽象,其类层次结构图如下呼所示:
前面提到tomcat默认采用的是Http11Protocol,要么要怎么更换默认的protocolHandler呢,先看看setProtocol方法的源码:
Service
连接器和容器一起才能对外提供服务,Service里面包含了一个容器和多个连接器,连接器是怎么加入到Service中的,可以看一下StandardService中的代码:
mapperListener在连接器初始化的时候会初始化mapperListener,mapperListener在初始化的时候会调用mapper对象的addXXX方法
Mapper对象在tomcat中存在于两个地方: 1、每个context容器对象中,它只记录了此context内部的访问资源与相对应的wrapper子容器的映射;
2、connector模块中,这是tomcat全局的变量,它记录了一个完整的映射对应关系,即根据访问的完整URL如何定位到哪个host下的哪个context的哪个wrapper容器。 初始化与启动
Connector的初始化过程如下:
1、Tomcat初始化时会调用Bootstrap的Load方法,这里会解析XML文件,Digester解析的过程中,会调用Connector的构造方法
2、紧接着初始化Server,一个Server可能有多个Service,在初始化Server的过程中会初始化多个Service.Service由一个容器和多个连接器组成,会先初始化容器,然后初始化化两个连接器,连接器的初始化是调用的Connector的initInternal方法
(2)初始化mapperListener,啥都没做,只是调用了父类(LifecycleMBeanBase)的initInternal方法!
由于Tomcat的生命周期控制,连接器的启动过程和初始化过程几乎一样,也是由Catalina的start方法开始,Server启动,Service启动,Container启动,Connector启动。Connector启动调用了它的startInternal方法,这个方法只做了两件事:启动protocolHandler和mapperListener,连接器的启动过程就是这样!下面分别来看看protocolHandler和mapperListener启动时都做了哪些事?
protocolHandler的初始化是在其父类AbstractProtocol的start方法中完成的,
(1)设置接收线程数和最大连接数,创建socket
(2)创建线程池,启动监听的线程监听用户请求
(3)启动一个线程来负责异步请求
mapperListener也继承了LifecycleMBeanBase类,也是受生命周期控制的。所以它的启动是在startInternal方法中完成的
(2)为各种容器添加监听器
(3)为各种容器建立映射关系
(责任编辑:IT) |