目录
理解
Redis也会有多个线程
ps -ef | grep redis-server
# redis 5498 1 0 Jan29 ? 00:53:04 /usr/bin/redis-server 0.0.0.0:6379
ps -T 5498
# PID SPID TTY TIME CMD
# 5498 5498 ? 00:53:04 redis-server
# 5498 5500 ? 00:00:00 redis-server
# 5498 5501 ? 00:00:00 redis-server
// redis.c
int main(int argc, char **argv) {
initServer();
}
void initServer() {
/* Create the serverCron() time event, that's our main way to process
* background operations. */
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
redisPanic("Can't create the serverCron time event.");
exit(1);
}
}
int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Start a scheduled AOF rewrite if this was requested by the user while
* a BGSAVE was in progress. */
if (server.rdb_child_pid == -1 && server.aof_child_pid == -1 &&
server.aof_rewrite_scheduled)
{
rewriteAppendOnlyFileBackground();
}
}
// aof.c
int rewriteAppendOnlyFileBackground(void) {
if ((childpid = fork()) == 0) {
/* Child */
char tmpfile[256];
if (rewriteAppendOnlyFile(tmpfile) == REDIS_OK) {
exitFromChild(0);
} else {
exitFromChild(1);
}
} else {
/* Parent */
return REDIS_OK;
}
return REDIS_OK;
}
int rewriteAppendOnlyFile(char *filename) {
// 遍历所有数据库
for (j = 0; j < server.dbnum; j++) {
// 遍历数据库所有键,并通过命令将它们的当前状态(值)记录到新 AOF 文件中
while((de = dictNext(di)) != NULL) {
}
}
}
Redis单线程处理事件
// redis.c
int main(int argc, char **argv) {
initServer();
}
void initServer() {
// 为 TCP 连接关联连接应答(accept)处理器
// 用于接受并应答客户端的 connect() 调用
for (j = 0; j < server.ipfd_count; j++) {
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
acceptTcpHandler,NULL) == AE_ERR)
{
redisPanic(
"Unrecoverable error creating server.ipfd file event.");
}
}
// 为本地套接字关联应答处理器
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
acceptUnixHandler,NULL) == AE_ERR) redisPanic("Unrecoverable error creating server.sofd file event.");
}
// ae.c
int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask,
aeFileProc *proc, void *clientData)
{
if (aeApiAddEvent(eventLoop, fd, mask) == -1)
return AE_ERR;
return AE_OK;
}
#ifdef HAVE_EVPORT
#include "ae_evport.c"
#else
#ifdef HAVE_EPOLL
#include "ae_epoll.c"
#else
#ifdef HAVE_KQUEUE
#include "ae_kqueue.c"
#else
#include "ae_select.c"
#endif
#endif
#endif
// config.h
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
// ae_epolll.c
static int aeApiAddEvent(aeEventLoop *eventLoop, int fd, int mask) {
if (epoll_ctl(state->epfd,op,fd,&ee) == -1) return -1;
return 0;
}
1 支持一个进程打开大数目的fd(文件描述符)
2 轮询效率更高 => select/poll会轮询所有fd 而epoll只处理就绪的fd 它有一个就绪设备的队列 每次只轮询该队列的数据
3 内存效率更高 => 使用mmap加速内核与用户空间的消息传递
git clone https://gist.github.com/be7872baa82d9b9c400b17a2b0fe5fe3.git
gcc be7872baa82d9b9c400b17a2b0fe5fe3/reactor-demo.c -o reactor-demo
./reactor-demo
telnet localhost 8080
# Accpet a new client,client addr is 127.0.0.1 .
优势
缺点
- 无法发挥多核优势(可通过单机多Redis服务解决)
参考
网友评论