nginx-4 IO复用

一些基本概念

IO复用(input output)
建立http连接,有三次握手,会有一定的消耗。实现header头中的keep-alive保持长连接。

IO多路复用
是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程。I/O多路复用就是通过一种机制,一个进程可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。

简单来说,利用一个进程来监视描述符的变化(读、写、异常)。

目前支持I/O多路复用的系统调用有 select,poll,epoll。

  • select

监视并等待多个文件描述符的属性变化(可读、可写或错误异常)。select函数监视的文件描述符分 3 类,分别是writefds、readfds、和 exceptfds。调用后 select会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时( timeout 指定等待时间),函数才返回。当 select()函数返回后,可以通过遍历 fdset,来找到就绪的描述符,并且描述符最大不能超过1024

select遍历的方式比较古老,效率不是很高,而且最大支持1024个连接。

  • pool

poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制
poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。

问题:
select/poll问题很明显,它们需要循环检测连接是否有事件。如果服务器有上百万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。

  • epoll:

epoll是在2.6内核中提出的,是之前的select和poll的增强版本。相对于select和poll来说,epoll更加灵活,没有描述符限制,无需轮询。epoll使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。

简单点来说就是当连接有I/O流事件产生的时候,epoll就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个进程。

主动通知,而不是轮询查找
这里可以多加一个选择nginx的原因,因为Nginx是基于epoll的异步非阻塞的服务器程序。自然,Nginx能够轻松处理百万级的并发连接,也就无可厚非了。

eventloop或称reactor

eventloop=event(事件) + loop(循环)
reactor = re(反复) + actor(动作)

它只是一个名词,或者一种模式,代表事件循环。它解决的问题是IO复用异步非阻塞。

IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket(也可以是管道、eventfd、信号)句柄的事件变化。

Reactor只是一个事件发生器,实际对socket句柄的操作,如connect/accept、send/recv、close是在callback中完成的。

注:
什么是句柄?句柄英文为handler,可以形象的比喻为锅柄、勺柄。也就是资源的唯一标识符、资源的ID。通过这个ID可以操作资源。

使用php扩展event 实现IO复用

编译安装event扩展

1,首先安装event扩展所依赖的库libevent

Libevent 是一个用C语言编写的、轻量级的开源高性能事件通知库,主要有以下几个亮点:事件驱动( event-driven),高性能;轻量级,专注于网络,不如 ACE 那么臃肿庞大;源代码相当精炼、易读;跨平台,支持 Windows、 Linux、 *BSD 和 Mac Os;支持多种 I/O 多路复用技术, epoll、 poll、 dev/poll、 select 和 kqueue 等;支持 I/O,定时器和信号等事件;注册事件优先级

安装libevent依赖:
官网:http://www.monkey.org/~provos/libevent/
下载:http://www.monkey.org/~provos/libevent-2.0.10-stable.tar.gz
tar zxvf libevent-2.0.10-stable.tar.gz
cd libevent-2.0.10-stable
./configure –prefix=/usr //安装路径设置
make
make install

测试libevent是否安装成功:
ls -al /usr/lib | grep libevent

2,安装event扩展
cd /usr/local/src
wget http://pecl.php.net/get/event-2.3.0.tgz
tar -zxvf event-2.3.0.tgz && cd event-2.3.0
/usr/local/php/bin/phpize
./configure --with-event-libevent-dir=/usr/local/libevent-2.1.8-stable/
注意 --with-event-libevent-dir libevent的安装目录
make && make install
在/usr/local/php/etc/ 目录下,复制php.ini 为php-cli.ini 添加下面配置
extension=event.so

代码

这次用另一种方式使用socket。

//创建资源流的上下文 上下文就是一些配置选项
$context=stream_context_create([
    'socket'=>[
        'backlog'=>2000  // 侦听队列中未连接的数量 相当于socket_listen($socket, 5);中的5
    ]]);
    
//设置连接重用 相当于socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1);中的SO_REUSEADDR小写字母 so=socket reuse addr=address 地址重用
stream_context_set_option($context,'socket','so_reuseaddr',1);

$socket = stream_socket_server("tcp://0.0.0.0:6001", $errno, $errstr,STREAM_SERVER_BIND | STREAM_SERVER_LISTEN,$context);

stream_set_blocking($socket,false);//非阻塞

stream_context_create解释:
就是一些流的类型和对应的配置选项,这里我们用的套接字类型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
stream_socket_server解释:
在这里插入图片描述
socket_server创建完成。

接下来使用event扩展来编写核心代码

在这里插入图片描述


在这里插入图片描述


点击construct,event对象通过构造函数传参。
各个参数的简单介绍:

  • EventBase : event对象实际是event_handler,执行事件的对象;event使用EventBase基本库的方法,add添加自己到监听队列中;
  • fd : event包含socket句柄(handler)以操作资源,读read写write等。
  • what : 注册事件type类型,何时触发回调。
  • cb : callback 检测到IO上有流操作时,执行的业务逻辑。
    在这里插入图片描述
  • 参数1:EventBase,点击上图的EventBase即可跳转到文档
    在这里插入图片描述
    它使用刚安装的libevent依赖库封装的功能类,使用libevent的IO事件驱动和多路复用方法,事件的基础库。
    在这里插入图片描述
  • 参数2:fd
    在这里插入图片描述
    这里就是$socket资源
  • 参数3:what
    在这里插入图片描述
    在这里插入图片描述
  • 参数4 :cb
    在这里插入图片描述
    在这里插入图片描述
    这里的意思是返回给回调函数的数据,有资源句柄等。

那么有了上边的铺垫后完成代码:

//开头代码在上边

//libevent基本类 事件驱动内核方法
$base=new EventBase(); 

//创建一个事件对象
$event=new  Event($base,$socket,Event::PERSIST |Event::READ | Event::WRITE,function ($socket){
    $client = stream_socket_accept($socket);
    //fread($client,65535)
    //fwrite($client,'hello world!')
    //fclose($client);
});

//加入事件监听 add方法:查看event手册
$event->add();
//调度挂起事件监听 loop=循环 方法:查看eventbase手册
$base->loop(); //返回booler类型

上面是对$server服务端监听,要达到IO复用,不重复创建客户端资源,则需要对客户端监听。

//开头代码在上边

$base=new EventBase(); 

服务端事件对象
$event=new  Event($base,$socket,Event::PERSIST |Event::READ | Event::WRITE,function ($socket){
    $client = stream_socket_accept($socket);
	
	客户端事件对象
	$base=new EventBase();
    
    $event=new  Event($base,$client,Event::PERSIST |Event::READ | Event::WRITE,function ($client){
         $msg=fread($client,65535);

         if($msg){
          //判断请求头中是否包含了keep-alive 如果包含 则响应长连接 代码略..
           	$content='21335435';
    		$string="HTTP/1.1 200 OK\r\n";
    		$string.="Content-Type: text/html;charset=utf-8\r\n";
    		$string.="Connection: keep-alive\r\n";
    		$string.="Content-Length: ".strlen($content)."\r\n\r\n";
    		fwrite($client,$string.$content);
         }
         
   		 //当socket断开连接,删除事件 $event->del(); 代码略..
    });
    $event->add(); //加入事件监听
    $base->loop(); //开始监听
   
});
$event->add();
$base->loop();

这样当浏览器携带了keep-alive请求时,则使用IO复用技术保持住连接,实现长连接。本来50ms打开的网页,20ms左右就打开了。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值