IO复用(Reactor模式和Preactor模式)——用epoll来提高服务器并发能力
上篇线程/进程并发服务器中提到,提高服务器性能在IO层需要关注两个地方,一个是文件描述符处理,一个是线程调度。
IO复用是什么?IO即Input/Output,在网络编程中,文件描述符就是一种IO操作。
为什么要IO复用?
1.网络编程中非常多函数是阻塞的,如connect,利用IO复用可以以非阻塞形式执行代码。
2.之前提到listen维护两个队列,完成握手的队列可能有多个就绪的描述符,IO复用可以批处理描述符。
3.有时候可能要同时处理TCP和UDP,同时监听多个端口,同时处理读写和连接等。
为什么epoll效率要比select高?
1.在连接数量较大的场景,select遍历需要每个描述符,epoll由内核维护事件表,只需要处理有响应的描述符。
2.select本身处理文件描述符受到限制,默认1024。
3.效率并不是绝对的,当连接率高,断开和连接频繁时,select不一定比epoll差。所以要根据具体场合使用。
epoll的两种模式,电平触发和边沿触发。
1.电平触发效率较边沿触发低,电平触发模式下,当epoll_wait返回的事件没有全部相应处理完毕,内核缓冲区还存在数据时,会反复通知,直到处理完成。epoll默认使用这种模式。
2.边沿触发效率较高,内核缓冲区事件只通知一次。
一个epoll实现demo
#include <iostream>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h> using namespace std; #define MAXLINE 5
#define OPEN_MAX 100
#define LISTENQ 20
#define SERV_PORT 5000
#define INFTIM 1000 int main(int argc, char* argv[])
{
int listen_fd, connfd_fd, socket_fd, epfd, nfds;
ssize_t n;
char line[MAXLINE];
socklen_t clilen; //声明epoll_event结构体的变量,ev用于注册事件,数组用于回传要处理的事件
struct epoll_event ev,events[];
//生成用于处理accept的epoll专用的文件描述符
epfd=epoll_create();
struct sockaddr_in clientaddr;
struct sockaddr_in serveraddr;
listen_fd = socket(AF_INET, SOCK_STREAM, );
//设置与要处理的事件相关的文件描述符
ev.data.fd = listen_fd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listen_fd,&ev); memset(&serveraddr, , sizeof(serveraddr));
serveraddr.sin_family = AF_INET;
serveraddr.sin_addr.s_addr = htonl(INADDR_ANY);
serveraddr.sin_port = htons(SERV_PORT); if (bind(listen_fd, (struct sockaddr*)&serveraddr, sizeof(serveraddr)) == -)
{
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit();
} if (listen(listen_fd, LISTENQ) == -)
{
exit();
} for ( ; ; )
{
//等待epoll事件的发生
nfds = epoll_wait(epfd,events,,);
//处理所发生的所有事件
for (int i = ; i < nfds; ++i)
{
if (events[i].data.fd == listen_fd)//如果新监测到一个SOCKET用户连接到了绑定的SOCKET端口,建立新的连接。 {
connfd_fd = accept(listen_fd,(sockaddr *)&clientaddr, &clilen);
if (connfd_fd < ){
perror("connfd_fd < 0");
exit();
}
char *str = inet_ntoa(clientaddr.sin_addr);
cout << "accapt a connection from " << str << endl;
//设置用于读操作的文件描述符
ev.data.fd = connfd_fd;
//设置用于注测的读操作事件
ev.events = EPOLLIN|EPOLLET;
//注册ev
epoll_ctl(epfd,EPOLL_CTL_ADD,connfd_fd,&ev);
}
else if (events[i].events&EPOLLIN)//如果是已经连接的用户,并且收到数据,那么进行读入。
{
memset(&line,'\0', sizeof(line));
if ( (socket_fd = events[i].data.fd) < )
continue;
if ( (n = read(socket_fd, line, MAXLINE)) < ) {
if (errno == ECONNRESET) {
close(socket_fd);
events[i].data.fd = -;
} else
std::cout<<"readline error"<<std::endl;
} else if (n == ) {
close(socket_fd);
events[i].data.fd = -;
}
cout << line << endl;
//设置用于写操作的文件描述符
ev.data.fd = socket_fd;
//设置用于注测的写操作事件
ev.events = EPOLLOUT|EPOLLET;
//修改socket_fd上要处理的事件为EPOLLOUT
//epoll_ctl(epfd,EPOLL_CTL_MOD,socket_fd,&ev);
}
else if (events[i].events&EPOLLOUT) // 如果有数据发送
{
socket_fd = events[i].data.fd;
write(socket_fd, line, n);
//设置用于读操作的文件描述符
ev.data.fd = socket_fd;
//设置用于注测的读操作事件
ev.events = EPOLLIN|EPOLLET;
//修改socket_fd上要处理的事件为EPOLIN
epoll_ctl(epfd,EPOLL_CTL_MOD,socket_fd,&ev);
}
}
}
return ;
}
执行效果如下:
第一次学epoll时,容易错误的认为epoll也可以实现并发,其实正确的话是epoll可以实现高性能并发服务器,epoll只是提供了IO复用,在IO“并发”,真正的并发只能通过线程进程实现。
那为什么可以同时连接两个客户端呢?实际上这两个客户端都是在一个进程上运行的,前面提到过各个描述符之间是相互不影响的,所以是一个进程轮循在处理多个描述符。
Reactor模式:
Reactor模式实现非常简单,使用同步IO模型,即业务线程处理数据需要主动等待或询问,主要特点是利用epoll监听listen描述符是否有相应,及时将客户连接信息放于一个队列,epoll和队列都是在主进程/线程中,由子进程/线程来接管各个描述符,对描述符进行下一步操作,包括connect和数据读写。主程读写就绪事件。
大致流程图如下:
Preactor模式:
Preactor模式完全将IO处理和业务分离,使用异步IO模型,即内核完成数据处理后主动通知给应用处理,主进程/线程不仅要完成listen任务,还需要完成内核数据缓冲区的映射,直接将数据buff传递给业务线程,业务线程只需要处理业务逻辑即可。
大致流程如下:
IO复用(Reactor模式和Preactor模式)——用epoll来提高服务器并发能力的更多相关文章
- Go 自带的 http/server.go 的连接解析 与 如何结合 master-worker 并发模式,提高单机并发能力
作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...
- Select、Poll、Epoll IO复用技术
简介 目前多进程方式实现的服务器端,一次创建多个工作子进程来给客户端提供服务, 但是创建进程会耗费大量资源,导致系统资源不足 IO复用技术就是让一个进程同时为多个客户端端提供服务 IO复用技术 之 S ...
- 高性能IO之Reactor模式(转载)
讲到高性能IO绕不开Reactor模式,它是大多数IO相关组件如Netty.Redis在使用的IO模式,为什么需要这种模式,它是如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务器用一个w ...
- 高性能IO之Reactor模式
The reactor design pattern is an event handling pattern for handling service requests delivered conc ...
- Java IO的Reactor模式
1. Reactor出现的原因 Reator模式是大多数IO相关组件如Netty.Redis在使用时的IO模式,为什么需要这种模式,如何设计来解决高性能并发的呢? 最最原始的网络编程思路就是服务 ...
- 同步异步阻塞非阻塞Reactor模式和Proactor模式 (目前JAVA的NIO就属于同步非阻塞IO)
在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作. 在比较这两个模式之前,我们首先的搞明白 ...
- Reactor模式与Proactor模式
该文章总结了网上资源对这两种模式的描述 原文地址:http://www.cnblogs.com/dawen/archive/2011/05/18/2050358.html 1.标准定义 两种I/O多路 ...
- 对于观察者模式,Reactor模式,Proactor模式的一点理解
最近就服务器程序IO效率这一块了解一下设计模式中的Reacotr模式和proactor模式,感觉跟观察者模式有些类似的地方,网上也看了一些其他人对三者之间区别的理解,都讲得很仔细,在此根据自己的理解做 ...
- 两种高效的事件处理模型:Reactor模式和Proactor模式
随着IO多路复用技术的出现,出现了很多事件处理模式.同步I/O模型通常由Reactor模式实现,而异步I/O模型则由Proactor模式实现. Reactor模式: Reator类图如上所示,Reac ...
随机推荐
- Permutations [LeetCode]
Given a collection of numbers, return all possible permutations. For example,[1,2,3] have the follow ...
- 18. 4Sum -- 找到数组中和为target的4个数
Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...
- Scout YYF I(POJ 3744)
Scout YYF I Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 5565 Accepted: 1553 Descr ...
- Java多线程-新特性-线程池
Sun在Java5中,对Java线程的类库做了大量的扩展,其中线程池就是Java5的新特征之一,除了线程池之外,还有很多多线程相关的内容,为多线程的编程带来了极大便利.为了编写高效稳定可靠的多线程程序 ...
- lucene索引文件格式
转自:http://blog.csdn.net/whuqin 本文介绍下lucene生成的索引有哪些文件组成,每个文件包含了什么信息.基于Lucene 4.10.0. 数据结构 索引(index)包含 ...
- PDF 补丁丁 0.4.1.804 测试版发布:合并文件夹的图片和PDF文件,自由生成多层次书签
新的测试版增强了合并文件的功能,可以合并文件夹内的图片和PDF文件,还可以在合并文件列表上直接指定与合并文件对应的PDF书签标题.通过拖放文件项目生成多层次的PDF书签.如下图所示: 另外,新的测试版 ...
- Loadrunner 使用检查点
在使用loadrunner 测试时,比如我们在登陆时,我们希望说登录是否成功.这个时候我可以使用检查点函数. 检查登录后,页面上是否返回了某个标识,这样我们可以根据标识判断登录事务是否成功. 现在 ...
- cf卡中,wtmp文件较大,导致磁盘空间满了
看了一下,有一个wtmp 和wtmp.1的文件非常大.wtmp记录的是机器注销.启动的信息.由此可见,机器长时间的不断重启,造成该日志记录超级大,把cf的空间给占满了. wtmp日志可以用who和la ...
- [CSS]三层嵌套的滑动门
原理: 最外层放水平平铺的背景,第二层放左边,第三层放右边,注意这个做法背景图不能透明 结构: <div class="module-title"> <span ...
- php pthreads 多线程扩展的使用:一个较为稳定例子。
今天研究了worker stackable的配合方法,写了两种形式,虽然能工作,但是都会出现内存不听增长的问题: 于是把第一个方法的代码邮件给了作者,到现在他没有回复我. 我最后放弃两者配合的方式,直 ...


