io --相关 tomcat redis kafka nginx… 对于性能的重要
前提知识:
计算机组成原理
当程序使用内核程序的系统调用的时候就会使用中断,
网络通信io的演变过程:
server.accept() 是阻塞获取
java 的线程 就是通过java调用系统内核????
java 中的3种i/o模型:
BIO(每线程 每连接,但线程内存浪费,cpu调度消耗----阻塞 accept recv)
NIO java new io
nio包 提供了新的io接口(soeketchannel bytebuffer ),
新接口的使用方式,在新接口使用的时候open()得到serversocketchannel
bind()绑定、configureBlocking设置是否阻塞
循环接受客户端连接的accept 都有返回 不会阻塞
AIO(NIO2)
NIO AIO 调用操作系统的epoll
windows中是iocp
Linux操作系统中5中I/O模型:阻塞、非阻塞(nonblocking)、多路服用、异步、驱动
异步IO —>NIO
优势:规避多线程的问题
弊端:假设1万个连接,只有一个发来数据
每循环一次–>必须像内核发送1万次recv的系统调用,那有9999次是无意义,浪费时间和资源—>称为 用户空间像内核空间的循环遍历,复杂度在系统调用上。
JAVA NIO :JAVA new io socket 包 ,有以下
import java.nio.ByteBuffer channels.ServerSocketChannel channel.SocketChannel
==>ServerSocketChannel ss=ServerSocketChannel .open();
ss.bin(new InetSocketAddree(port=‘xx’));
ss.configureBlocking(false); //设置是否阻塞
操作系统的nio :nonblocking
I/O多路复用-----单个线程,通过记录跟踪每个I/O流(sock)的状态,来同时管理多个I/O流
I/O多路复用,I/O就是指的我们网络I/O,多路指多个TCP连接(或多个Channel),复用指复用一个或少量线程。串起来理解就是很多个网络I/O复用一个或少量的线程来处理这些连接。
多路复用过程
三大组件
**1、Selector:**多路复用器。
轮询注册在其上的Channel,当发现某个或者多个Channel处于“就绪状态”后(accept接收连接事件、connect连接完成事件、read读事件、write写事件),从阻塞状态返回就绪的Channel的SelectionKey集合,之后进行IO操作。
2、Channel:封装了socket。
ServerSocketChannel:封装了ServerSocket,用于accept客户端连接请求;
SocketChannel:一对SocketChannel组成一条虚电路,进行读写通信
3、Buffer:用于存取数据,最主要的是ByteBuffer
position:下一个将被操作的字节位置
limit:在写模式下表示可以进行写的字节数,在读模式下表示可以进行读的字节数
capacity:Buffer的大小
select 原理
两次的系统 调用 select recvfrom
select :能同时处理(监听)的文件描述符有限,1024
文件描述符进行监听如果有就绪文件描述符,select会重置readfds、writefds、exceptfds文件描述符集来通知应用程序哪些文件描述符就绪。
这个特性将导致select函数返回后,再次调用select之前,必须重置我们关心的文件描述符,也就是三个文件描述符集已经不是我们之前传入 的了。
poll :也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。
没有监听文件描述符的限制。
epoll原理
epoll_create(int size )
生成一个epoll专用的文件描述符
size为epoll上能关注的最大文件描述符数
epoll_ctl(int epfd,int op,int fd, struct epoll_event *event)
用于控制某个epoll文件描述符事件,可以注册、修改、删除
epfd :epoll_create 生成的epoll专用的文件描述符
op: EPOLL_CTL_ADD 注册 EPOLL_CTL_MOD 修改 EPOLL_CTL_DEL 删除
fd:关联的文件描述符
event:告诉内核要监听什么时间
epoll_wait(int epfd, struct epoll_event *events,int maxevents,int timeout)
等待IO时间发送
epfd :epoll_create生成的epoll专用的文件描述符
events:用于回传待处理时间的数组(传出参数)
max… :告诉内核这个events的大小(events数组的大小肯定是有上限的)
timeout :超时时间 -1 永久阻塞 0 立即返回 >0 超时时间
区别
相同点:
1)三者都需要在fd上注册用户关心的事件;
2)三者都要一个timeout参数指定超时时间;
select
每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大;
每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大;
select支持的文件描述符数量太小了,默认是1024。
poll
大量的fd的数组被整体复制于用户态和内核地址空间之间,不管有无意义
与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符
没有最大连接数的限制,原因是它是基于链表来存储的
epoll
使用mmap(用户态、内核态共享空间)加速内核与用户空间的消息传递
无连接数限制
回调函数返回的是就绪链表
Redis 高并发,高性能?
1、Redis是基于内存的,内存的读写速度非常快;
2、Redis是单线程的,省去了很多上下文切换线程的时间;
3、Redis使用多路复用技术,可以处理并发的连接,系统的高吞吐量。
非阻塞IO 内部实现采用epoll,采用了epoll+自己实现的简单的事件框架。
epoll中的读、写、关闭、连接都转化成了事件,然后利用epoll的多路复用特性,绝不在io上浪费一点时间。
为什么是单线程?
1、单线程的情况下,无需考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗
2、单线程多进程集群方案
3、不能存在上下文切换而消耗CPU
IO多路服用–高吞吐量
1、让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗)。
2、Redis在内存中操作数据的速度非常快(内存内的操作不会成为这里的性能瓶颈)
Redis高并发快总结
- Redis是纯内存数据库,一般都是简单的存取操作,线程占用的时间很多,时间的花费主要集中在IO上,所以读取速度快。
- Redis使用的是非阻塞IO,IO多路复用,使用了单线程来轮询描述符,将数据库的开、关、读、写都转换成了事件,减少了线程切换时上下文的切换和竞争。
- Redis采用了单线程的模型,保证了每个操作的原子性,也减少了线程的上下文切换和竞争。
- 数据结构上,Redis全程使用hash结构,读取速度快,还有一些特殊的数据结构,对数据存储进行了优化,如压缩表,对短数据进行压缩存储,再如,跳表,使用有序的数据结构加快读取的速度。
- Redis采用自己实现的事件分离器,效率比较高,内部采用非阻塞的执行方式,吞吐能力比较大。
@[TOC]Netty
仅支持NIO
1、Linux 下 AIO相比较NIO性能提升不明显(Linux常用作服务器,但AIO实现不够成熟)
2、Netty 单独实现epoll ,why ?
(1) netty 暴露了更多的可控参数
JDK中NIO默认实现epoll是水平触发
Netty中epoll是边缘触发(默认ET)和水平触发可切换
(2) Netty实现的垃圾回收更少,性能更好