1.连接建立
redis主线程
void acMain(aeEventLoop *eventloop) { //主线程事件循环体
eventloop->stop=0
while(!eventloop->stop){
acProcessEvents(eventloop,AE_ALL_EVENTS | AE_CALL_BEFORE_SLEEP | AE_CALL_AFTER_SLEEP);
}
}
aeApiPoll相当于epoll_wait的封装
建立连接从两个方向
(1)来自客户端连接。accept-->epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd(注册为可读事件EPOLLIN),&ev);
(2)连接外部服务。connect=-1 & errno=EINPROGREE-->epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd(注册为可写事件EPOLLOUT),&ev);--->EPOLLOUT触发,说明连接建立成功
2.连接断开
(1)来自客户端断开。客户端发送FIN包,写通道关闭 --》服务端收到FIN,read=0或者将事件注册为EPOLLRDHUP--》支持半关闭,关闭服务器读通道 --》不支持半关闭,直接close()
(2)来自服务器断开。shutdown(SHUT_WR),发送FIN给客户端- 或者 write()==-1 && errno==EPIPE 说明此时写通道关闭,但可能收到数据
3.消息到达
(1)read==0 收到客户端关闭的FIN包
(2)read>0 正常,处理相应的业务逻辑
(3)read==-1
errno== EWOULDBLOCK 非阻塞IO,读缓冲区为空
errno==EINTR 系统调用被中断打断,重试
errno==ETIMEDOUT tcp-keepalive探测包超时
拓展:tcp-keeplive在传输层实现,在不存活的时候,不知道是进程阻塞还是死锁造成的,所以一般不用 然后自己在传输层实现心跳检测包,每隔10秒发送一次心跳包,3次没有close()
tcp-keeplive的设置在/etc/sysctl.conf中配置,配置间隔多久,多少次,一共多长时间,
一般在下列情况使用,使用心跳检测
(注1)数据库间主从复制,使用心跳检测
(注2)客户端与服务器,使用心跳检测
(注3)客户端-》反向代理--》上游服务器;反向代理与上游服务器使用探活检测 nginx
(注4)客户端--》数据库,使用探活检测
4.消息发送完毕
(1)write>0 正常,接着处理正常的业务逻辑
(2)write==-1 errno == EWOULDBLOCK 写空间不够,将fd注册为EPOLLOUT可写事件,EPOLLOUT被触发,再次发送
errno == EINTR 此时系统调用被中断打断,重试
errno == EPIPE 写通道关闭
几种模型
1.单reactor redis 6.0多线程 (处理数据解压缩、压缩 RESP协议,数据解压缩耗CPU,影响网络处理) 代表redis 内存数据库,操作redis当中的数据结构
2.单reactor模型+任务队列+线程池。 skynet
有可读事件,将事件放入任务队列,然后由线程池取任务队列中内容,工作线程去做消耗
将网络事件与业务进行了分离
3.多reactor +多线程 memcached中应用 one eventloop per thread 每个线程都有一个事件循环
多线程理accept防止抖动
4.多reactor nginx中应用
多进程处理accept全连接队列,防止抖动
memcached中源码网络事件循环
while(!stop_main_loop){ //调用的libenent
if(event_base_loop(main_loop,EVLOOP_ONCE )!=0){
retal = EXIT_FAILURE;
break;
}