• Tomcat 源码分析 (连接器) (六)


    1.连接器概述

    Tomcat要实现两个核心功能

    1. 处理Socket连接,负责网络字节流与RequestResponse对象的转化。
    2. 加载和管理Servlet,以及具体处理Request请求。

    因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这两件事情。连接器负责对外交流,容器负责内部处理。

    在这里插入图片描述
    连接器就是接收http请求并解析http请求,然后将请求交给servlet容器。

    org.apache.catalina.connector.Connector

    CoyoteTomcat的连接器框架的名称 , 是Tomcat服务器提供的供客户端访问的外部接口。客户端通过Coyote与服务器建立连接、发送请求并接受响应 。

    1. Coyote 封装了底层的网络通信(Socket 请求及响应处理),为Catalina 容器提供了统一 的接口,使Catalina 容器与具体的请求协议及IO操作方式完全解耦。
    2. CoyoteSocket 输 入转换封装为 Request 对象,交由Catalina 容器进行处理,处理请求完成后,Catalina 通 过Coyote 提供的Response 对象将结果写入输出流 。
    3. Coyote 作为独立的模块,只负责具体协议和IO的相关操作,与Servlet 规范实现没有直接关系,因此即便是RequestResponse 对象也并未实现Servlet规范对应的接口,而是在Catalina中将他们进一步封装为ServletRequest和ServletResponse

    在这里插入图片描述

    2.连接器组件

    在这里插入图片描述

    • EndPoint
    • Processor:Coyote 协议处理接口
    • ProtocolHandler: Coyote 协议接口
    • Adapter: 实现类为CoyoteAdapter

    EndPoint:

    1. Coyote 通信端点,即通信监听的接口,是具体Socket接收和发送处理 器,是对传输层的抽象,因此EndPoint用来实现TCP/IP协议的。
    2. Tomcat 并没有EndPoint 接口,而是提供了一个抽象类AbstractEndpoint , 里面定 义了两个内部类:AcceptorSocketProcessorAcceptor用于监听Socket连接请求。 SocketProcessor用于处理接收到的Socket请求,它实现Runnable接口,在Run方法里 调用协议处理组件Processor进行处理。为了提高处理能力,SocketProcessor被提交到 线程池来执行。而这个线程池叫作执行器(Executor),我在后面的专栏会详细介绍 Tomcat如何扩展原生的Java线程池。

    Processor:

    1. Coyote 协议处理接口 ,如果说EndPoint是用来实现TCP/IP协议的,那么 Processor用来实现HTTP协议,Processor接收来自EndPointSocket,读取字节流解 析成Tomcat Request和Response对象,并通过Adapter将其提交到容器处理, Processor是对应用层协议的抽象。

    ProtocolHandler:

    1. Coyote 协议接口, 通过Endpoint 和 Processor , 实现针对具体协 议的处理能力。Tomcat 按照协议和I/O 提供了6个实现类 : AjpNioProtocolAjpAprProtocolAjpNio2ProtocolHttp11NioProtocolHttp11Nio2ProtocolHttp11AprProtocol。我们在配置tomcat/conf/server.xml 时 , 至少要指定具体的 ProtocolHandler , 当然也可以指定协议名称: HTTP/1.1 ,如果安装了APR,那么 将使用Http11AprProtocol , 否则使用 Http11NioProtocol

    Adapter:

    1. 实现类为CoyoteAdapter,这是 适配器模式的经典运用,连接器调用CoyoteAdapterSevice方法,传入的是Tomcat Request对象,CoyoteAdapter负责将Tomcat Request转成ServletRequest,再调用容 器的Service方法。

    3.创建连接器对象

    Connector.java

        public Connector() {
            this("HTTP/1.1"); //创建出
        }
    
    
        public Connector(String protocol) {
            boolean apr = AprStatus.isAprAvailable() &&
                AprStatus.getUseAprConnector();
            ProtocolHandler p = null;
            try {
                p = ProtocolHandler.create(protocol, apr);
            } catch (Exception e) {
                log.error(sm.getString(
                        "coyoteConnector.protocolHandlerInstantiationFailed"), e);
            }
            if (p != null) {
                protocolHandler = p;
                protocolHandlerClassName = protocolHandler.getClass().getName();
            } else {
                protocolHandler = null;
                protocolHandlerClassName = protocol;
            }
            // Default for Connector depends on this system property
            setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
        }
    
    
        public Connector(ProtocolHandler protocolHandler) {
            protocolHandlerClassName = protocolHandler.getClass().getName();
            this.protocolHandler = protocolHandler;
            // Default for Connector depends on this system property
            setThrowOnFailure(Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    ProtocolHandler.create()

        public static ProtocolHandler create(String protocol, boolean apr)
                throws ClassNotFoundException, InstantiationException, IllegalAccessException,
                IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
            if (protocol == null || "HTTP/1.1".equals(protocol)
                    || (!apr && org.apache.coyote.http11.Http11NioProtocol.class.getName().equals(protocol))
                    || (apr && org.apache.coyote.http11.Http11AprProtocol.class.getName().equals(protocol))) {
                if (apr) {
                    return new org.apache.coyote.http11.Http11AprProtocol();
                } else {
                    return new org.apache.coyote.http11.Http11NioProtocol();
                }
            } else if ("AJP/1.3".equals(protocol)
                    || (!apr && org.apache.coyote.ajp.AjpNioProtocol.class.getName().equals(protocol))
                    || (apr && org.apache.coyote.ajp.AjpAprProtocol.class.getName().equals(protocol))) {
                if (apr) {
                    return new org.apache.coyote.ajp.AjpAprProtocol();
                } else {
                    return new org.apache.coyote.ajp.AjpNioProtocol();
                }
            } else {
                // Instantiate protocol handler
                Class<?> clazz = Class.forName(protocol);
                return (ProtocolHandler) clazz.getConstructor().newInstance();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    该处可能会传 HTTP 或者 AJP ,根据不同的协议创建不同的协议处理器。也就是连接器,我们看到这里的全限定名是 org.apache.coyote.http11.Http11NioProtocol或者 org.apache.coyote.ajp.Ajp11NioProtocol,这两个类都是在 coyote 包下,也就是连接器模块。

    后面的连接器的初始化和启动在之前的文章有写
    init()startInternal()

    4.IO模型与协议

    Tomcat 支持的IO模型(自8.5/9.0 版本起,Tomcat 移除了 对 BIO 的支持)

    • NIO: 非阻塞I/O,采用Java NIO类库实现。
    • NIO2: 异步I/O,采用JDK 7最新的NIO2类库实现。
    • APR: 采用Apache可移植运行库实现,是C/C++编写的本地库。如果选择该方 案,需要单独安装APR库。

    应用层协议:

    • Http/1.1
    • AJP: 用于和Web服务器集成(如Apache),以实现对静态资源的优化以及 集群部署,当前支持AJP/1.3。
    • Http/2: HTTP 2.0大幅度的提升了Web性能。下一代HTTP协议 , 自8.5以及9.0 版本之后支持。

    在这里插入图片描述
    在 8.0 之前 , Tomcat 默认采用的I/O方式为 BIO , 之后改为 NIO。 无论 NIO、NIO2 还是 APR, 在性能方面均优于以往的BIO。 如果采用APR, 甚至可以达到 Apache HTTP Server 的影响性能。

    Tomcat为了实现支持多种I/O模型和应用层协议,一个容器可能对接多个连接器,就好比一个房间有多个门。但是单独的连接器或者容器都不能对外提供服务,需要把它们组装起来才能工作,组装后这个整体叫作Service组件。这里请你注意,Service本身没有做什么重要的事情,只是在连接器和容器外面多包了一层,把它们组装在一起。Tomcat内可能有多个Service,这样的设计也是出于灵活性的考虑。通过在Tomcat中配置多个Service,可以实现通过不同的端口号来访问同一台机器上部署的不同应用。

  • 相关阅读:
    基于php+thinkphp+vue的校园二手交易网站
    【数据库应用-1】——CDA
    一些网络的常见问题
    软件设计师考试学习2
    Linux删除空目录/非空目录和文件
    YoloV7改进策略:独家原创,LSKA(大可分离核注意力)改进YoloV7,比Transformer更有效,包括论文翻译和实验结果
    ContentProvider 属性介绍
    密码安全:保护你的数据不被入侵的重要性
    《Go Web 编程》之第3章 接收请求
    Linux文件/目录高级管理二
  • 原文地址:https://blog.csdn.net/qq_43141726/article/details/125992445