Tomcat 第五篇:请求处理流程(下)
1. 请求处理流程 AprEndPoint
顺着上一篇接着聊,当一个请求发送到 Tomcat 以后,会由连接器 Connector
转送至 AprEndPoint
,在 AprEndPoint
中调用了 startInternal()
方法,这个方法总共做了做了四件事儿:
- LimitLatch 限制连接次数。
- 创建了 poller 线程。
- 创建了 sendfile 线程。
- 创建了 acceptor 。
其中, poller
、 sendfile
、 acceptor
都是 AprEndPoint
的内部类,因为他们的父类都实现了 Runnable
,所以核心逻辑都在他们自己的 run()
方法中。
其中的涉及到的源代码太多了,我就是懒得往出列了,所以画了下面这个图给各位做个示意。
LimitLatch
是连接控制器,它负责控制最大连接数。Acceptor
跑在一个单独的线程中,它在一个死循环里面通过调用accept()
方法来接收新连接,会返回一个 long 类型的socket
,然后将这个socket
封装成AprSocketWrapper
对象。Poller
本身也跑在一个单独的线程中,它早内部维护了一个SocketList
对象,这个对象中含有socket
数组,它在一个死循环里不断检测socket
的数据就绪状态,一旦有socket
可读,就生成一个SocketProcessor
任务对象扔给Executor
去处理。Executor
就是一个线程池,负责运行SocketProcessor
任务类,SocketProcessor
的run()
方法会调用Http11Processor
来读取和解析请求数据。
肯能有的朋友看完了,都不知道 AprEndPoint
或者说 Apr
这种连接模式是什么。
稍微做下简介:
APR(Apache Portable Runtime Libraries)是 Apache 可移植运行时库,它是用 C 语言实现的,其目的是向上层应用程序提供一个跨平台的操作系统接口库。Tomcat 可以用它来处理包括文件和网络 I/O,从而提升性能。
在 Tomcat8.5.x 中,默认的 I/O 模式使用的是 NIO ,使用的链接器是 org.apache.coyote.http11.Http11NioProtocol
,当然,由于是默认的,无需显示配置,在 server.xml
中只需要这么写就可以了:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
但是如果要换成 APR ,就需要这么写了:
<Connector port="8443" protocol="org.apache.coyote.http11.Http11AprProtocol"
maxThreads="150" SSLEnabled="true" >
<UpgradeProtocol className="org.apache.coyote.http2.Http2Protocol" />
<SSLHostConfig>
<Certificate certificateKeyFile="conf/localhost-rsa-key.pem"
certificateFile="conf/localhost-rsa-cert.pem"
certificateChainFile="conf/localhost-rsa-chain.pem"
type="RSA" />
</SSLHostConfig>
</Connector>
接下来聊一个拷问灵魂的问题, APR 是如何提升性能的?
跟 NioEndpoint
一样, AprEndpoint
也实现了非阻塞 I/O,它们的区别是:NioEndpoint
通过调用 Java 的 NIO API 来实现非阻塞 I/O,而 AprEndpoint
是通过 JNI 调用 APR 本地库而实现非阻塞 I/O 的。
Tomcat 的 Endpoint 组件在接收网络数据时需要预先分配好一块 Buffer,所谓的 Buffer 就是字节数组 byte[] ,Java 通过 JNI 调用把这块 Buffer 的地址传给 C 代码,C 代码通过操作系统 API 读取 Socket 并把数据填充到这块 Buffer。
Java NIO API 提供了两种 Buffer 来接收数据: HeapByteBuffer 和 DirectByteBuffer 。
HeapByteBuffer 对象本身在 JVM 堆上分配,并且它持有的字节数组 byte[] 也是在 JVM 堆上分配。但是如果用 HeapByteBuffer 来接收网络数据,需要把数据从内核先拷贝到一个临时的本地内存,再从临时本地内存拷贝到 JVM 堆,而不是直接从内核拷贝到 JVM 堆上。
数据从内核拷贝到 JVM 堆的过程中,JVM 可能会发生 GC , GC 过程中对象可能会被移动,也就是说 JVM 堆上的字节数组可能会被移动,这样的话 Buffer 地址就失效了。如果这中间经过本地内存中转,从本地内存到 JVM 堆的拷贝过程中 JVM 可以保证不做 GC。
Tomcat 的 AprEndpoint 通过操作系统层面的 sendfile 特性解决了这个问题,sendfile 系统调用方式非常简洁。
2. 请求处理流程 NioEndPoint
前面介绍了 AprEndpoint
的请求处理流程,我们在顺便看下 Tomcat 默认的 NioEndPoint
处理流程。
实际上这两个处理流程非常的相似,区别基本上是因为非阻塞 I/O 的实现方式。
- 在
Acceptor
中的accept()
方法返回一个Channel
对象,接着把Channel
对象交给Poller
去处理。 Poller
在内部维护一个Channel
数组,它在一个死循环里不断检测Channel
的数据就绪状态,一旦有Channel
可读,就生成一个SocketProcessor
任务对象扔给Executor
去处理。每个Poller
线程都有自己的Queue
。每个Poller
线程可能同时被多个Acceptor
线程调用来注册PollerEvent
。Poller
不断的通过内部的Selector
对象向内核查询Channel
的状态,一旦可读就生成任务类SocketProcessor
交给Executor
去处理。Poller
的另一个重要任务是循环遍历检查自己所管理的SocketChannel
是否已经超时,如果有超时就关闭这个SocketChannel
。Executor
是线程池,负责运行SocketProcessor
任务类,SocketProcessor
的run()
方法会调用Http11Processor
来读取和解析请求数据。ServerSocketChannel
通过accept()
接受新的连接,accept()
方法返回获得SocketChannel
对象,然后将SocketChannel
对象封装在一个PollerEvent
对象中,并将PollerEvent
对象压入Poller
的Queue
里,这是个典型的生产者 - 消费者模式,Acceptor
与Poller
线程之间通过Queue
通信。
参考
https://jonhuster.blog.csdn.net/article/details/93297251
Tomcat 第五篇:请求处理流程(下)的更多相关文章
- 学习java随笔第五篇:流程控制
条件语句 if(表达式){方法体}else if(表达体)else{方法体} 简写形式:if... 一般形式:if...else... 完整形式:if...else if...else 分支语句 sw ...
- .Net基础篇_学习笔记_第五天_流程控制while循环
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Webflux请求处理流程
spring mvc处理流程 在了解SpringMvc的请求流程源码之后,理解WebFlux就容易的多,毕竟WebFlux处理流程是模仿Servlet另起炉灶的. 下面是spring mvc的请求处理 ...
- ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程
从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...
- ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程
好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人>: --> 开篇:上一篇 ...
- Http 请求处理流程
引言 我查阅过不少Asp.Net的书籍,发现大多数作者都是站在一个比较高的层次上讲解Asp.Net.他们耐心.细致地告诉你如何一步步拖放控件.设置控件属性.编写CodeBehind代码,以实现某个特定 ...
- 第五篇 :微信公众平台开发实战Java版之如何获取公众号的access_token以及缓存access_token
一.access_token简介 为了使第三方开发者能够为用户提供更多更有价值的个性化服务,微信公众平台 开放了许多接口,包括自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等, 开 ...
- 第五篇 Getting Started with ORACLE EBS(开始学习ORACLE EBS)
第一篇介绍了ERP软件是供应链管理软件.告诉你这个软件改善或提升企业管理的切入点和着力点.有了着力点才能给力. 第二篇介绍了什么是咨询以及咨询工作共通的章法,告诉了你咨询的套路是什么,就像练习一套拳, ...
- ASP.Net MVC请求处理流程
ASP.Net MVC请求处理流程 好听的歌 我一直觉得看一篇文章再听一首好听的歌,真是种享受.于是,我在这里嵌入一首好听的歌,当然你觉得不想听的话可以点击停止,歌曲 from 王菲 <梦中人& ...
随机推荐
- OC基础--字符串
前言 做iOS开发有3年了,从当初的小白到现在,断断续续看过很多资料,之前也写过一些博文来记录,但是感觉知识点都比较凌乱.所以最近准备抽时间把iOS开发的相关知识进行一个梳理,主要分为OC基础.UI控 ...
- mysql必知必会——GROUP BY和HAVING
mysql必知必会——GROUP BY和HAVING 创建表结构 create table `employ_info` ( `id` int(11) NOT NULL AUTO_INCREMENT, ...
- 对于分布式Job的思考
引言 在清理Github的时候,发现以前写的一个简单的分布式任务分发系统ClawHub/task-distribution,使用了zk的选主与队列,调度器使用spring的ThreadPoolTask ...
- Zabbix如何监控Linux防火墙服务
今天在巡检的时候,突然想到Zabbix能否监控Linux的防火墙服务呢? 显然是可以的,但是Zabbix 5下默认的模板"Template OS Linux by Zabbix agent& ...
- JAVA实现汉字转拼音
两个工具包都可以实现:pinyin4j/JPinyin pinyin4j 第一个是使用pinyin4j的jar,此jar对多音字语句的处理不太理想 package edu.ws; import net ...
- .net core Configuration对象
前因:最近在阅读.net core源码,发现关于Configuration介绍的文档都比较多,但是都比较杂乱,(微软文档太官方),所以写下一些自己的感想 主要通过三种使用情况来介绍 Web应用程序使用 ...
- Selenium-WebDriver安装
一.chrome浏览器: 根据chrome浏览器版本,下载对应的驱动 chromedriver版本 支持的Chrome版本 v2.37 v64-66 v2.36 v63-65 v2.35 v62-64 ...
- python的logging模块及应用
一.logging日志模块等级 常见log级别从高到低: CRITICAL >ERROR >WARNING >INFO >DEBUG,默认等级为WARNING,即>=WA ...
- Docker添加TLS认证修复2375端口暴露引发的漏洞
#### 1.环境准备 ```bash# 查看Docker服务器主机名hostnamectl``` ![1582697962553](C:\Users\86176\AppData\Roaming\Ty ...
- 基于abp的小小设备控制系统设计
客户有一堆小设备,需要通过小程序来控制它们,主要是设备门的开关.电源开关.状态查询.压力控制等.下面主要纪录下设计思路.源码地址:https://gitee.com/bxjg1987/abp 最初的设 ...