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空间的释放

异步任务处理模型

    每个线程都有一个阻塞队列,阻塞队列中没有任务,线程就阻塞。主线程将任务提交到相应线程的阻塞队列中同时唤醒线程从阻塞队列中提取任务进行处理。异步任务处理模型的流程图如下:

异步任务处理模型的实现

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]--;
    }
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值