- 彻底弄懂IO
- 基本概念
- IO 模型
- IO发展历程
- OS基础
- 引入
- BIO
- NIO
- select
- epoll
- 同步
- IO是个细活
- epoll
- 零拷贝
- 分布式学习路径
相关链接:
https://www.topgoer.cn/docs/golangxiuyang/golangxiuyang-1cmef12eubu5j
https://blog.csdn.net/TTTZZZTTTZZZ/article/details/87888487
https://www.bilibili.com/video/BV11K4y1C7rm?p=2 彻底弄懂IO 基本概念
- 同步
同步就是发起一个调用后,被调用者未处理完请求之前,调用不返回。 - 异步
异步就是发起一个调用后,立刻得到被调用者的回应表示已接收到请求,但是被调用者并没有返回结果,此时我们可以处理其他的请求,被调用者通常依靠事件,回调等机制来通知调用者其返回结果。
同步和异步的区别最大在于异步的话调用者不需要等待处理结果,被调用者会通过回调等机制来通知调用者其返回结果。
- 阻塞
阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当条件就绪才能继续。 - 非阻塞
非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。 - BIO
Blocking I/O:同步阻塞I/O模型。
数据的读取写入必须阻塞在一个线程内等待其完成。 - NIO
New I/O: 同步非阻塞的I/O模型。
NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O *** 作方法。 NIO提供了与传统BIO模型中的Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。
BIO与NIO的区别:
(1)IO流是阻塞的,NIO流是不阻塞的。
(2)IO 面向流(Stream oriented),而 NIO 面向缓冲区(Buffer oriented)。
(3)NIO 通过Channel(通道) 进行读写。
(4) NIO有选择器,而IO没有。
- AIO
Asynchronous I/O: 异步非阻塞的IO模型 。
异步 IO 是基于事件和回调机制实现的,也就是应用 *** 作之后会直接返回,不会堵塞在那里,当后台处理完成, *** 作系统会通知相应的线程进行后续的 *** 作。
redis、nginx、netty[可设置]、kafka等热门技术都是基于epoll。
OS基础程序存在磁盘里,加载到内存之后才能run起来。
PC开机后第一个运行的程序kernel,它向下管理硬件,向上承托其它程序。
kernel内核程序进入内存之后,进行GDT【全局描述符表】注册,它划分出用户空间和内核空间。内核不能被其它程序直接修改,因为需要保证OS的安全可靠,所以开启了保护模式【基于CPU指令集rang0到3】。
程序是不能直接IO访问硬件的,内核可以。所以程序都是通过系统调用syscall【软中断】的方式去调内核进行IO【包括磁盘IO、网络IO等】。这个IO是需要成本的。如何降低IO成本,是IO发展【BIO>NIO>select>epoll>AIO等多路复用技术】的初衷。
引入一个简单的BIO代码示例。
抓取命令【抓取程序与OS发生的系统调用syscall】
strace -ff -o ./ooxx java TestSocket.java 一个输出文件就是一个线程。
查找哪个文件是主程序 grep ‘step1’ ./*
思路:如果某个程序慢,找出这个进程,看这个进程下是不是有很多线程。
执行 客户端连接命令:【网络就会多一个连接,文件描述符也多了一个socket,主线程文件就会多一个accept系统调用过程:socket、bind、listen、poll、accept【创建socket会生成一个socket文件并且生成文件描述符,套字节文件,用于存储建立链接后的数据】】
nc localhost 8090
BIOjava其实并不值钱,JVM虚拟机才是真正的有价值。凡是可以编译成字节码的语言【不止java】都可以运行在JVM上。
java是解释型语言。JVM是java的解释器,它将由java编译来的字节码文件解释成OS认识的程序,然后再去j进行系统调用。【这也是Java比C语言慢的原因】
凡是服务端应用程序,一定会有以下几个步骤:
socket、bind、listen、accept【这时如果没有客户端连接,则会阻塞,如果有多个客户端来连接,则clone新的线程来处理客户端连接:一个线程对应一个客户端,这是传统的BIO】
缺点很明显:成本高,无论是新建scoket,clone新线程[也占用栈内存,线程切换浪费CPU成本],还是读写,都需要系统调用syscall软中断。根本原因:阻塞的,bio使得不得不新建多线程。如何非阻塞?
确实可以解决早期的业务,但是随着业务越来越丰富,不得不考虑降低IO成本。
如下:
NIO上述BIO的缺陷使得不得不考虑在应用层实现NIO。
服务器 *** 作系统OS是支持非阻塞NIO的,通过执行 man 2 socket 可以证明。
如果在app应用层【比如Java NIO】可以调用OS底层的NIO,那么就实现了的应用层级别的NIO。
app应用层的NIO:New IO。OS *** 作系统的NIO:NonBlocking IO【不再创建新线程,通过死循环来轮询】。
但是NIO也有缺陷:C10K问题。比如来了10000个客户端连接,不管客户端有没有发送数据,服务端就会通过轮询【O[n]复杂度,即O[n]次syscall】所有socket文件来判断有没有发送数据【O[m]复杂度】。所以在高并发下虽然没有新建多个线程,但是多次的系统调用syscall还是依然存在。
select服务器 *** 作系统OS支持另外一种非阻塞的系统调用select,通过执行 man 2 select 可以证明。
select 多路复用器。一次性读取【O[1]复杂度】所有socket文件,处理有数据发送【O[m]复杂度】的的客户端。
缺点:一次需要传很多文件数据【虽然只syscall了一次,但是拷贝了很多数据到内核】;需要CPU主动遍历O[n]次来判断谁发送了数据。
epoll如何避免select多路复用器的两个缺陷:
一次需要传很多文件数据。→ 基于增量模式传文件数据。
需要CPU主动遍历O[n]次来判断谁发送了数据。→ 基于事件驱动模式【硬件中断。充分发挥硬件,尽量不浪费CPU】。
服务器 *** 作系统OS支持epoll,通过执行man epoll、man epoll_create、man epoll_ctr、man epoll_wait可以证明。
示例:通过strace命令追踪redis、nginx、kafka等应用程序,可以发现epoll技术的使用。
- nginx
- redis
同步细节问题:同样使用epoll,nginx为啥阻塞,redis为啥轮询?
redis是单线程的,除了IO,它还需要做其它事,比如LRU、LFU、RDB、AOF等,所以需要轮询。
nginx的work进程仅仅就是等客户端连接,,不需要做其它事情,所以阻塞。nginx的work进程仅仅就是等客户端连接,,不需要做其它事情,所以阻塞。
select、poll、epoll等都是多路复用器的实现,返回的是I/O的状态[哪个客户端连接是通的]。读写仍然需要程序自己发起,所以都是同步。
IO是个细活redis在版本6.X之前都是单线程,原子性、串行化 *** 作。
redis在版本6.X之后支持多线程,并发读、串行写【写仍然是原子性 *** 作】。
epoll 零拷贝kafka是基于JVM的。
kafka是怎么做持久化的?→零拷贝、mmap。
考虑到内核kernel,很多技术的创新都在于减少内核调用。
kafka写:用到mmap技术,将数据封装好之后通过mmap直接刷到磁盘的segment文件上,充分利用硬件支持的mmap技术,不经过syscall。
这里用到虚拟内存,虚拟内存是一种内存扩种技术,如果通过参数设置【RandomAccessFile】可以用到堆外内存,所以才能直接映射到磁盘,不会浪费内存。
kafka读:如果没有零拷贝,消费者消费数据的流程:请求给kafka,kafak调用内核,内核读磁盘,数据从磁盘返回给内核,内核拷贝给kafka,kafka再把数据write给内核的socket返回给客户端。
零拷贝技术,消费者消费数据的流程:请求给kafka,kafak调用内核的sendfile,内核读磁盘,数据从磁盘返回给内核,内核不需要再拷贝给kafka【零拷贝的前提:数据不需要加工】,直接返回到客户端。
从网络到分布式。
- 网络
高并发负载均衡:网络协议原理
高并发负载均衡:LVS的DR TUN,NAT模型推导
高并发负载均衡:LVS的DR模型试验搭建
高并发负载均衡:基于keepalived的LVS高可用搭建 - redis
redis介绍及NIO原理
redis的string类型&bitmap
redis的list、set、hash、sorted_set、skiplist
redis的消息订阅、pipeline、事务、modules、布隆过滤器、缓存LRU
redis的特久化RDB、fork、copyonwrite、AOF、RDB&AOF混合使用
redis的集群:主从复制、CAP、PAXOS、cluster分片集群
redis开发:spring.data.redis、连接、序列化、high/low api - zookeeper
zookeeper介绍、安装、shell c小i使用,基本概念验证
zookeeper原理知识,paxos、zab、角色功能、AP开发基础
zookeeper案例:分布式配置注册发现、分布式锁、ractive模式编程
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)