分类: 大数据
2018-01-30 08:44:54
原文地址:Redis源码解析--NET 作者:scq2099yt
关于Redis资料:
要看原滋原味的请点,要看有我参与的山寨货请点,当然我也不反对看。
在接下来的日子里,我会记录下我对Redis源码的一些认识,首先从Event driven programming library开始,没有理由,如果有:那就是redis.h包含的非系统头文件从#include "ae.h"开始,本系列文章以版源码为基准,言归正传吧。
一、NET分层
Redis 网络部分主要分四层:
图1 NET分层图
1、 TCP/Unix Socket层(Anet.h(117)、Anet.c(405))
(1)socket创建
anetCreateSocket:创建TCP/Unix socket,设置socket SO_REUSEADDR。
(2)socket属性设置
anetTcpNoDelay:设置是否关闭Nagle算法,Nagle算法作用点。
anetNonBlock:设置阻塞还是非阻塞。
anetTcpKeepAlive:设置是否开启协议栈心跳,协议栈心跳作用点这里。
anetSetSendBuffer:设置发送buffer大小。
(3)Connect
anetTcpGenericConnect:TCP Connect原始接口封装,输入IP地址和阻塞/非阻塞参数。
anetTcpConnect:TCP阻塞连接。
anetTcpNonBlockConnect:TCP非阻塞连接。
anetUnixGenericConnect:Unix Connect原始接口封装,输入IP地址和阻塞/非阻塞参数。
anetUnixConnect:Unix阻塞连接。
anetUnixNonBlockConnect:Unix非阻塞连接。
(4)Listen
anetListen:bind和listen(511原因请查Nginx)封装。
anetTcpServer:调用anetCreateSocket和anetListen监听连接到来。
anetUnixServer:调用anetCreateSocket和anetListen监听连接到来。
(5)Accept
anetGenericAccept:accept封装,while直到accept成功或失败才返回。
anetTcpAccept:调用anetGenericAccept,返回fd,带回IP和Port(函数参数)或错误。
anetUnixAccept:调用anetGenericAccept,返回fd,或带回错误(函数参数)。
(6)IP和host互转
anetPeerToString:由IPAddress->Host。
anetResolve:由Host->IPAddresss。
(7)格式化error
anetSetError:变长参数格式化,函数参数带回格式化后error信息。
2、 I/O模型层(Ae_select.c(72)、Ae_epoll.c(101)、Ae_kqueue.c(105))
三者都有统一的接口,功能大体类似,但也有细节差别,以epoll(参看这里)为蓝本解析如下:
aeApiState:包含epoll fd句柄和Event指针的struct,epoll和kqueue基本一致,而select是rfds和wfds集合及副本,具体见Ae_select.c代码第7~12行。
aeApiCreate:创建aeApiState,并以此初始化aeEventLoop(作用见后文)。epoll_create参数采用Linux kernel的hint值1024。
aeApiFree:close epoll fd句柄,释放malloc的aeApiState和aeEventLoop。
aeApiAddEvent:通过mask修改或者添加fd对应Event的EPOLLIN或EPOLLOUT(epoll_ctl)。
aeApiDelEvent:通过mask修改或者删除fd对应Event的EPOLLIN或EPOLLOUT(epoll_ctl)。
aeApiPoll:epoll_wait等待内核返回事件集合,填写fire事件集合用于回调AE_READABLE和AE_WRITABLE对应函数。
aeApiName:取得I/O模型字符串名称("select"、"epoll"、"kqueue")。
3、 EventLoop层(Ae.h(117)、Ae.c(405))
(1)回调函数指针
typedef void aeFileProc(struct aeEventLoop *eventLoop, int fd, void *clientData, int mask);
typedef int aeTimeProc(struct aeEventLoop *eventLoop, long long id, void *clientData);
typedef void aeEventFinalizerProc(struct aeEventLoop *eventLoop, void *clientData);
typedef void aeBeforeSleepProc(struct aeEventLoop *eventLoop);
(2)Event Struct
aeFileEvent:读写事件回调。
aeTimeEvent:定时器事件回调。
aeFiredEvent:触发事件回调。
aeEventLoop:主事件,包含读写事件、定时器事件、触发事件列表。
(3)Event接口API
aeCreateEventLoop:创建EventLoop。
aeDeleteEventLoop:删除EventLoop。
aeStop:置EventLoop stop标志。
aeCreateFileEvent:添加关注事件。
aeDeleteFileEvent:删除关注事件。
aeGetFileEvents:获取事件mask
aeGetTime:获取当前时间。
aeAddMillisecondsToNow:增加毫秒数当前时间的秒和毫秒上。
aeCreateTimeEvent:添加定时器事件。
aeDeleteTimeEvent:删除定时器事件。
SearchNearestTimer:搜索最近的定时器。
processTimeEvents:处理定时器事件,回调函数返回AE_NOMORE(-1)则删除定时器,否则更新定时器时间为回调函数返回的时间。
aeProcessEvents:处理各种事件,用最近定时器即将到来的时间作为epoll_wait的超时间,非常巧妙,如果马上到就立即返回,否则超时间到再返回。
aeMain:主main,while循环直到eventLoop->stop不为0,在调用aeProcessEvents前,先回调aeBeforeSleepProc。
aeGetApiName:获取I/O模型字符串名称("select"、"epoll"、"kqueue")。
aeSetBeforeSleepProc:设置aeBeforeSleepProc回调函数。
4、 Networking层(Networking.c(1334))
暂不表与网络层无直接关系的接口函数。
createClient:创建redisClient,有连接则设置fd为nonblocking,设置TCP_NODELAY,设置AE_READABLE对应的回调函数readQueryFromClient。
prepareClientToWrite:在发送数据给客户端时的预处理,即可以发送数据时设置AE_WRITABLE对应的回调函数sendReplyToClient;是REDIS_LUA_CLIENT时返回REDIS_OK;是fake client或者slave或者setup write handler failed时返回REDIS_ERR。
acceptCommonHandler:调用createClient,校验是否达到最大客户端数。
acceptTcpHandler:针对TCP依次调用anetTcpAccept、acceptCommonHandler。
acceptUnixHandler:针对Unix,作用同acceptTcpHandler。
freeClient:释放redisClient,删除AE_READABLE、AE_WRITABLE,断开master/slave,清除MULTI/EXEC state等。
sendReplyToClient:发送数据的回调函数,处理write逻辑。
readQueryFromClient:接收数据的回调函数,处理read逻辑。
还有一大票addReply…和get…,在此略去。
二、NET流程
以redis-cli 从redis-server get数据为例来描述整体流程。
图2 流程图
打完收工,。。。