redis bio异步组件的源码分析

         redis对于客户端请求的处理是基于单线程模型。但是除了请求处理之外还有很多耗时的操作,如aof持久化,rdb持久化

等等,由于为了避免数据竞争,通过主进程fork子进程进行异步处理。除了aof和rdb,其余耗时操作都在线程组中异步处理

异步任务类型

       在redis中,异步任务处理线程组封装在BIO组件中。BIO组件中目前包括三个线程,分别处理三种类型的任务:

1、文件句柄的关闭

      文件句柄的关闭对于操作系统来说是一个比较重的操作。redis在需要重新创建新的文件句柄,废弃的文件句柄实效的时候,

由异步线程来关闭释放。

2、aof文件持久化

   aof文件持久化主要有三种策略:

     1)no策略,aof文件同步由操作系统完成

     2)always策略,aof持久化完成,就进行aof文件同步

     3)everysec策略,每秒对aof文件进行同步一次,由异步线程组完成

3、懒空间释放

    内存的释放也是一个比较重的操作,因此也由异步线程组完成。异步线程组释放的内存空间主要有包括三种:

    1)对象空间的释放

    2)DB空间的异步释放

    3)slots-keys空间的释放

异步任务处理模型

    每个线程都有一个阻塞队列,阻塞队列中没有任务,线程就阻塞。主线程将任务提交到相应线程的阻塞队列中同时唤醒线程

从阻塞队列中提取任务进行处理。异步任务处理模型的流程图如下:

redis bio异步组件的源码分析_第1张图片

异步任务处理模型的实现

1、线程组的初始化

#define BIO_NUM_OPS       3
void bioInit(void) {
    for (j = 0; j < BIO_NUM_OPS; j++) {
        pthread_mutex_init(&bio_mutex[j],NULL);
        //初始化阻塞队列的有新任务的信号量
        pthread_cond_init(&bio_newjob_cond[j],NULL);
        //初始化线程完成任务的信号量
        pthread_cond_init(&bio_step_cond[j],NULL);
        bio_jobs[j] = listCreate();//初始化阻塞队列
        bio_pending[j] = 0;
    }
    /* Set the stack size as by default it may be small in some system */
    pthread_attr_init(&attr);
    pthread_attr_getstacksize(&attr,&stacksize);
    if (!stacksize) stacksize = 1; /* The world is full of Solaris Fixes */
    while (stacksize < REDIS_THREAD_STACK_SIZE) stacksize *= 2;
    pthread_attr_setstacksize(&attr, stacksize);
    for (j = 0; j < BIO_NUM_OPS; j++) {
        void *arg = (void*)(unsigned long) j;//创建线程
        if (pthread_create(&thread,&attr,bioProcessBackgroundJobs,arg) != 0) {
            exit(1);
        }
        bio_threads[j] = thread;
    }
}
2、主线提交任务

//types是任务类型
void bioCreateBackgroundJob(int type, void *arg1, void *arg2, void *arg3) {
    struct bio_job *job = zmalloc(sizeof(*job));
    job->time = time(NULL);
    job->arg1 = arg1;
    job->arg2 = arg2;
    job->arg3 = arg3;
    pthread_mutex_lock(&bio_mutex[type]);
    listAddNodeTail(bio_jobs[type],job);//提交任务到阻塞队列
    bio_pending[type]++;
    pthread_cond_signal(&bio_newjob_cond[type]);//通过信号量通知线程处理任务
    pthread_mutex_unlock(&bio_mutex[type]);
}
3、任务线程

void *bioProcessBackgroundJobs(void *arg) {
    ……
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
    pthread_mutex_lock(&bio_mutex[type]);
    while(1) {//循环处理阻塞队列中任务
        listNode *ln;
        //阻塞队列中没有任务,线程阻塞
        if (listLength(bio_jobs[type]) == 0) {
            pthread_cond_wait(&bio_newjob_cond[type],&bio_mutex[type]);
            continue;
        }
        /* Pop the job from the queue. */
        ln = listFirst(bio_jobs[type]);
        job = ln->value;
        pthread_mutex_unlock(&bio_mutex[type]);
        /* 根据任务类型进行相应的业务逻辑处理*/
        if (type == BIO_CLOSE_FILE) {
            close((long)job->arg1);//文件句柄的关闭
        } else if (type == BIO_AOF_FSYNC) {
            aof_fsync((long)job->arg1);//aof文件持久化
        } else if (type == BIO_LAZY_FREE) {//空间懒释放
            if (job->arg1)//对象空间释放
                lazyfreeFreeObjectFromBioThread(job->arg1);
            else if (job->arg2 && job->arg3)//DB空间释放
                lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);
            else if (job->arg3)//slots-keys空间释放
                lazyfreeFreeSlotsMapFromBioThread(job->arg3);
        } 
        /*广播任务完成的信号 */
        pthread_cond_broadcast(&bio_step_cond[type]);
        //从阻塞队列中删除任务
        pthread_mutex_lock(&bio_mutex[type]);
        listDelNode(bio_jobs[type],ln);
        bio_pending[type]--;
    }
}


你可能感兴趣的:(redis,redis源码)