深入理解Redis 中的事件循环

redis的事件模型详解(结合Reactor设计模式)         文章基于redis-4.0.1源码详细介绍一下redis的事件模型。       一、redis事件模型概览         redis是一个事件驱动的服务程序,在redis的服务程序中存在两种类型的事件,分别是文件事件和时间事件。文件事件是对网络通信操作的统称,时间事件是redis中定时运行的任务或者是周期性的任务(目前redis中只有serverCron这一个周期性时间事件... 阅读详情

在目前的很多服务中,由于需要持续接受客户端或者用户的输入,所以需要一个事件循环来等待并处理外部事件,这篇文章主要会介绍 Redis 中的事件循环是如何处理事件的。

在文章中,我们会先从 Redis 的实现中分析事件是如何被处理的,然后用更具象化的方式了解服务中的不同模块是如何交流的。

aeEventLoop

在分析具体代码之前,先了解一下在事件处理中处于核心部分的 aeEventLoop 到底是什么:

aeEventLoop 在 Redis 就是负责保存待处理文件事件和时间事件的结构体,其中保存大量事件执行的上下文信息,同时持有三个事件数组:

  • aeFileEvent
  • aeTimeEvent
  • aeFiredEvent

aeFileEvent 和 aeTimeEvent 中会存储监听的文件事件和时间事件,而最后的 aeFiredEvent 用于存储待处理的文件事件,我们会在后面的章节中介绍它们是如何工作的。

Redis 服务中的 EventLoop

在 redis-server 启动时,首先会初始化一些 redis 服务的配置,最后会调用 aeMain 函数陷入 aeEventLoop 循环中,等待外部事件的发生:

int main(int argc, char **argv) {  
    ...

    aeMain(server.el);
} main(int argc, char **argv) {  
    ...

    aeMain(server.el);
}

aeMain 函数其实就是一个封装的 while 循环,循环中的代码会一直运行直到 eventLoop 的 stop 被设置为 true :

void aeMain(aeEventLoop *eventLoop) {  
    eventLoop->stop = 0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}0;
    while (!eventLoop->stop) {
        if (eventLoop->beforesleep != NULL)
            eventLoop->beforesleep(eventLoop);
        aeProcessEvents(eventLoop, AE_ALL_EVENTS);
    }
}

它会不停尝试调用 aeProcessEvents 对可能存在的多种事件进行处理,而 aeProcessEvents 就是实际用于处理事件的函数:

int aeProcessEvents(aeEventLoop *eventLoop, int flags) {  
    int processed = 0, numevents;

    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        struct timeval *tvp;

        #1:计算 I/O 多路复用的等待时间 tvp

        numevents = aeApiPoll(eventLoop, tvp);
        for (int j = 0; j < numevents; j++) {
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
            int mask = eventLoop->fired[j].mask;
            int fd = eventLoop->fired[j].fd;
            int rfired = 0;

            if (fe->mask & mask & AE_READABLE) {
                rfired = 1;
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
            }
            if (fe->mask & mask & AE_WRITABLE) {
                if (!rfired || fe->wfileProc != fe->rfileProc)
                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
            }
            processed++;
        }
    }
    if (flags & AE_TIME_EVENTS) processed += processTimeEvents(eventLoop);
    return processed;
} aeProcessEvents(aeEventLoop *eventLoop, int flags) {  
    int processed = 0, numevents;

    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        struct timeval *tvp;

        #1:计算 I/O 多路复用的等待时间 tvp

        numevents = aeApiPoll(eventLoop, tvp);
        for (int j = 0; j < numevents; j++) {
            aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
            int mask = eventLoop->fired[j].mask;
            int fd = eventLoop->fired[j].fd;
            int rfired = 0;

            if (fe->mask & mask & AE_READABLE) {
                rfired = 1;
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
            }
            if (fe->mask & mask & AE_WRITABLE) {
                if (!rfired || fe->wfileProc != fe->rfileProc)
                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
            }
            processed++;
        }
    }
    if (flags & AE_TIME_EVENTS) processed += processTimeEvents(eventLoop);
    return processed;
}

上面的代码省略了 I/O 多路复用函数的等待时间,不过不会影响我们对代码的理解,整个方法大体由两部分代码组成,一部分处理文件事件,另一部分处理时间事件。

Redis 中会处理两种事件:时间事件和文件事件。

文件事件

在一般情况下, aeProcessEvents 都会先 计算最近的时间事件发生所需要等待的时间 ,然后调用 aeApiPoll 方法在这段时间中等待事件的发生,在这段时间中如果发生了文件事件,就会优先处理文件事件,否则就会一直等待,直到最近的时间事件需要触发:

numevents = aeApiPoll(eventLoop, tvp);  
for (j = 0; j < numevents; j++) {  
    aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
    int mask = eventLoop->fired[j].mask;
    int fd = eventLoop->fired[j].fd;
    int rfired = 0;

    if (fe->mask & mask & AE_READABLE) {
        rfired = 1;
        fe->rfileProc(eventLoop,fd,fe->clientData,mask);
    }
    if (fe->mask & mask & AE_WRITABLE) {
        if (!rfired || fe->wfileProc != fe->rfileProc)
            fe->wfileProc(eventLoop,fd,fe->clientData,mask);
    }
    processed++;
}for (j = 0; j < numevents; j++) {  
    aeFileEvent *fe = &eventLoop->events[eventLoop->fired[j].fd];
    int mask = eventLoop->fired[j].mask;
    int fd = eventLoop->fired[j].fd;
    int rfired = 0;

    if (fe->mask & mask & AE_READABLE) {
        rfired = 1;
        fe->rfileProc(eventLoop,fd,fe->clientData,mask);
    }
    if (fe->mask & mask & AE_WRITABLE) {
        if (!rfired || fe->wfileProc != fe->rfileProc)
            fe->wfileProc(eventLoop,fd,fe->clientData,mask);
    }
    processed++;
}

文件事件如果绑定了对应的读/写事件,就会执行对应的对应的代码,并传入事件循环、文件描述符、数据以及掩码:

fe->rfileProc(eventLoop,fd,fe->clientData,mask);  
fe->wfileProc(eventLoop,fd,fe->clientData,mask);

其中 rfileProc 和 wfileProc 就是在文件事件被创建时传入的函数指针:

int aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData) {  
    aeFileEvent *fe = &eventLoop->events[fd];

    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
} aeCreateFileEvent(aeEventLoop *eventLoop, int fd, int mask, aeFileProc *proc, void *clientData) {  
    aeFileEvent *fe = &eventLoop->events[fd];

    if (aeApiAddEvent(eventLoop, fd, mask) == -1)
        return AE_ERR;
    fe->mask |= mask;
    if (mask & AE_READABLE) fe->rfileProc = proc;
    if (mask & AE_WRITABLE) fe->wfileProc = proc;
    fe->clientData = clientData;
    if (fd > eventLoop->maxfd)
        eventLoop->maxfd = fd;
    return AE_OK;
}

需要注意的是,传入的 proc 函数会在对应的 mask 位事件发生时执行。

时间事件

在 Redis 中会发生两种时间事件:

  • 一种是定时事件,每隔一段时间会执行一次;
  • 另一种是非定时事件,只会在某个时间点执行一次;

时间事件的处理在 processTimeEvents 中进行,我们会分三部分分析这个方法的实现:

static int processTimeEvents(aeEventLoop *eventLoop) {  
    int processed = 0;
    aeTimeEvent *te, *prev;
    long long maxId;
    time_t now = time(NULL);

    if (now < eventLoop->lastTime) {
        te = eventLoop->timeEventHead;
        while(te) {
            te->when_sec = 0;
            te = te->next;
        }
    }
    eventLoop->lastTime = now;int processTimeEvents(aeEventLoop *eventLoop) {  
    int processed = 0;
    aeTimeEvent *te, *prev;
    long long maxId;
    time_t now = time(NULL);

    if (now < eventLoop->lastTime) {
        te = eventLoop->timeEventHead;
        while(te) {
            te->when_sec = 0;
            te = te->next;
        }
    }
    eventLoop->lastTime = now;

由于对系统时间的调整会影响当前时间的获取,进而影响时间事件的执行;如果系统时间先被设置到了未来的时间,又设置成正确的值,这就会导致 时间事件会随机延迟一段时间执行 ,也就是说,时间事件不会按照预期的安排尽早执行,而 eventLoop中的 lastTime 就是用于检测上述情况的变量:

typedef struct aeEventLoop {  
    ...
    time_t lastTime;     /* Used to detect system clock skew */
    ...
} aeEventLoop; struct aeEventLoop {  
    ...
    time_t lastTime;     /* Used to detect system clock skew */
    ...
} aeEventLoop;

如果发现了系统时间被改变(小于上次 processTimeEvents 函数执行的开始时间),就会强制所有时间事件尽早执行。

prev = NULL;
    te = eventLoop->timeEventHead;
    maxId = eventLoop->timeEventNextId-1;
    while(te) {
        long now_sec, now_ms;
        long long id;

        if (te->id == AE_DELETED_EVENT_ID) {
            aeTimeEvent *next = te->next;
            if (prev == NULL)
                eventLoop->timeEventHead = te->next;
            else
                prev->next = te->next;
            if (te->finalizerProc)
                te->finalizerProc(eventLoop, te->clientData);
            zfree(te);
            te = next;
            continue;
        }NULL;
    te = eventLoop->timeEventHead;
    maxId = eventLoop->timeEventNextId-1;
    while(te) {
        long now_sec, now_ms;
        long long id;

        if (te->id == AE_DELETED_EVENT_ID) {
            aeTimeEvent *next = te->next;
            if (prev == NULL)
                eventLoop->timeEventHead = te->next;
            else
                prev->next = te->next;
            if (te->finalizerProc)
                te->finalizerProc(eventLoop, te->clientData);
            zfree(te);
            te = next;
            continue;
        }

Redis 处理时间事件时,不会在当前循环中直接移除不再需要执行的事件,而是会在当前循环中将时间事件的 id 设置为 AE_DELETED_EVENT_ID ,然后再下一个循环中删除,并执行绑定的 finalizerProc 。

aeGetTime(&now_sec, &now_ms);
        if (now_sec > te->when_sec ||
            (now_sec == te->when_sec && now_ms >= te->when_ms))
        {
            int retval;

            id = te->id;
            retval = te->timeProc(eventLoop, id, te->clientData);
            processed++;
            if (retval != AE_NOMORE) {
                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
            } else {
                te->id = AE_DELETED_EVENT_ID;
            }
        }
        prev = te;
        te = te->next;
    }
    return processed;
}if (now_sec > te->when_sec ||
            (now_sec == te->when_sec && now_ms >= te->when_ms))
        {
            int retval;

            id = te->id;
            retval = te->timeProc(eventLoop, id, te->clientData);
            processed++;
            if (retval != AE_NOMORE) {
                aeAddMillisecondsToNow(retval,&te->when_sec,&te->when_ms);
            } else {
                te->id = AE_DELETED_EVENT_ID;
            }
        }
        prev = te;
        te = te->next;
    }
    return processed;
}

在移除不需要执行的时间事件之后,我们就开始通过比较时间来判断是否需要调用 timeProc 函数, timeProc 函数的返回值 retval 为时间事件执行的时间间隔:

  • retval == AE_NOMORE :将时间事件的 id 设置为 AE_DELETED_EVENT_ID ,等待下次 aeProcessEvents 执行时将事件清除;
  • retval != AE_NOMORE :修改当前时间事件的执行时间并重复利用当前的时间事件;

以使用 aeCreateTimeEvent 一个创建的简单时间事件为例:

aeCreateTimeEvent(config.el,1,showThroughput,NULL,NULL)1,showThroughput,NULL,NULL)

时间事件对应的函数 showThroughput 在每次执行时会返回一个数字,也就是该事件发生的时间间隔:

int showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData) {  
    ...
    float dt = (float)(mstime()-config.start)/1000.0;
    float rps = (float)config.requests_finished/dt;
    printf("%s: %.2f\r", config.title, rps);
    fflush(stdout);
    return 250; /* every 250ms */
} showThroughput(struct aeEventLoop *eventLoop, long long id, void *clientData) {  
    ...
    float dt = (float)(mstime()-config.start)/1000.0;
    float rps = (float)config.requests_finished/dt;
    printf("%s: %.2f\r", config.title, rps);
    fflush(stdout);
    return 250; /* every 250ms */
}

这样就不需要重新 malloc 一块相同大小的内存,提高了时间事件处理的性能,并减少了内存的使用量。

我们对 Redis 中对时间事件的处理以流程图的形式简单总结一下:

创建时间事件的方法实现其实非常简单,在这里不想过多分析这个方法,唯一需要注意的就是时间事件的 id 跟数据库中的大多数主键都是递增的:

long long aeCreateTimeEvent(aeEventLoop *eventLoop, long long milliseconds,  
        aeTimeProc *proc, void *clientData,
        aeEventFinalizerProc *finalizerProc) {
    long long id = eventLoop->timeEventNextId++;
    aeTimeEvent *te;

    te = zmalloc(sizeof(*te));
    if (te == NULL) return AE_ERR;
    te->id = id;
    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
    te->timeProc = proc;
    te->finalizerProc = finalizerProc;
    te->clientData = clientData;
    te->next = eventLoop->timeEventHead;
    eventLoop->timeEventHead = te;
    return id;
}proc, void *clientData,
        aeEventFinalizerProc *finalizerProc) {
    long long id = eventLoop->timeEventNextId++;
    aeTimeEvent *te;

    te = zmalloc(sizeof(*te));
    if (te == NULL) return AE_ERR;
    te->id = id;
    aeAddMillisecondsToNow(milliseconds,&te->when_sec,&te->when_ms);
    te->timeProc = proc;
    te->finalizerProc = finalizerProc;
    te->clientData = clientData;
    te->next = eventLoop->timeEventHead;
    eventLoop->timeEventHead = te;
    return id;
}

事件的处理

上一章节我们已经从代码的角度对 Redis 中事件的处理有一定的了解,在这里,我想从更高的角度来观察 Redis 对于事件的处理是怎么进行的。

整个 Redis 服务在启动之后会陷入一个巨大的 while 循环,不停地执行 processEvents 方法处理文件事件 fe 和时间事件 te 。

有关 Redis 中的 I/O 多路复用模块可以看这篇文章 Redis 和 I/O 多路复用 。

当文件事件触发时会被标记为 “红色” 交由 processEvents 方法处理,而时间事件的处理都会交给 processTimeEvents 这一子方法:

在每个事件循环中 Redis 都会先处理文件事件,然后再处理时间事件直到整个循环停止, processEvents 和 processTimeEvents 作为 Redis 中发生事件的消费者,每次都会从“事件池”中拉去待处理的事件进行消费。

文件事件的处理

由于文件事件触发条件较多,并且 OS 底层实现差异性较大,底层的 I/O 多路复用模块使用了 eventLoop->aeFiredEvent 保存对应的文件描述符以及事件,将信息传递给上层进行处理,并抹平了底层实现的差异。

整个 I/O 多路复用模块在事件循环看来就是一个输入事件、输出 aeFiredEvent 数组的一个黑箱:

在这个黑箱中,我们使用 aeCreateFileEvent 、  aeDeleteFileEvent 来添加删除需要监听的文件描述符以及事件。

在对应事件发生时,当前单元格会“变色”表示发生了可读(黄色)或可写(绿色)事件,调用 aeApiPoll 时会把对应的文件描述符和事件放入 aeFiredEvent 数组,并在 processEvents 方法中执行事件对应的回调。

时间事件的处理

时间事件的处理相比文件事件就容易多了,每次 processTimeEvents 方法调用时都会对整个 timeEventHead 数组进行遍历:

遍历的过程中会将时间的触发时间与当前时间比较,然后执行时间对应的 timeProc ,并根据 timeProc 的返回值修改当前事件的参数,并在下一个循环的遍历中移除不再执行的时间事件。

总结

笔者对于文章中两个模块的展示顺序考虑了比较久的时间,最后还是觉得,目前这样的顺序更易于理解。

Redis 对于事件的处理方式十分精巧,通过传入函数指针以及返回值的方式,将时间事件移除的控制权交给了需要执行的处理器 timeProc ,在 processTimeEvents 设置 aeApiPoll 超时时间也十分巧妙,充分地利用了每一次事件循环,防止过多的无用的空转,并且保证了该方法不会阻塞太长时间。

事件循环的机制并不能时间事件准确地在某一个时间点一定执行,往往会比实际约定处理的时间稍微晚一些。

Reference

其它

Follow: Draveness · GitHub

Source: http://draveness.me/redis-eventloop

 【关于我们】

才淇(微信公众号:caiqicehua),专注于国内各大互联网公司社会招聘内推。每天更新最新互联网名企(包括但不限于今日头条、网易游戏、BAT、网易互联网、小米、京东、乐视、携程等名企)内推信息,有技术岗、有产品岗、有运营岗、有设计岗、有交互岗、有销售岗,更有其他N多相关岗位!更多内推信息请扫描以下二维码关注查阅。

 

Redis服务端初始化流程 参考链接:https://redissrc.readthedocs.io/en/latest/init/server.html https://www.jianshu.com/p/1166288c6d32?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation https://bl... 阅读详情

相关推荐

Redis事件循环与网络通信机制详解

Redis 的事件循环与网络通信机制是其高性能的关键所在。通过采用事件驱动模型、非阻塞 I/O 和多路复用技术,以及高效的通信协议,Redis 能够在处理大量并发连接的同时保持低延迟和高吞吐量。深入理解这些底层机制有助于我们更好地使用和优化 Redis,以及在开发类似的高性能应用程序时借鉴其设计思想。

yonggeit的博客 3078

【redis原理】redis事件机制

redis事件机制分析,涉及文件事件和时间事件,主要涉及源码分析

dylan的博客 846

【SAP GUI 脚本 VBA】实战:构建Excel驱动的SAP自动化操作平台

本文详细介绍了如何利用VBA和SAP GUI脚本构建Excel驱动的SAP自动化操作平台,实现高频事务码的标准化封装和批量处理。通过实战案例展示物料主数据查询(MM03)、会计凭证修改(FB02)等场景的自动化实现,帮助用户提升10倍+操作效率,同时确保数据可追溯性和操作安全性。

weixin_33748818的博客 503

redis详解

工作原理 事件模型 redis以其高性能而闻名,它最大程度地利用了 单线程,非阻塞,多路复用的I/O模型 来快速地处理请求。 通信协议 Redis基本上就是一个接受并处理来自客户端请求的非阻塞、I/O复用的TCP服务器。我们可以使用各种编程语言通过TCP协议与Redis进行通信。对Redis来说,这种通信协议叫做Redis Serialization Protocal(RESP,Redis序列...

YABAJ的专栏 1941

论文Agile Autonomy: Learning High-Speed Flight in the Wild开源代码运行记录

​ Agile Autonomy: Learning High-Speed Flight in the Wild Learning High-Speed Flight in the Wild 论文链接:https://www.science.org/doi/10.1126/scirobotics.abg5810 代码:https://github.com/uzh-rpg/agile_autonomy 国内复现:https://github.com/Howardcl/AgileAutonomyPX4

Hanamizukimr的博客 1488

科研党必备:用Python+Excel批量下载文献的保姆级教程(附常见错误解决)

本文提供了一套利用Python和Excel实现外文文献批量下载的自动化解决方案。教程详细介绍了如何从数据库导出并清洗DOI信息,编写健壮的Python脚本进行批量下载,并包含错误处理与进阶优化技巧,旨在帮助科研人员将重复性劳动自动化,大幅提升文献收集效率。

weixin_29050513的博客 305

redis入门(二):redis的命令集合

redis 中根据不同的数据结构,分为了不同的api操作。有String 操作,Hash 操作,List 操作,Set 操作,不同的数据结构有着不同的数据实现形式在api的表现也是不同的。 redis的所有命令api在 官网 已经全部展示出来,而redis-py 的基本上都已经实现了上面所有的命令,除了特殊几个以外:SELECT,DEL,MULTI/EXEC,SUBSCRIBE/LISTEN``SCAN/SSCAN/HSCAN/ZSCAN。 下面是几个数据结构常用的命令: String 操作 set()

haleycat的博客 1376

数学建模——相关系数(4)——斯皮尔曼相关系数(spearman)

文章目录引述斯皮尔曼相关系数(spearman)定义 引述 经过之前几节的学习,我们了解并掌握了皮尔逊相关系数。在学习中我们发现,皮尔逊相关系数的使用条件相当苛刻:两组变量必须是连续数据、呈现正态分布,且两者间必须成线性关系。如果我们在数学建模中拿到一组数据无法满足以上条件,那么有没有其他的方法去判断两组变量之间的相关性呢?答案是肯定的,它就是斯皮尔曼相关系数(spearman). 斯皮尔曼相关系...

东 6万+

深入解析Redis中的事件

深入解析Redis中的事件

pleasenoerror的博客 276

AutoSAR配置与实践(基础篇)2.5 RTE对数据一致性的管理

SWC内部不同的Runnable访问共同全局变量,Runnable类似C文件中的函数,这些函数如果被放在不同Task上运行, 可能出现出现同一时刻多个函数共同运行的情况。可以通过为受影响的任务分配相同的优先级,为受影响的任务分配相同的内部操作系统资源,或者将OS的任务配置为非抢占性来实现。数据一致性:当多个操作同时读写同一个数据,由于任务的抢占,出现了数据被篡改的情况,造成非预期的数据结果。SWC内部变量保护,限定局变量的作用域在变量的作用域在SWC的不同runnable间。

nihaoljq2010的博客 2628

2026年瑞芯微数字IC设计笔试试卷带答案

本文主要考察数字集成电路设计相关知识,包括低功耗设计、时序分析、跨时钟域处理、总线协议等内容。试题分为单选题、多选题、代码设计题和简答题四部分。 单选题涉及门控时钟降低动态功耗、静态时序分析的限制、跨时钟域同步方案、时序违例修复方法、AHB总线信号、异步FIFO设计、半导体工艺电阻特性、二进制数转换、同步/异步复位比较以及分频电路设计等知识点。 多选题重点考查动态功耗优化技术、亚稳态特性、异步FIFO设计规范、Verilog可综合代码编写要求以及AXI总线协议特点等。 代码设计题要求实现一个同步FIFO和3

Open 215

Redis面试精讲 Day 26:Redis源码分析:事件循环与网络模型

术语 | 定义 | 作用 || 事件循环(Event Loop) | Redis主线程中持续运行的循环体,负责监听并处理各类事件 | 驱动整个服务的运行 || 文件事件(File Event) | 对Socket读写事件的封装,如客户端连接、命令读取、响应发送 | 处理网络I/O操作 || 时间事件(Time Event) | 定时任务事件,如服务器定时任务、过期键清理 | 执行周期性或延迟操作 |

在未来等你的专栏 968

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

redis启动_当你启动 Redis 的时候,Redis 做了什么?

点击▲关注 “ITPUB” 给公众号标星置顶更多精彩 第一时间直达假设 Redis 安装在了 /usr/local/ 目录下,那么启动 Redis 是通过执行 /usr/local/bin/redis-server -c xxx.conf 的方式执行。redis-server 是一个通过编译 server.c 文件生成的程序,因此想了解redis是怎么启动的,应该从 server.c...

weixin_39609770的博客 188

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

QuantConnect Lean算法交易引擎:3步跑通首次回测的完整指南

QuantConnect Lean算法交易引擎是一款开源的量化交易框架,它把数据解析、订单撮合、费用滑点、期货展期这些脏活全部分离出去,让你只写策略逻辑本身。这篇文章带你从零走到:克隆代码、编译启动引擎、跑通第一个回测,并给出避开新手坑的具体做法。 [![Lean算法交易引擎系统架构总览](https://raw.gitcode.com/GitHub_Trending/le/Lean/raw/0

gitblog_00085的博客 800

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

关于redis启动流程介绍

转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/94.html?1455870894 1. 准备运行环境 * 设置oom handler​,zmalloc分配内存失败时调用* 初始化随机种子,用于生成随机数* 将server参数初始化为默认值* 创建命令与处理函数的映射表 2. 解析命令行参数、loadServe...

weixin_30783913的博客 75

Redis 源码分类

本文对Redis 8.0版本的110个C语言源码文件进行了系统分类,共划分为16个功能类别。主要包括:数据结构实现(如双向链表、哈希表等)、彩蛋功能、数据类型命令、事件循环、内存管理、持久化、CRC校验算法、命令行工具、网络IO、算法实现、Lua脚本支持、分布式功能、调试工具、字符串处理、监控系统以及服务器核心模块。这种分类方法有助于开发者更高效地研究和理解Redis源码结构,为后续深入分析各功能模块提供了清晰的框架。文章还展示了部分彩蛋功能的实现效果图,体现了Redis源码的趣味性。

你的指尖有改变世界的力量 734

10个关键技巧:通过annotated_redis_source掌握Redis核心实现原理

Redis作为一款高性能的内存数据库,其核心实现原理一直是开发者关注的焦点。annotated_redis_source项目提供了带有详细注释的Redis 2.6源码,为深入理解Redis内部机制提供了绝佳途径。本文将分享10个关键技巧,帮助你通过这份注释源码轻松掌握Redis核心实现原理。 ## 技巧1:从数据结构入手,理解Redis存储本质 Redis的高效性能离不开其精心设计的数据结构。

gitblog_00008的博客 569

redis启动流程介绍

转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/114.html?1455860562 1. 准备运行环境 * 设置oom handler​,zmalloc分配内存失败时调用* 初始化随机种子,用于生成随机数* 将server参数初始化为默认值* 创建命令与处理函数的映射表 2. 解析命令行参数、loadServ...

weixin_30788239的博客 136

redis源码分析

主流程 server.c 全局变量server,只有一个,记录本服务的信息。 1.spt_init初始化全局环境变量,关联变量environ 2.设置时区 3.初始化oom_handler 4.其他初始化:随机数种子,crc64,acl,服务配置,tls,哨兵模式,帮助,加载配置文件,moduleInitModulesSystem模块系统初始化 5.关键函数initServer,初始化核心。 6.创建pid文件 7.设置cpu亲和性 8.执行事件循环核心 9.异常情况退出释放结束。 initServer 1

u013860464的博客 2487

redis源码阅读二-终于把redis的启动流程搞明白了

阅读redis的源码永远也绕不过它的启动。希望这篇文章能给大家带来一些东西。

yxkong的专栏 783

redis自顶向下源码阅读(七)——服务器初始化

1. 信号量操作 忽略两个信号量 signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); SIGHUP: hong up,挂断。本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。 SIGPIPE: 对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程. SIG_IGN: 表示对前面的信号量参数进

江江的博客 338

深入理解Redis

深入理解Redis

Hello World 4043

深入理解Redis【建议收藏】

行走江湖多年,Redis中间件被问到多次,这也印证了Redis在企业中的价值和地位,也是普通程序员和高低程序员的分水岭!

码农佩奇,一个热爱生活的程序猿! 1385
上一篇: 用 Node.js 和 AWS Lambda 创建无服务器的微服务
下一篇: 资深互联网运营经理分析微信公众号如何通过内容运营来打动用户
caiqicehua
博客等级 码龄10年 153粉丝 9原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值