赞
踩
客户端:socket、connnect、send、recv
服务端:socket、bind、listen、accept、recv、send、close
阻塞IO和非阻塞IO:没有数据的时候是不是立即返回
1.组成:非阻塞IO+IO多路复用(复用的网络线程)
linux下的IO多路复用模型
select:需要轮询检查每一个IO是否有事件。
poll:将读、写、错误分成三种类型去检测
epoll:将读、写、操作事件集中到一个阻塞中去完成,并且返回有事件的IO。
2.特征:事件循环+事件驱动/回调
3.缺点:容易分裂业务逻辑
4.解决办法:协程:同步非阻塞
//创建监听套接字 listenfd=socket(); //绑定监听套接字 bind(listenfd,addr); //创建epollIO多路复用 efd=epoll_create(0); //将listenfd放到efd中管理 epoll_clt(efd,epoll_ctl_add,listenfd,&ev); while(1){ epoll_event ev[]; int nevents=epoll_wait(efd,ev,events,timeout); for(int i;i<nevents;i++) { epoll_event *e=&ev[i]; if(e->fd==listenfd) { //建立连接的事件 }else {//读、写、错误事件 if(e->events & epollin||epollhub){//可读 } if(e->events & epollout){//可写 } if(e->events & epollerr){//错误事件 } } } }
epoll原理
epoll_create() 在内核中创建了一个红黑树,去管理注册的事件;还创建了一个就绪队列,这是一个双向队列,如果事件发生了,放到就绪队列里。
epoll_ctl() 对红黑树进行操作。
epoll_wait() 将网络中就绪的事件拷贝到events数组中。
reactor的变种1:nginx去fork出多个进程去处理。
master进程fork()出多个worker进程(CPU核心数) 。通过共享内存+进程锁**,worker进程依次获得处理连接的权利。优化:如果一个worker进程处理了7/8*conntect,就不会去获得进程锁。负载均衡。
问题1:nginx为什么使用多进程?
nginx是静态web服务器,处理http请求,连接之间一般没有关联(除了游戏,玩家之间的交互),隔离性强,使用slab共享内存(在共享内存中加一些数据结构:数组、红黑树),简单数据统一。
运行环境隔离性+数据统一性
问题2:nginx为什么不适用多进程+多线程?
fork()不能在多线程中使用,linux当中只能克隆当前线程,不能克隆其他线程的内容:malloc。其它系统调用,加锁。
reactor的变种2:reactor+队列+线程池:skynet,线程池消耗队列,处理业务逻辑。
每一个线程都有一个循环
重点:1.accept一个线程;2.读、写、错误另起线程。
注:clientfd%io_threads分配;另起的线程需要创建epoll对象。
概述:监听线程和处理线程分开,每个线程创建一个epoll,loop循环就是这个epoll。
one loop per thread的变种1:one loop per thread+队列+线程池(会有一定的延时)
比较
1.reactor:突然大量的连接或流量涌入,会降低吞吐率。
原因:因为epoll_wait的处理能力有限,大量的连接涌入不行;因为大量的流量涌入会导致阻塞操作,会影响其他的连接处理。redis,redis6.0引入多线程,只是解决协议序列化的问题。
2.one loop per thread ,memcached-KV数据库,数据结构比较简单,加锁简单。
问题3:redis为什么选择reactor而不是用one loop per thread:
银行 epoll
门 accept
柜台 网络IO处理,可能包含业务处理
单线程reactor:一个银行 一个柜台
reactor+线程池:一个银行 多个柜台
one loop per thread:多个银行,每个银行一个柜台
one loop per thread+线程池:one loop per thread,每个银行,每个银行多个柜台
阻塞IO和给阻塞IO
1.阻塞在网络线程;
2.IO操作在没有数据到达时是否立即返回;
3.创建fd的时候,决定了是否阻塞。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。