Redis是基于单线程实现,但是速度却不慢,原因有二:
1)基于内存运行机制,单线程降低了cpu内核资源的使用压力;
2)Redis会根据不同的系统使用了I/O复用机制;
redis的I/O复用是redis依赖设备平台提供的机制,redis会使用一些C语言提供的函数库,这些函数库能为redis选择当前系统版本最优秀的I/O复用技术。说白了,redis依赖于C语言提供的函数库,通过对IO多路复用的函数库进行封装,根据自身需要提供IO复用技术;
并且redis会依据不同平台而设置不同的使用机制,体现到代码实现里面来,就是定义不同的宏文件,在引入头文件即可。例如在linux中会使用epoll。
epoll是linux内核一个可扩展的I/O处理机制,工作顺序简单概述:
1)接收到链接请求,给链接建立一个文件描述符(linux把所有设备看作是文件);
2)对文件描述符添加自定义的监听事件(例如,redis可以指定read和write的监听);
3)当监听事件被触发(请求已经就绪,完成登录、安全校验等前置步骤),监听器把事件加入处理队列,并告知应用程序;
4)应用程序遍历待处理的文件句柄,异步处理,完成后返回响应;
截取博客较好的代码实现:(转载:https://blog.csdn.net/qq_19923217/article/details/81943705)
static void do_epoll(int listenfd)
{
int epollfd;
struct epoll_event events[EPOLLEVENTS];
int ret;
char buf[MAXSIZE];
memset(buf,0,MAXSIZE);
//创建一个描述符
epollfd = epoll_create(FDSIZE);
//添加监听描述符事件
add_event(epollfd,listenfd,EPOLLIN);
for ( ; ; )
{
//获取已经准备好的描述符事件
ret = epoll_wait(epollfd,events,EPOLLEVENTS,-1);
handle_events(epollfd,events,ret,listenfd,buf);
}
//文件描述符会占用内存空间,处理完成后需要释放
close(epollfd);
}
static void handle_events(int epollfd,struct epoll_event *events,int num,int listenfd,char *buf)
{
int i;
int fd;
//进行选好遍历
for (i = 0;i < num;i++)
{
fd = events[i].data.fd;
//根据描述符的类型和事件类型进行处理
if ((fd == listenfd) &&(events[i].events & EPOLLIN))
handle_accpet(epollfd,listenfd);
else if (events[i].events & EPOLLIN)
do_read(epollfd,fd,buf);
else if (events[i].events & EPOLLOUT)
do_write(epollfd,fd,buf);
}
}
static void do_read(int epollfd,int fd,char *buf)
{
int nread;
nread = read(fd,buf,MAXSIZE);
if (nread == -1)
{
perror("read error:");
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else if (nread == 0)
{
fprintf(stderr,"client close.\n");
close(fd);
delete_event(epollfd,fd,EPOLLIN);
}
else
{
printf("read message is : %s",buf);
//修改描述符对应的事件,由读改为写
modify_event(epollfd,fd,EPOLLOUT);
}
}