Tomcat调用解析(EndPoint分析)

Tomcat源码:ProtocolHandler与Endpoint 前文中我们介绍了连接器与其入口Connector以及线程池Executor,而在Connector中则是通过protocolHandler的init与start启动了整个连接器,本文我们就来介绍下protocolHandler与其关键组件Endpoint的内容,以及线程池Executor是如何发挥其作用的。目录前言一、ProtocolHandler1、构造方法1.1、Http11NioProtocol与其抽象父类1.2、 AbstractProtocol与成员变量的赋值2、生命周期方法2.1、init方法。 阅读详情

解析EndPoint调用过程:

我们已知整个tomcat组件都是通过LifeCycle接口下的LifeCycleBase模版方法init调用initInternal完成初始化,调用start方法运行。

  • LifeCycle
  •  LifeCycleBase
    
  •  		tomcat组件
    

那么EndPoint也不例外,不过EndPoint是通过Protocolhandler进行初始化的。
Protocolhandler则是通过Connector进行初始化。
其中Connector才继承了LifeCycleBase类。

那么我们直接在Connector类下找到initInternal方法,重要操作一般都在try中。

try {
    protocolHandler.init();
} catch (Exception e) {
    throw new LifecycleException(
            sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
}

ProtocolHandler是一个协议处理器。
Tomcat支持NIO NIO2(异步) Apr这三种IO模型,支持HTTP1.1 AJP等应用层协议。
IO在传输层中,协议在应用层中,所以两两组合,最起码都有6种。

AbstractProtocol作为抽象类实现ProtocolHandler接口,使用组合的方式,其内部成员变量有一个AbstractEndpoint封装EndPoint。
连接器结构

ProtocolHandler中有EndPoint和Processor。

我们找到一个最常用的ProtocolHandler:Http11NioProtocol

public Http11NioProtocol() {
    super(new NioEndpoint());
}

其构造方法调用父类的构造方法注入NioEndpoint。

NioEndpoint重要的有三个东西:

1.Acceptor[] acceptors

2.Poller[] pollers

3.PollerEvent

其中1,2都相当于一个线程数组

在调用Connector的start方法时,往下会调用EndPoint的start方法,那么这时就会创建线程start。

先开启pollers数组线程

再开启acceptors数组线程

我们可以分别查看两个类的run方法:

Poller:

以下是Poller的run方法

public void run() {
    // Loop until destroy() is called
    while (true) {

        boolean hasEvents = false;

        try {
            if (!close) {
                hasEvents = events();
                if (wakeupCounter.getAndSet(-1) > 0) {
                    //if we are here, means we have other stuff to do
                    //do a non blocking select
                    keyCount = selector.selectNow();
                } else {
                    keyCount = selector.select(selectorTimeout);
                }
                wakeupCounter.set(0);
            }
            if (close) {
                events();
                timeout(0, false);
                try {
                    selector.close();
                } catch (IOException ioe) {
                    log.error(sm.getString("endpoint.nio.selectorCloseFail"), ioe);
                }
                break;
            }
        } catch (Throwable x) {
            ExceptionUtils.handleThrowable(x);
            log.error("",x);
            continue;
        }
        //either we timed out or we woke up, process events first
        if ( keyCount == 0 ) hasEvents = (hasEvents | events());

        Iterator<SelectionKey> iterator =
            keyCount > 0 ? selector.selectedKeys().iterator() : null;
        // Walk through the collection of ready keys and dispatch
        // any active event.
        while (iterator != null && iterator.hasNext()) {
            SelectionKey sk = iterator.next();
            NioSocketWrapper attachment = (NioSocketWrapper)sk.attachment();
            // Attachment may be null if another thread has called
            // cancelledKey()
            if (attachment == null) {
                iterator.remove();
            } else {
                iterator.remove();
                processKey(sk, attachment);
            }
        }//while

        //process timeouts
        timeout(keyCount,hasEvents);
    }//while

    getStopLatch().countDown();
}

循环调用events方法

private final SynchronizedQueue<PollerEvent> events =
        new SynchronizedQueue<>();

events内容:
在这个同步队列里找到一个不为空的PollerEvent调用其run方法。

然后重置PollerEvent,并且添加到eventCache当中

private SynchronizedStack eventCache;

Acceptor

protected class Acceptor extends AbstractEndpoint.Acceptor {

    @Override
    public void run() {

        int errorDelay = 0;

        // Loop until we receive a shutdown command
        while (running) {

            // Loop if endpoint is paused
            while (paused && running) {
                state = AcceptorState.PAUSED;
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    // Ignore
                }
            }

            if (!running) {
                break;
            }
            state = AcceptorState.RUNNING;

            try {
                //if we have reached max connections, wait
                countUpOrAwaitConnection();

                SocketChannel socket = null;
                try {
                    // Accept the next incoming connection from the server
                    // socket
                    socket = serverSock.accept();
                } catch (IOException ioe) {
                    // We didn't get a socket
                    countDownConnection();
                    if (running) {
                        // Introduce delay if necessary
                        errorDelay = handleExceptionWithDelay(errorDelay);
                        // re-throw
                        throw ioe;
                    } else {
                        break;
                    }
                }
                // Successful accept, reset the error delay
                errorDelay = 0;

                // Configure the socket
                if (running && !paused) {
                    // setSocketOptions() will hand the socket off to
                    // an appropriate processor if successful
                    if (!setSocketOptions(socket)) {
                        closeSocket(socket);
                    }
                } else {
                    closeSocket(socket);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString("endpoint.accept.fail"), t);
            }
        }
        state = AcceptorState.ENDED;
    }


}

Acceptor run方法循环直到shutdown

socket = serverSock.accept();等待客户端请求

其中setSocketOptions方法详细查看

protected boolean setSocketOptions(SocketChannel socket) {
    // Process the connection
    try {
        //disable blocking, APR style, we are gonna be polling it
        socket.configureBlocking(false);
        Socket sock = socket.socket();
        socketProperties.setProperties(sock);

        NioChannel channel = nioChannels.pop();
        if (channel == null) {
            SocketBufferHandler bufhandler = new SocketBufferHandler(
                    socketProperties.getAppReadBufSize(),
                    socketProperties.getAppWriteBufSize(),
                    socketProperties.getDirectBuffer());
            if (isSSLEnabled()) {
                channel = new SecureNioChannel(socket, bufhandler, selectorPool, this);
            } else {
                channel = new NioChannel(socket, bufhandler);
            }
        } else {
            channel.setIOChannel(socket);
            channel.reset();
        }
        getPoller0().register(channel);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        try {
            log.error("",t);
        } catch (Throwable tt) {
            ExceptionUtils.handleThrowable(tt);
        }
        // Tell to close the socket
        return false;
    }
    return true;
}

NioChannel channel = nioChannels.pop();弹出一个channel

初始化channel

getPoller0().register(channel);

public Poller getPoller0() {
    int idx = Math.abs(pollerRotater.incrementAndGet()) % pollers.length;
    return pollers[idx];
}

返回一个poller

查看register方法:

public void register(final NioChannel socket) {
    socket.setPoller(this);
    NioSocketWrapper ka = new NioSocketWrapper(socket, NioEndpoint.this);
    socket.setSocketWrapper(ka);
    ka.setPoller(this);
    ka.setReadTimeout(getSocketProperties().getSoTimeout());
    ka.setWriteTimeout(getSocketProperties().getSoTimeout());
    ka.setKeepAliveLeft(NioEndpoint.this.getMaxKeepAliveRequests());
    ka.setSecure(isSSLEnabled());
    ka.setReadTimeout(getConnectionTimeout());
    ka.setWriteTimeout(getConnectionTimeout());
    PollerEvent r = eventCache.pop();
    ka.interestOps(SelectionKey.OP_READ);//this is what OP_REGISTER turns into.
    if ( r==null) r = new PollerEvent(socket,ka,OP_REGISTER);
    else r.reset(socket,ka,OP_REGISTER);
    addEvent(r);
}

创建一个NioSocketWrapper类 设置属性

addEvent方法就是往events中添加PollerEvent

private final SynchronizedQueue<PollerEvent> events =
        new SynchronizedQueue<>();

总结:整个Endpoint流程可以看出先启动Poller数组线程,再启动Accept数组线程。
Accept结束socket请求,然后再Poller中获取一个poller,调用register方法往events中添加PollerEvent。

Poller则是循环检测events中是否有PollerEvent,调用其run方法。

tomcat服务器解析(二) --- Endpoint Endpoint主要用来提供基础的网络I/O服务,封装了网络通讯相关的细节。在AbstractProtocol中对Endpoint有这样一段注释     /**      * Endpoint that provides low - level network I/O - must be matched to the      * ProtocolHandler im 阅读详情

相关推荐

tomcat如何处理请求 源码分析(一)( Connector 部分)

tomcat源码分析共4个阶段 1.从整体把握tomcat的架构 2.分析springboot内嵌的tomcat是怎么启动的 3.分析tomcat如何接受处理请求1(HttpServlet之前) 4.分析tomcat如何接受处理请求2(HttpServlet处理) 目标: 本文主要分析一个来自浏览器的请求如何被tomcat接受,分析了从接收到socket至HttpServlet处理之间的各个方法调...

小样 772

tomcat的启动流程及原理

tomcat的启动流程及原理

lgxzzz的博客 1753

7 Tomcat分析

7 Tomcat分析 7.1.1 Tomcat的顶层结构 Tomcat中最顶层的容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务。Service主要包含两部分:Connector和Container。Connector用于处理连接相关的事情,并提供Socket与request、response的转换,Container用于封装和管理Servlet,以及...

青葱暖咖啡 383

死磕Tomcat系列(2)——EndPoint源码解析

死磕Tomcat系列(2)——EndPoint源码解析 前言 上一节中我们描述了Tomcat的整体架构,我们知道Tomcat分为两大组件,一个连接器和一个容器。而我们这次要讲的 Endpoint的组件就是属于连接器里面的。它是一个通信的端点,就是负责对外实现TCP/IP协议。Endpoint是个接口,它的具体实现类就是AbstractEndpoint,而AbstractEndpoint具体的实现就...

qq_43419105的博客 450

UDS诊断入门第一课:手把手教你玩转0x10会话控制服务(附ISO14229实战解析)

刚接触汽车电子诊断的工程师们,往往会被厚厚的ISO 14229标准文档和各种专业术语吓退。但就像学习编程要从"Hello World"开始一样,掌握UDS协议的最佳切入点就是0x10诊断会话控制服务。这个看似简单的服务,实际上是整个诊断系统的"钥匙"——它决定了ECU(电子控制单元)当前处于何种工作模式,直接影响着其他诊断服务的可用性。 在实际车辆诊断中,ECU默认处于DefaultSession模式,这种模式下许多高级诊断功能是被禁用的。想象一下你正在开发一个需要刷写ECU固件的工具,如果连最基本的会

weixin_30402343的博客 393

STM32与K210串口通信

这篇文章是为了填上一篇埋下的坑,k210和STM32通过串口通信,我是采用数据包的形式发送数据的,因为K210发送的数据有几种,采用数据包的形式发送比较安全。本文代码运行的平台是STM32F103C8T6和K210 Dock,只涉及到K210发送数据给STM32,stm32将接受到的数据显示到0.96寸OLED屏上。

qq_70715693的博客 3万+

Tomcat(二):Tomcat在SpringBoot中是如何启动的

说明:本文tomcat版本是9.0.21,不建议零基础读者阅读。 从连接器(Connector)源码说起 既然是来解析连接器(Connector),那么我们直接从源码入手,后面所有源码我会剔除不重要部分,所以会忽略大部分源码细节,只关注流程。源码如下(高能预警,大量代码): public class Connector extends LifecycleMBeanBase { ...

scofen的博客 1047

飞牛Docker部署Zerotier后无法访问?三步搞定SSH权限配置!

本文详细解析了在飞牛NAS上通过Docker部署Zerotier后无法访问的常见问题及解决方案。重点介绍了SSH权限配置、tun模块加载和Docker容器权限设置等关键步骤,帮助用户快速解决虚拟网络接口的权限问题,确保Zerotier节点间的正常通信。

qq_27496129的博客 793

tomcat是干嘛用的_大公司程序员带你死磕Tomcat系列(二)——EndPoint源码解析

死磕Tomcat系列(2)——EndPoint源码解析在上一节中我们描述了Tomcat的整体架构,我们知道了Tomcat分为两个大组件,一个连接器和一个容器。而我们这次要讲的EndPoint的组件就是属于连接器里面的。它是一个通信的端点,就是负责对外实现TCP/IP协议。EndPoint是个接口,它的具体实现类就是AbstractEndpoint,而AbstractEndpoint具体的实现类就有...

weixin_39674028的博客 173

2025.06.20【pacbio】|PB甲基化分析结果的统计与可视化介绍

本文介绍了PacBio甲基化数据的统计分析与可视化方法。主要内容包括:1) 分析甲基化位点分布、比例及染色体分布;2) 修饰基序(Motif)的统计与热图展示;3) 全基因组和局部区域测序深度可视化;4) 使用IGV查看单个reads修饰情况。文章提供了R代码示例,涵盖柱状图、分布图、热图等多种可视化形式,并建议了差异甲基化分析等进阶方法,帮助科研人员深入挖掘甲基化数据。

2066

保姆级QFIL线刷教程:从驱动安装到成功刷机的完整避坑指南(Win10/11实测)

本文提供了一份详细的QFIL线刷教程,涵盖从驱动安装到成功刷机的完整流程,特别针对Win10/11系统进行了实测。文章重点解决了EDL模式下的常见问题,如Download Fail和COM Port识别错误,并提供了实用的避坑技巧和故障排除方案,帮助用户实现零失误操作。

weixin_29245767的博客 765

tomcat 9.0源码分析之Endpoint的启动流程

Connector根据配置的协议名称初始化protocolHandler /** * Coyote protocol handler. */ protected final ProtocolHandler protocolHandler; public Connector(String protocol) { boolean aprConnector = ...

lzf的博客 599

一文学会VOS2009对接网关指定走某个落地网关

介绍:  在我们日常使用VOS2009/3000的过程中,肯定会有很多落地网关与对接网关,并且某些时候还需要某些对接网关指定去走某个落地网关或者某些落地网关,从而实现比较精准的控制对接网关与落地网关的关系,下面就演示下在VOS2009/3000中如何配置某个对接网关指定走某个或者多个落地网关。 实现步骤:  VOS2009可以通过添加固定前缀的方式来指定对接网关走某个落地网关。 比如:  r1对接网关在被叫改写规则里面填写*:a*  r2对接网关在被叫改写规则里填写*:b* 1.登录VOS客户端 2.进入

非著名运维的博客 1963

深入解析Tomcat Processor的协议处理机制

本文系统解析了Tomcat框架中Processor组件的核心功能与设计原理。作为连接网络层与Servlet容器的关键桥梁,Processor负责HTTP协议解析、Request/Response对象构建及容器适配工作。文章详细剖析了Processor的模块化设计、适配器模式应用、请求处理流程源码,及其与ProtocolHandler/Endpoint的协作机制。通过分层架构分析与代码示例,揭示了Tomcat高并发处理的核心机理,为性能优化与协议扩展提供了理论基础。全文贯穿连接器-处理器-容器的三层次架构思想

持续输出Java相关知识 1155

互联网的发展简史—web

互联网是我现在生活所离不开的一个东西,但是你知道互联网的前世今生吗?本文将讲解互联网的发展史,以及很火的元宇宙概念。

yj11290301的博客 3563

【数据集】30 m地表温度LST数据集

【数据集】30 m地表温度LST数据集

WW、forever的博客 1129

Tomcat源码笔记(六)Connector--Endpoint

Endpoint端点,在Tomcat中也就是接收socket连接和处理连接的地(ServerSocket.accept())。 AbstractEndpoint声明了三个组件,Accepter,SocketProcessor,以及一个Handler接口。 Accepter 顾名思义,ServerSocket.accept()接收socket连接 SocketProcessor 模板方法,子类实现具体的SocketProcessor类,处理接收到的socket,SocketProcessor处...

xyjy11的博客 552

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

Tomcat分析五 —— Connector分析

​ Connector用于接收请求并将请求封装成Request和Response具体处理,底层使用Socket进行连接,Request和Response是按照HTTP协议来封装的,所以Connector同时实现了TCP/IP协议和HTTP协议。 ​ Request和Response封装完后交给Container进行处理,Container就是Servlet的容器,Container处理完返回给...

执者失之的专栏 805

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Tomcat源码解析

我们知道如果要设计一个系统,首先是要了解需求,我们已经了解了Tomcat要实现两个 核心功能: 1) 处理Socket连接,负责网络字节流与Request和Response对象的转化。 2) 加载和管理Servlet,以及具体处理Request请求。 因此Tomcat设计了两个核心组件连接器(Connector)和容器(Container)来分别做这 两件事情。连接器负责对外交流,容器负责内部处理 2.3 连接器 - Coyote 2.3.1 架构介绍 Coyote 是Tomcat的连接器框架的名称 ,

liuhehe的博客 2484

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

(四)Tomcat源码解析 - 一次request与response的过程

Tomcat最本质就是一个能运行JSP/Servlet的Web服务器 ,如果真正的想了解tomcat它的运转机制,最典型的应用就是用户通过浏览器访问服务器,Tomcat接收到请求后转发给Servlet,由Servlet处理完成之后,把结果返回给客户端。今天分析tomcat一次request与response的过程。 最简单的方式是,Hello World!  进入debug模式。Tomcat处理

AnndyR的专栏 1793

Tomcat线程分析

用一般配置下Tomcat 9线程情况进行分析 main Thread:一般是唯一非daemon线程,await NioBlockingSelector.BlockPoller-(1,2) ContainerBackgroundProcessor:一般只有engine有此线程 http-nio-8080-exec-(1到10) http-nio-8080-ClientPoller-(0到1) htt...

kobexzf的博客 1657

Tomcat8源码解析

Tomcat8源码解析 Tomcat总体架构 Connector:开启Socket并监听客户端请求,返回响应数据; Container:负责具体的请求处理; 一个Service负责维护多个Connector和一个Container,这样来自Connector的请求只能有它所属的Service维护的Container处理; Engine:代表整个servlet引擎 Host:表示一个虚拟...

qq_24101357的博客 2万+

Tomcat源码解析四(连接器启动过程解析)

一、逻辑分析: 连接器中有三个核心组件:Endpoint、Processor 和 Adapter, 其中Endpoint 和 Processor 放在一起抽象成了 ProtocolHandler 组件, 关系图如下: Endpoint Endpoint 是通信端点,即通信监听的接口,是具体的 Socket 接收和发送处理器,是对传输层的抽象,因此 Endpoint 是用来实现 TCP/...

AN_ERA的博客 479

[Spring mvc 深度解析(二)] Tomcat分析

第7章 Tomcat分析 ​ 前面已经给大家介绍了网站处理请求时所涉及的各种协议和实现方法,不过之前的实现只是为了让大家明白原理而设计的简单示例程序,本章分析一个实际环境中经常使用的具体的实现——Tomcat。 1 Tomcat的顶层结构及启动过程 1.1 Tomcat的顶层结构 ​ Tomcat中最顶层的容器叫Server,代表整个服务器,Server中包含至少一个Service,用于具体提供服务。Service主要包含两部分:Connector和Container。Connector用于处理连接相关

LoveSummer 3213
上一篇: java内部类引用外部变量
下一篇: Git学习教程
slience.
博客等级 码龄7年 111粉丝 18原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值