comment:本文基于Tomcat7.0.68
Tomcat架构:
Connector作用&定位
根据官方文档,解释如下:
Connect(连接器)负责接收外部连接请求,创建Request、Response对象用于请求的数据交换,并分配线程让Container处理该请求。
官方文档 https://tomcat.apache.org/tomcat-7.0-doc/config/http.html
Connector可以支持多种协议的请求(如HTTP,AJP等)的请求,对Connector的配置在conf/server.xml中
默认为:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000" redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
即在tomcat中支持两种协议的连接器:HTTP/1.1与AJP/1.3
源码内部实现
Connect类图关键属性和方法:
其中,ProtocalHandler是协议处理器接口,不同的协议各自实现。Service是由一个或多个Connector共享一个Container组成,对外部请求提供服务,service主要为了关联Connector和Container。关于Service,请移步。 Adapter是基于coyote的servlet容器的入口。
协议处理器
上文说到,Connector支持多种协议的请求,必然需要进行协议的解析等处理,ProtocalHandler类层次结构为:
从类图中看到,协议上有三种不同的实现方式:JIO、APR、NIO。
关于协议的更多,请移步:
在Connector的代码中:
/**
* Coyote Protocol handler class name.
* Defaults to the Coyote HTTP/1.1 protocolHandler.
*/
protected String protocolHandlerClassName =
"org.apache.coyote.http11.Http11Protocol";
可以看到,默认采用http1.1协议
Connector构造方法
public Connector() {
this(null);
}
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);
}
}
上述代码中根据协议名称构造Connector,继续进入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为null(还记得在哪里设置的吗?conf/server.xml),则setProtocol什么都没做。但是上文中提到,代码初始化protocolHandlerClassName了。
回到构造方法,接下来会加载以protocolHandlerClassName为名字的类,并创建对象实例。至此,构造方法完成。
Q 怎么更换默认的protocolHandler?
A 自己实现ProtocolHandler,再setProtocolHandlerClassName设置为自定义类。
Q 可以在server.xml自定义协议(如xxx),自定义xxx协议的处理器吗?
A 待check……
Connector工作流程
初始化和启动
时序图:
1. Tomcat初始化时会调用Bootstrap的Load方法,会调用Connector的构造方法(已在上文中分析)。
2. 接着调用server.init(),调用链会走到Connector的initInternal()方法
protected void initInternal() throws LifecycleException {
super.initInternal();
adapter = new CoyoteAdapter(this);//connector作为参数传入,设置到CoyoteAdapter的属性中
protocolHandler.setAdapter(adapter);//adapter设置到protocolHandler协议处理器的属性中
//……忽略非核心代码
protocolHandler.init();
//AbstractHttp11JsseProtocol.init()生成JSSEImplementation实例,接着调用AbstractProtocol.init()方法, 进而进入AbstractEndpoint.init(),再调用JIoEndpoint.bind()**核心方法,下面细说**
mapperListener.init();//初始化mapperListener,只是调用了父类(LifecycleMBeanBase)的initInternal,非核心
}
JIoEndpoint.bind()中,会设置Acceptor线程数为1,设置最大连接数(default 200),设置最大连接数的时候会初始化connectionLimitLatch(用于控制Connector的并发连接数,其值即为最大连接数),并初始化serverSocketFactory创建serverSocket。 Acceptor线程在start的时候细说。
protected void startInternal() throws LifecycleException {
……忽略非核心代码
protocolHandler.start();//调用AbstractProtocol.start()进而进入AbstractEndpoint.start()在调用JIoEndpoint.startInternal()方法核心,见下文
mapperListener.start(); //见下文
}
3.接着调用server.start(),调用链会走到Connector的startInternal()方法
4. JIoEndpoint.startInternal()所做的事情:
- 创建工作线程池: 其参数如下:corePoolSize=10,maximumPoolSize=200,keepAliveTime=60s
- 如果在init阶段未初始化ConnectionLatch,则此时会进行初始化
- 启动Acceptor线程(启动的个数在init中初始化了,default 1)监听的serverSocket上的请求(关于该线程的更多,请移步 http://blog.csdn.net/cx520forever/article/details/52441543 )
- 启动一个异步timeout线程(关于该线程的更多,请移步http://blog.csdn.net/cx520forever/article/details/52496079 )
- 再附一张 JIoEndpoint.startInternal()的时序图
-
5. mapperListener.start()
前文说到Connector的属性,有两个属性未列举,mapper和mapperListener
mapper维护了一个从Host到Wrapper的各级容器的快照,即容器的信息,在org.apache.catalina.connector.Request进入Container容器前,Mapper会根据这次请求的hostname和contextPath将host和context容器设置到Request的mappingData属性中。
MappingListener注册到Engine,Host各级容器上,容器状态发生变化就通知它变化更新到Mapper中。
根据Mapper可以确定将请求分派到哪个Host和哪个Servlet容器上以及哪个Servlet上,在传到Servlet前,通过Filter链并在这个过程中调用可能的Listener,最终执行Servlet的service方法。
请求处理
前文中说到Acceptor线程会监听Socket请求并转发给工作线程池,处理请求的即JIoEndpoint的内部类SocketProcessor
。
SocketProcessor根据socket的状态进行第一层处理,另外SSL的握手也是由它负责,在处理时又调用handler.process(socket, SocketStatus.OPEN_READ);
,Hanlder接口是每个具体的Endpoint的内部接口,一般由对应Protocol的一个Handler内部类实现,比如JIoEndponit的handler对应的就是Http11Protocol的Http11ConnectionHandler。
查看handler的调用链,会到adapter.service(request, response);
,CoyoteAdapter完成http request的解析和分派,进而调用connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
,这样,Connector就把请求传递到了Container,也就是Engine对应Pipeline的第一个Valve的invoke方法。关于Pipeline & Valve机制,参考本人另一篇博客:pipeline-valve机制
最后一张图总结下Connector的处理流程:
缺省状态(BIO)下HTTP connector的架构及其消息流:
CoyoteAdapter对象负责将http request解析成HttpServletRequest对象,之后绑定相应的容器,然后从engine开始逐层调用valve直至该servlet。
Http11Protocol 类完全路径org.apache.coyote.http11.Http11Protocol,这是支持HTTP的BIO实现。Http11Protocol包含了JIoEndpoint对象及Http11ConnectionHandler对象,她维护了一个Http11Processor对象池,Http11Processor对象会调用CoyoteAdapter完成HTTP Request的解析和分派。
附上一张Tomcat的Connector组件工作具体序列图:
参考
http://www.cnblogs.com/hansongjiang/p/4229756.html
http://www.cnblogs.com/hansongjiang/category/628889.html
http://blog.csdn.net/aesop_wubo/article/details/7617416
http://www.programgo.com/article/24913246630/
https://yq.aliyun.com/articles/20175
http://blog.csdn.net/fjslovejhl/article/details/20375359
http://blog.csdn.net/fjslovejhl/article/details/22090885 LimitLatch
http://www.360doc.com/content/16/0612/19/1073512_567216241.shtml 实际问题
http://blog.csdn.net/Zerohuan/article/details/50752635#t11
http://blog.csdn.net/c929833623lvcha/article/details/44677569