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