Tomcat NIO(2)

Easter79
• 阅读 570

上一篇文章里我们介绍了 tomcat io 主要包含那些 items,在这里我们主要介绍tomcat io 的基础-多路复用。tomcat 服务器(tomcat7以上)默认使用 java NIO 模型,NIO 不仅仅需要 java 语言上的支持,同时还离不开各种操作系统对于多路复用的支持(linux,windows,mac 等等),所以 tomcat的NIO 是建立在操作系统基础之上的。

对于 linux 操作系统,IO 多路复用使用的是 epoll 方式,对于 windows 操作系统中 IO 多路复用使用的是 iocp 方式,对于 mac 操作系统 IO 多路复用使用的是 kqueue 方式。由于对于 tomcat 服务器来说基本主要部署在 linux 操作系统上,所以我们主要介绍 linux 的 epoll 模型。epoll 是 event poll 的简称,在 linux 内核版本 2.6 开始支持,所以如果你的 tomcat 服务器如果希望默认使用 NIO,除了自己版本在 tomcat7 以上之外,还需要部署在 linux 内核版本大于 2.6 的操作系统之上。

在介绍 epoll 多路复用之前,我们先简单描述一下传统 IO,也就是 BIO(block IO),从而和 epoll IO 有一个大致的对比。在 tomcat6 和之前的版本默认都是使用的 BIO 模型,从 linux 操作系统的角度看,并没有利用 epoll 模型,BIO 模型大致如下:

Tomcat NIO(2)

  • 从 linux 操作系统角度看有一个 socket 监听在某个端口,等待客户端的连接请求,我们称运行监听 socket 的线程为 acceptor thread 。

  • 有多个客户端的连接请求过来,每个请求经过3次握手,监听线程 accept 请求,为每个连接请求在 server 端创建 socket 。

  • 对于服务端的 socket 会尝试读取客户端发送的数据,如果客户端不发送数据,那么这个读取操作会一直阻塞,一直到有数据发送过来。

  • 从操作系统的角度看,当客户端没有数据发送的时候,服务端这个读取数据的线程或进程就会进入 TASK_INTERRUPTIBLE 状态,也就是平时常用的 top 命令中的 S 状态。在操作系统的等待队列里,等待有客户端数据到来,然后唤醒这个读取线程或者进程来读取数据。

  • 基于以上,一般对于每个连接请求的服务端 socket 都会创建一个线程来读取并操作数据。所以连接请求,服务端 socket ,服务端线程是一一对应的关系。

  • 对于上述模型,在并发连接比较少的情况下没有问题。如果并发连接数量巨大,那么意味着操作系统要创建巨大数量的线程来支持并发,同时也需要对这些线程进行调度和上下文切换。这些大量多线带来的工作量对于操作系统来说都是巨大的负担,所以这种 IO 模型很难支持大量的并发。

为了解决传统 IO 模型带来的问题,linux 内核(2.6版本及以上)提供了 epoll 模型,epoll 是event poll ,这种 IO 模型是基于事件的非阻塞 IO 。从 linux 操作系统的角度看,epoll 模型大致如下:

Tomcat NIO(2)

  • 从 linux 操作系统角度看有一个 socket 监听在某个端口,等待客户端的连接请求,我们称运行监听 socket 的线程为 acceptor thread 。

  • 有多个客户端的连接请求过来,每个请求经过3次握手,监听线程 accept 请求,为每个连接请求在 server 端创建 socket 。

  • 对于服务端的 socket 来说,linux 操作系统会为其注册一系列感兴趣的事件(例如读事件,当数据就绪可读的时候触发。写事件,当 buffer 有缓冲,可以写数据的时候触发)。这样所有的服务端 socket 可以形成一个 intreast list 。

  • 对于 individual 的 server 端 socket 来说,如果客户端发送了数据,linux 操作系统会触发注册的读事件,然后会把这个 socket 加入一个就绪列表中,我们称之为 ready list。对于 ready list 之中的 socket 是一定可以读到数据的,因为已经触发了读事件,即数据就绪可读。

  • 一般我们会有一个用户空间的线程或者进程来运行 java NIO 的 API ,在这个线程里通过来轮询 ready list ,如果 list 里有 socket 则进行读取数据和操作数据。如果 ready list 没有触发事件的 socket ,对于操作系统来说,该线程会进入 TASK_INTERRUPTIBLE 状态( top 命令中的 S 状态),在操作系统的等待队列里,等待 ready list 有数据,然后唤醒这个读取线程读取并操作数据。

  • 基于上述,epoll 用少量的线程就可以支持大量的连接请求,从而避免了传统 IO 的问题。

综合上述的传统 IO 和 epoll 模式下的 IO ,我们总结如下:

  • 传统 IO 对于每个连接都需要操作系统分配一个单独的线程(或者进程),在高并发下的大量线程(或者进程)会给操作系统带来巨大负担,所以传统 IO 对高并发支持不友好。

  • epoll 模型下的 IO 会对 socket 注册感兴趣的事件(读写事件等),当事件发生的时候把就绪的 socket 放到 ready list 里,这个列表里的 socket 一定可以读写数据。对于检查事件是否发生,把 socket 放入就绪列表里等任务都是由操作系统内核完成的,不由用户空间的应用程序做,最大限度的利用了操作系统。

  • 用户空间的线程利用包装好的 java NIO API 发起对 ready list 轮询的系统调用,这样少量的用户空间线程就可以完成对大量连接请求的支持,所以 epoll 模式下的 IO 对高并发的支持是友好的。

  • 对于户空间线程我们一般称之为事件轮询线程,tomcat NIO 中一般叫 poller thread 。当 poller thread 发现有可用 socket 的时候一般不会自己处理读取操作数据,而是把 socket 给预先定义好的线程池中的线程来读取数据,操作数据。

  • 对于 tomcat 来说,上面的线程池就是 io 线程池,也就是我们平时配置的 tomcat 线程池,这里面的线程读取数据,运行 servlet 。

  • 对于 epoll 下的 tomcat io 线程池来说,数据的读取是同步的。从操作系统的角度来说,NIO API 发起读数据的系统调用,这个线程会一直等到数据读完返回。只是这个时候一定有数据可读,不必等待过长的时间,所以 tomcat NIO 是同步非阻塞 IO。

目前先写到这里,下一篇文章里我们继续介绍 tomcat NIO 中主要涉及的类和这些类的作用。

Tomcat NIO(2)

本文分享自微信公众号 - TA码字()。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

点赞
收藏
评论区
推荐文章
blmius blmius
2年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Easter79 Easter79
2年前
swap空间的增减方法
(1)增大swap空间去激活swap交换区:swapoff v /dev/vg00/lvswap扩展交换lv:lvextend L 10G /dev/vg00/lvswap重新生成swap交换区:mkswap /dev/vg00/lvswap激活新生成的交换区:swapon v /dev/vg00/lvswap
Jacquelyn38 Jacquelyn38
2年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
Wesley13 Wesley13
2年前
Java获得今日零时零分零秒的时间(Date型)
publicDatezeroTime()throwsParseException{    DatetimenewDate();    SimpleDateFormatsimpnewSimpleDateFormat("yyyyMMdd00:00:00");    SimpleDateFormatsimp2newS
Stella981 Stella981
2年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
2年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
2年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
2年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
2年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
3个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
Easter79
Easter79
Lv1
今生可爱与温柔,每一样都不能少。
文章
2.8k
粉丝
5
获赞
1.2k