2019-4-7升级单进程非阻塞为多进程非阻塞

升级第2代,区别在于:

放弃select方式,采用epoll。epoll应用在nginx等软件,成为高并发优秀软件的基础。epoll无需轮询,也无需在用户空间和内核之间切换,属于事件通知。好比后台系统采用先进的监听方式,并能快速的通知客服处理哪个。这里用的是swoole的event,基于epoll。

多进程,一个客服处理速度是有限的,那么就两个或者多个客服一同处理,后台会平均分配任务给客服。而多进程用的php扩展pcntl模块,另外套接字等等函数如果陌生的话,nginx系列也有类似的文章会比较详细,但最重要的是翻php手册。

废话不多说,扔下代码慢慢体会。

代码

<?php
class EpollServer
{
	protected 	$_workerPids=[]; //子进程pid
	protected 	$_workerNum=2; //子进程数
	protected 	$_sAddr;
	protected 	$_masterPid;//父进程pid

	protected 	$onConnect; //链接事件回调
	protected 	$onReceive; //消息事件回调
	protected 	$onClose;//关闭事件回调

	public function __construct($socket_addr){
		$this->_sAddr = $socket_addr;
		$this->_masterPid = posix_getpid();//当前进程pid 因在主进程空间故为主进程pid
		var_dump('master pid:',$this->_masterPid); 
	}

	public function start(){
		$this->forkWorker($this->_workerNum);
	}

	public function forkWorker($worker_num)
	{
		for ($i=0; $i < $worker_num; $i++)
		{
			$pid=pcntl_fork();
			if($pid<0){
				exit('fork fail!');
			}else if($pid>0){
				//父进程空间
				$this->_workerPids[]=$pid;
			}else{
				//子进程空间
				$this->accept();//处理连接
				exit;
			}
		}
		var_dump('worker pids:',$this->_workerPids);
		for ($i=0; $i < $worker_num; $i++) {
			$status=0;
			$work_pid=pcntl_wait($status); //阻塞
			echo '回收'.$work_pid.PHP_EOL;
		}
	}

	public function accept()
	{
		$connext_option=[
			'socket'=>[
				'backlog'=>10240, //等待处理连接的队列
			],
		];
		$connext=stream_context_create($connext_option);
		stream_context_set_option($connext,'socket','so_reuseaddr',1);
		stream_context_set_option($connext,'socket','so_reuseport',1);

		$main_sock=stream_socket_server($this->_sAddr,$errno,$errstr,STREAM_SERVER_BIND|STREAM_SERVER_LISTEN,$connext);

		swoole_event_add($main_sock,function($sfd){
			$clnt_sock=stream_socket_accept($sfd);
			if($sfd && is_callable($this->onConnect)){
				call_user_func($this->onConnect,$sfd,$clnt_sock);
			}

			swoole_event_add($clnt_sock,function($cfd) use ($sfd){
				$buf=fread($cfd,1024);
				if(empty($buf)){
                    if(!is_resource($cfd) || feof($cfd) ){
                        fclose($cfd);
                        if(is_callable($this->onClose)){
                        	call_user_func($this->onClose,$sfd,$cfd);
                        }
                    }
                }else{
                	if(is_callable($this->onReceive)){
                        call_user_func($this->onReceive,$sfd,$cfd,$buf);
                    }
                }
			});

		});

	}

	public function on($event,$closure)
	{
		if(empty($event) && is_callable($closure)){
			echo "parameters error!".PHP_EOL;
			exit;
		}
		$event=lcfirst($event);
		switch ($event) {
			case 'connect':
				$this->onConnect = $closure;
				break;
			case 'receive':
				$this->onReceive = $closure;
				break;
			case 'close':
				$this->onClose = $closure;
				break;
			default:
				echo "invalid event!".PHP_EOL;
				exit;
				break;
		}
	}

}

$epoll_server=new EpollServer('tcp://0.0.0.0:6001');

$epoll_server->on('connect',function($server,$fd){
	echo 'client_no:'.(int)$fd.' has connected!'.PHP_EOL;
});
$epoll_server->on('receive',function($server,$fd,$data){
    echo $data.PHP_EOL;
    fwrite($fd, 'response');
});
$epoll_server->on('close',function($server,$fd){
	echo 'client_no:'.(int)$fd.' has closed!'.PHP_EOL;
});

$epoll_server->start();

关于进程结构

在这里插入图片描述
对应的进程结构
在这里插入图片描述

请求

客户端

<?php
$client = new swoole_client(SWOOLE_SOCK_TCP);
$client->connect('127.0.0.1', 6001, -1);

for ($i=0; $i < 10 ; $i++) {
	$client->send('hello!');
	echo $client->recv().PHP_EOL;
}

$client->close();

服务端打印
在这里插入图片描述

压测

同样关闭所有窗口输出
本地压测
可以tcp压测:

for ($i=0; $i < 10000 ; $i++) {....

http本地压测:

$epoll_server->on('receive',function($server,$fd,$data){
	$content="response";
    $http_resonse = "HTTP/1.1 200 OK\r\n";
    $http_resonse .= "Content-Type: text/html;charset=UTF-8\r\n";
    $http_resonse .= "Connection: keep-alive\r\n"; //连接保持
    $http_resonse .= "Server: php socket server\r\n";
    $http_resonse .= "Content-length: ".strlen($content)."\r\n\r\n";
    $http_resonse .= $content;
    fwrite($fd, $http_resonse);
    });
ab -n 100000 -c 10000 -k http://127.0.0.1:6001/

一般是没有压力的

外网压测:

ab -n 1025 -c 1025 -k http://39.xxx.xx.xx:6001/

在这里插入图片描述
响应速度一般(当然同样参数压测百度更糟糕),压测本身无太大意义,但是超过select系统最大支持的1024个长连接,epoll理论上是没有限制的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值