Tomcat - Tomcat 网络通信模型剖析 & 并发参数解读
文章目录

什么是IO
IO是指为数据传输所提供的输入输出流,其输入输出对象可以是:文件、网络服务、内存等。

举个例子,假设应用在从硬盘中读取一个大文件过程中, CPU会与硬盘一样出于高负荷状态么?
CPU 没有太高的增涨 。 通常情况下IO操作是比较耗时的,所以为了高效的使用硬件,应用程序可以用一个专门线程进行IO操作,而另外一个线程则利用CPU的空闲去做其它计算。这种为提高应用执行效率而采用的IO操作方法即为IO模型。
Tomcat 支持四种线程模型
| IO模型 | 描述 | 
|---|---|
| BIO | 同步阻塞式IO,即Tomcat使用传统的java.io进行操作。该模式下每个请求都会创建一个线程,对性能开销大,不适合高并发场景。优点是稳定,适合连接数目小且固定架构。 | 
| NIO | 同步非阻塞式IO,jdk1.4 之后实现的新IO。该模式基于多路复用选择器监测连接状态在同步通知线程处理,从而达到非阻塞的目的。比传统BIO能更好的支持并发性能。Tomcat 8.0之后默认采用该模式 | 
| APR | 全称是 Apache Portable Runtime/Apache可移植运行库),是Apache HTTP服务器的支持库。可以简单地理解为,Tomcat将以JNI的形式调用Apache HTTP服务器的核心动态链接库来处理文件读取或网络传输操作。使用需要编译安装APR 库 | 
| AIO (asynchronous I/O) | 异步非阻塞式IO,jdk1.7后之支持 。与nio不同在于不需要多路复用选择器,而是请求处理线程执行完程进行回调调知,已继续执行后续操作。Tomcat 8之后支持。 | 
Tomcat 如何使用指定IO模型
配置 server.xml 文件当中的 <Connector protocol="HTTP/1.1"> 修改即可。
默认配置 8.0 protocol=“HTTP/1.1” 8.0 之前是 BIO 8.0 之后是NIO
BIO: protocol ="org.apache.coyote.http11.Http11Protocol"
NIO: protocol ="org.apache.coyote.http11.Http11NioProtocol"
AIO: protocol ="org.apache.coyote.http11.Http11Nio2Protocol"
APR: protocol ="org.apache.coyote.http11.Http11AprProtocol"
tomcat 8 以前 默认的是 BIO , 8 默认是 NIO
Tomcat BIO VS NIO
在高并发场景下BIO与NIO的线程数的变化

生产环境上,模拟了处理较慢的过程。
为什么差异这么大呢?
我们先看下这两种模型的区别
BIO

源码翻一翻
我们下个Tomcat 7的版本
https://archive.apache.org/dist/tomcat/tomcat-7/v7.0.99/src/
  <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
7嘛 ,默认的BIO, 对应的class org.apache.coyote.http11.Http11Protocol.java
看下 Http11Protocol构造函数
    public Http11Protocol() {
        endpoint = new JIoEndpoint();
        cHandler = new Http11ConnectionHandler(this);
        ((JIoEndpoint) endpoint).setHandler(cHandler);
        setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);
        setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);
        setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY);
    }
重点 JIoEndpoint ------> Java I/O Endpoint

对应JIoEndpoint 中的


 

熟悉吧 Socket, 同步阻塞。
继续
 

这个getExecutor()获取的 就是 那个线程池,用来处理任务的
怎么处理的呢? 继续

NIO

老套路
下载 apache-tomcat-8.5.55-src
Http11NioProtocol的构造函数
 
重点 NioEndpoint

Acceptor 和 SocketProcessor 大致形同, 主要在于Poller .
先看看Acceptor ,和BIO一样 ,有点不一样的是,Acceptor 交个 Poller处理

 进入

 添加事件
 
 public class Poller implements Runnable { 
 
} 
那就看run方法
public void run() {
            // Loop until destroy() is called  轮询
            while (true) {
                boolean hasEvents = false;
                try {
                    if (!close) {
                        hasEvents = events();
                        // > 0 说明有socket需要处理
                        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();
        }
影响 BIO/NIO线程数量的多少的因素
BIO
- 线程数量 会受到 客户端阻塞、网络延迟、业务处理慢===>线程数会更多
NIO
- 线程数量 会受到业务处理慢===>线程数会更多
Tomcat connector 并发参数解读
| 名称 | 描述 | 
|---|---|
| acceptCount | 等待最大队列 | 
| address | 绑定客户端特定地址,127.0.0.1 | 
| bufferSize | 每个请求的缓冲区大小。bufferSize * maxThreads | 
| compression | 是否启用文档压缩 | 
| compressableMimeTypes | text/html,text/xml,text/plain | 
| connectionTimeout | 客户发起链接 到 服务端接收为止,中间最大的等待时间 | 
| connectionUploadTimeout | upload 情况下连接超时时间 | 
| disableUploadTimeout | true 则使用connectionTimeout | 
| enableLookups | 禁用DNS查询 true | 
| keepAliveTimeout | 当长链接闲置 指定时间主动关闭 链接 ,前提是客户端请求头 带上这个 head"connection" " keep-alive" | 
| maxKeepAliveRequests | 最大的 长连接数 | 
| maxHttpHeaderSize | HTTP请求、响应头信息的最大大小,默认是8192bytes | 
| maxSpareThreads | BIO 模式下 最多线闲置线程数 | 
| maxThreads | (执行线程) 最大执行线程数 | 
| minSpareThreads | (初始线业务线程 10) BIO 模式下 最小线闲置线程数 | 
文章来源: artisan.blog.csdn.net,作者:小小工匠,版权归原作者所有,如需转载,请联系作者。
原文链接:artisan.blog.csdn.net/article/details/106522422
- 点赞
- 收藏
- 关注作者
 
             
           
评论(0)