Tomcat7-Connector(连接器)学习

comment:本文基于Tomcat7.0.68

Tomcat架构:
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

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值