Tomcat源码分析----Connector初始化与加载

简介: 一个应用服务器的性能很大程度上取决于网络通信模块的实现,因此Connector对于tomcat而言是重中之重。在本章节中以下两个问题会被回答:一个http请求是怎么被tomcat监听到的,会有哪些处理;为什么请求可以有需要通过nginx的,也可以不需要nginx的直接请求到tomcat上?

一个应用服务器的性能很大程度上取决于网络通信模块的实现,因此Connector对于tomcat而言是重中之重。在本章节中以下两个问题会被回答:

  • 一个http请求是怎么被tomcat监听到的,会有哪些处理;
  • 为什么请求可以有需要通过nginx的,也可以不需要nginx的直接请求到tomcat上?

1 Connector配置

通过对Container的初始化分析,我们很自然的会回过头看conf/server.xml中的connector配置。在xml中配置了2个connector。

<Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

为什么会有多个Connector呢?我们部署服务器的时候,通常会有2种方式:

  • 1 直接部署tomcat,在浏览器中请求http与tomcat直连
  • 2 部署一个nginx作反向代理,tomcat与nginx直连

这就是上面两种配置,通过协议protocol来区分。所以多个connector的好处是通过不同的协议,是tomcat服务器能够随着http的应用场景,服务器架构的升级而兼容起来。

好的,现在配置了2个Connector,那么继续思考一下,Connector是通信过程,如果是你你会怎么设计?显然需要做3件事:

  • (1)监听端口,创建服务端与客户端的链接;
  • (2)获取到客户端请求的socket数据,并对Socket数据进行解析和包装成Http请求数据格式;
  • (3)将包装后的数据交给Container处理。

通过源码来分析,Connector有两个属性:protocolHandler(协议)和adapter(适配器),其中protocolHandler完成的是步骤(1)(2),adapter完成的是步骤(3)。

2 初始化工作

2.1 Connector构造函数

在Connector的构造方法中,通过反射生成protocolHandler.

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);
    }
}

协议的设置在conf/server.xml中配置,由setProtocol来赋值,Tomcat提供了6种协议:
三种不带Ajp的协议,客户端与Tomcat服务器直接连接。
Http11Protocol---------------默认协议,阻塞IO方式的支持http1.1协议
Http11NioProtocol----------非阻塞IO方式的支持http1.1协议
Http11AprProtocol----------使用ARP技术的支持http1.1协议(ARP:Apache portable runtime)
三种带Ajp的协议为定向包协议,即WEB服务器通过 TCP连接和SERVLET容器连接,例如tomcat和Apache、Nginx等前端服务器连接
AjpProtocol--------------------阻塞IO方式的支持Ajp协议
AjpNioProtocol---------------非阻塞IO方式的支持Ajp协议
AjpAprProtocol---------------使用ARP技术的支持Ajp协议
为了便于分析,之对Http11Protocol进行分析,其他的大同小异。在Http11Protocol的构造方法中,对成员变量endpoint和cHandler进行初始化,这两个很重要,后续会继续讲到。
继续到Connector代码中,由前面提到的tomcat启动过程知道,会调用Connector两个方法init和start。而端口的绑定和监听则分别在这两个方法中完成

2.2 Connector的init方法

调用的是initInternal,做了三件事:

  • 1 适配器初始化成CoyoteAdapter;
  • 2 调用Http11Protocol的init方法;
  • 3 调用mapperListener的init方法。
protected void initInternal() throws LifecycleException {

    super.initInternal();
    //TODO:注意,是在在这里初始化的Adapter,将是配置赋值给协议处理类
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);
    ……
    try {
        //TODO:在这里初始化http11protocol
        protocolHandler.init();
    } catch (Exception e) {
        ……
    }
    ……
    mapperListener.init();
}

步骤2中Http11Protocol的init方法,最终会调用到其父类AbstractProtocol的init方法,在这个方法里面对endpoint(Http11Protocol使用的是JIoEndPoint)进行了初始化。

public void init() throws Exception {
    ……
    try {
        //endpoint进行了初始化。
        endpoint.init();
    } catch (Exception ex) {
        ……
    }
}

endpoint.init()在AbstractEndpoint中,完成了对需要监听的端口的绑定。

public final void init() throws Exception {
    testServerCipherSuitesOrderSupport();
    if (bindOnInit) {
        bind();//绑定服务端需要监听的端口
        bindState = BindState.BOUND_ON_INIT;
    }
}

在JIoEndpoint的bind()中完成了对端口的绑定。

2.3 Connector的start方法

Connector的启动会调用start方法,在startInternal方法中,

protected void startInternal() throws LifecycleException {
    ……
    setState(LifecycleState.STARTING);//发送STARTING事件
    try {
        protocolHandler.start();//启动端口监听
    }
    ……
    mapperListener.start();//很重要,后面会提到
}

其中,protocolHandler.start();即调用了Http11Protocol的start方法。最终调用了调用了JIoEndpoint的startInternal方法,初始化了连接请求处理的线程池,监听端口的线程(默认200个)

public void startInternal() throws Exception {
    if (!running) {
        ……
        if (getExecutor() == null) {
            createExecutor();//初始化请求处理的线程池
        }
        initializeConnectionLatch();//设置链接线程阈值
        startAcceptorThreads();//初始化监听接收客户端请求的线程
        ……
    }
}

OK,到了现在Connector的启动已经透明化了,Connector的初始化工作其实是根据server.xml的配置,创建了服务端Socket来监听客户端的请求。并初始化了一个线程池来处理接收到的请求。
通过上面的分析,我们可以看到JIoEndpoint是一个阻塞式的IO模型;而若使用Http11NioProtocol协议,则调用的是NioEndpoint,是一个多路复用IO模型。

系列文章直达:

初始化与启动:https://yq.aliyun.com/articles/20169?spm=0.0.0.0.4yGfpo
容器:https://yq.aliyun.com/articles/20172?spm=0.0.0.0.2uPEZi
连接器:https://yq.aliyun.com/articles/20175?spm=0.0.0.0.2uPEZi
一个http请求的经历:https://yq.aliyun.com/articles/20177?spm=0.0.0.0.2uPEZi
重要的设计模式:https://yq.aliyun.com/articles/20179?spm=0.0.0.0.2uPEZi

目录
相关文章
|
6月前
|
缓存 JavaScript 应用服务中间件
Nginx+Tomcat代理环境下JS无法完全加载问题
Nginx+Tomcat代理环境下JS无法完全加载问题
|
1月前
|
Java 应用服务中间件
Springboot启动的时候初始化的线程池默认配置tomcat
Springboot启动的时候初始化的线程池默认配置tomcat
16 1
|
5月前
|
XML Java 应用服务中间件
SpringBoot配置外部Tomcat项目启动流程源码分析(长文)
SpringBoot配置外部Tomcat项目启动流程源码分析(长文)
61 0
|
6月前
|
应用服务中间件
The Tomcat connector configured to listen on port 10000 failed to start. The port may already be in
The Tomcat connector configured to listen on port 10000 failed to start. The port may already be in
|
10月前
|
存储 缓存 前端开发
07.Tomcat源码分析——类加载体系
由于在生产环境中,Tomcat一般部署在Linux系统下,所以本文将以 startup.sh shell脚本为准,对Tomcat的启动进行分析。
31 0
07.Tomcat源码分析——类加载体系
|
10月前
|
应用服务中间件 Windows
The Tomcat connector configured to listen on port 18081 failed to start. The port may already be in
The Tomcat connector configured to listen on port 18081 failed to start. The port may already be in
145 0
|
11月前
|
前端开发 Java 应用服务中间件
TOMCAT 源码分析 -- 一次请求
TOMCAT 源码分析 -- 一次请求
68 0
|
11月前
|
Java 应用服务中间件
TOMCAT 源码分析 -- 构建环境
TOMCAT 源码分析 -- 构建环境
78 0
|
11月前
|
监控 前端开发 Java
TOMCAT 源码分析 -- 启动(下)
TOMCAT 源码分析 -- 启动
71 0
|
11月前
|
XML 前端开发 Java
TOMCAT 源码分析 -- 启动(上)
TOMCAT 源码分析 -- 启动
107 0