【Redis-6.0.8】Redis中的定时任务

本文深入探讨Redis中的定时任务机制,包括初始化过程中的时间事件创建、关键数据结构aeTimeEvent及aeEventLoop的详解,并分析定时任务如何与网络事件结合。

0.阅读与说明

Redis-ae事件模型

redis-ae事件驱动的源码分析

timeval使用和理解

三目运算符使用

1.初识Redis中的定时任务

1.1 看看哪里设定了定时任务

initServer函数中调用了aeCreateTimeEvent(产生时间事件)的函数:
void initServer(void) {
...
if (aeCreateTimeEvent(server.el, 1, serverCron, NULL, NULL) == AE_ERR) {
        serverPanic("Can't create event loop timers.");
        exit(1);
    }
...
}

1.2 aeCreateTimeEvent的实现代码

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->prev = NULL;
    te->next = eventLoop->timeEventHead;
    te->refcount = 0;
    if (te->next)
        te->next->prev = te;
    eventLoop->timeEventHead = te;
    return id;
}

1.3 看看aeTimeEvent(时间事件)的定义

/* Time event structure */
typedef struct aeTimeEvent {
    long long id; /* time event identifier. */
    long when_sec; /* seconds */
    long when_ms; /* milliseconds */
    aeTimeProc *timeProc;
    aeEventFinalizerProc *finalizerProc;
    void *clientData;
    struct aeTimeEvent *prev;
    struct aeTimeEvent *next;
    int refcount; /* refcount to prevent timer events from being
  		   * freed in recursive time event calls. */
} aeTimeEvent;

1.4 看看aeEventLoop中的aeTimeEvent类型的指向变量timeEventHead

/* State of an event based program */
typedef struct aeEventLoop {
    int maxfd;   /* highest file descriptor currently registered */
    int setsize; /* max number of file descriptors tracked */
    long long timeEventNextId;
    time_t lastTime;     /* Used to detect system clock skew */
    aeFileEvent *events; /* Registered events */
    aeFiredEvent *fired; /* Fired events */
    aeTimeEvent *timeEventHead;
    int stop;
    void *apidata; /* This is used for polling API specific data */
    aeBeforeSleepProc *beforesleep;
    aeBeforeSleepProc *aftersleep;
    int flags;
} aeEventLoop;

 

 

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

    /* Nothing to do? return ASAP */
    if (!(flags & AE_TIME_EVENTS) && !(flags & AE_FILE_EVENTS)) return 0;

    /* Note that we want call select() even if there are no
     * file events to process as long as we want to process time
     * events, in order to sleep until the next time event is ready
     * to fire. */
    if (eventLoop->maxfd != -1 ||
        ((flags & AE_TIME_EVENTS) && !(flags & AE_DONT_WAIT))) {
        int j;
        aeTimeEvent *shortest = NULL;
        struct timeval tv, *tvp;

        if (flags & AE_TIME_EVENTS && !(flags & AE_DONT_WAIT))
            shortest = aeSearchNearestTimer(eventLoop);
        if (shortest) {
            long now_sec, now_ms;

            aeGetTime(&now_sec, &now_ms);
            tvp = &tv;

            /* How many milliseconds we need to wait for the next
             * time event to fire? */
            long long ms =
                (shortest->when_sec - now_sec)*1000 +
                shortest->when_ms - now_ms;

            if (ms > 0) {
                tvp->tv_sec = ms/1000;
                tvp->tv_usec = (ms % 1000)*1000;
            } else {
                tvp->tv_sec = 0;
                tvp->tv_usec = 0;
            }
        } else {
            /* If we have to check for events but need to return
             * ASAP because of AE_DONT_WAIT we need to set the timeout
             * to zero */
            if (flags & AE_DONT_WAIT) {
                tv.tv_sec = tv.tv_usec = 0;
                tvp = &tv;
            } else {
                /* Otherwise we can block */
                tvp = NULL; /* wait forever */
            }
        }

        if (eventLoop->flags & AE_DONT_WAIT) {
            tv.tv_sec = tv.tv_usec = 0;
            tvp = &tv;
        }

        if (eventLoop->beforesleep != NULL && flags & AE_CALL_BEFORE_SLEEP)
            eventLoop->beforesleep(eventLoop);

        /* Call the multiplexing API, will return only on timeout or when
         * some event fires. */
        numevents = aeApiPoll(eventLoop, tvp);

        /* After sleep callback. */
        if (eventLoop->aftersleep != NULL && flags & AE_CALL_AFTER_SLEEP)
            eventLoop->aftersleep(eventLoop);

        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 fired = 0; /* Number of events fired for current fd. */

            /* Normally we execute the readable event first, and the writable
             * event laster. This is useful as sometimes we may be able
             * to serve the reply of a query immediately after processing the
             * query.
             *
             * However if AE_BARRIER is set in the mask, our application is
             * asking us to do the reverse: never fire the writable event
             * after the readable. In such a case, we invert the calls.
             * This is useful when, for instance, we want to do things
             * in the beforeSleep() hook, like fsynching a file to disk,
             * before replying to a client. */
            int invert = fe->mask & AE_BARRIER;

            /* Note the "fe->mask & mask & ..." code: maybe an already
             * processed event removed an element that fired and we still
             * didn't processed, so we check if the event is still valid.
             *
             * Fire the readable event if the call sequence is not
             * inverted. */
            if (!invert && fe->mask & mask & AE_READABLE) {
                fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                fired++;
                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
            }

            /* Fire the writable event. */
            if (fe->mask & mask & AE_WRITABLE) {
                if (!fired || fe->wfileProc != fe->rfileProc) {
                    fe->wfileProc(eventLoop,fd,fe->clientData,mask);
                    fired++;
                }
            }

            /* If we have to invert the call, fire the readable event now
             * after the writable one. */
            if (invert) {
                fe = &eventLoop->events[fd]; /* Refresh in case of resize. */
                if ((fe->mask & mask & AE_READABLE) &&
                    (!fired || fe->wfileProc != fe->rfileProc))
                {
                    fe->rfileProc(eventLoop,fd,fe->clientData,mask);
                    fired++;
                }
            }

            processed++;
        }
    }
    /* Check time events */
    if (flags & AE_TIME_EVENTS)
        processed += processTimeEvents(eventLoop);

    return processed; /* return the number of processed file/time events */
}

 

  • 如果在一定时间内没有收到网络时间,就可以处理定时任务了,在下面这段函数的epoll_wait中,网络消息和定时消息产生了关联.
static int aeApiPoll(aeEventLoop *eventLoop, struct timeval *tvp) {
    aeApiState *state = eventLoop->apidata;
    int retval, numevents = 0;
   
    /* 如果在一定时间内没有收到网络时间,就可以处理定时任务了 */
    retval = epoll_wait(state->epfd,state->events,eventLoop->setsize,
            tvp ? (tvp->tv_sec*1000 + tvp->tv_usec/1000) : -1);
    if (retval > 0) {
        int j;

        numevents = retval;
        for (j = 0; j < numevents; j++) {
            int mask = 0;
            struct epoll_event *e = state->events+j;

            if (e->events & EPOLLIN) mask |= AE_READABLE;
            if (e->events & EPOLLOUT) mask |= AE_WRITABLE;
            if (e->events & EPOLLERR) mask |= AE_WRITABLE|AE_READABLE;
            if (e->events & EPOLLHUP) mask |= AE_WRITABLE|AE_READABLE;
            eventLoop->fired[j].fd = e->data.fd;
            eventLoop->fired[j].mask = mask;
        }
    }
    return numevents;
}

 

<think>好的,我现在需要帮助用户解决在Ubuntu上安装Redis时遇到的pkg-config not found错误。首先,我得回想一下用户提供的引用内容,看看有没有相关的解决方法。用户提到了引用[1]、[2]、[4]中关于安装Redis的步骤和可能出现的错误。 首先,pkg-config是一个用来帮助编译时定位头文件和库文件的工具,很多软件在编译时会依赖它。如果系统里没有安装pkg-config,Redis的编译过程可能会报错。用户提到在Ubuntu上使用apt安装,而之前可能用的是yum,所以需要确认Ubuntu下的正确安装命令。 根据引用[1]中的信息,当出现pkg-config not found时,可能需要安装pkg-config包。在Ubuntu系统中,应该使用apt-get来安装。此外,可能还需要安装其他依赖库,比如libssl-dev,因为Redis可能需要SSL支持,尤其是在新版本中。 接下来,用户提供的引用[2]和[4]提到了下载Redis源码、解压、编译的步骤。如果编译过程中出现错误,可能需要先安装依赖,然后执行make distclean再重新编译。例如,在引用[2]中,建议如果编译错误,先执行make distclean清除之前的编译文件,再重新make。 所以,解决步骤可能是这样的: 1. 安装pkg-config和必要的依赖库。 2. 确保所有依赖都安装完成后,进入Redis源码目录,执行make distclean。 3. 再次尝试编译和安装。 另外,用户提到的权限问题(如Permission denied)可能需要使用sudo来执行安装命令,或者确保当前用户有足够的权限写入目标目录。例如,在make install时可能需要sudo权限,或者将目录权限设置正确。 需要确认用户是否已经正确安装了所有依赖项,包括build-essential、tcl等,这些在编译Redis时也是必要的。引用[4]中的步骤提到了编译Redis源码,但没有提到依赖安装,所以可能需要补充这些步骤。 总结下来,解决方案应该是: - 使用sudo apt-get安装pkg-config和相关开发库。 - 安装Redis所需的其他依赖,如build-essential、tcl、libssl-dev等。 - 清理之前的编译文件,重新编译安装。 同时,提醒用户在安装过程中可能需要使用sudo来处理权限问题,尤其是在执行make install时。</think>### 分步解答 在Ubuntu上解决Redis安装过程中出现的`pkg-config not found`错误,需按以下步骤操作: --- #### **1. 安装pkg-config和基础编译工具** 该工具用于管理编译时依赖关系。运行以下命令: ```bash sudo apt-get update sudo apt-get install pkg-config build-essential ``` - `build-essential`包含GCC编译器、make工具等基础编译环境[^2][^4] --- #### **2. 安装Redis的依赖库** Redis依赖`libssl-dev`(TLS支持)和`tcl`(测试工具): ```bash sudo apt-get install libssl-dev tcl ``` --- #### **3. 清理并重新编译Redis** 若之前编译失败,需先清理残留文件: ```bash cd redis-* # 进入解压后的Redis源码目录 make distclean # 清除之前的编译文件[^2] ``` 重新编译并安装: ```bash make sudo make install # 需要sudo权限写入系统目录 ``` --- #### **4. 验证安装** 检查是否生成关键文件: ```bash ls /usr/local/bin/redis-server # 默认安装路径 ``` 启动Redis服务端测试: ```bash redis-server ``` --- #### **常见问题补充** - **权限问题**:若`make`阶段报`Permission denied`,可能是源码目录权限问题,尝试: ```bash sudo chown -R $USER:$USER . # 将目录所有权赋予当前用户 ``` - **网络问题**:若下载依赖包失败(如`Failed to fetch`),检查APT源配置或更换镜像源[^1] ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值