【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)

摘要:
由于其高性能、稳定性和对非血液相关进程间通信的支持,本地套接字也是应用最广泛的IPC机制之一。事实上,PHP的本地套接字编程与网络套接字编程基本相同,但传递的参数不同。PHP为套接字编程提供了两组API。一个是socket_*系列方法,这在我们之前的系列文章中进行了演示,另一个是stream_socket_*,后者使用起来更方便。这里我们用后者来演示。

原文:http://blog.csdn.net/zhang197093/article/details/78143687?locationNum=6&fps=1

--------------------------------------------------------------------------------------------------------------------------------------------------------

Socket API一开始是为了解决网络通讯而设计的,而后来在此之上又衍生出一种叫做本地套接字(Unix Domain Socket)的技术,本地套接字顾名思义,只支持本地的两个进程之间进行通信,虽然网络套接字(Internet Domain Socket)也可以通过本地回环地址(127.0.0.1)来实现本地进程间通信,但由于本地套接字不需要经过网络协议栈,封包拆包、计算校验和等操作,所以效率上相比网络套接字有一定的优势。由于本地套接字性能高、稳定、支持非血缘关系的进程间通讯,所以本地套接字也是当下使用最广泛的IPC(进程间通信)的机制之一。

Nginx 与 PHP-FPM 之间使用网络套接字(127.0.0.1:9000)和使用本地套接字两种通信方式的性能对比 

一般我们都是让 PHP-FPM 监听 127.0.0.1:9000 ,显然这时 Nginx 与 PHP-FPM 是通过网络套接字来实现通讯的,其实,如果 Nginx和 PHP-FPM运行在同一台服务器上,我们还可以让 PHP-FPM监听本地套接字,接下来就针对这两种方式的性能做一简单的比较。

这里我的Nginx开启两个worker进程

[plain] view plain copy
 
  1. [root@localhost ~]# ps -ef | grep nginx  
  2. root      1838     1  0 22:48 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf  
  3. nginx     1839  1838  0 22:48 ?        00:00:00 nginx: worker process                     
  4. nginx     1840  1838  0 22:48 ?        00:00:00 nginx: worker process                     
  5. root      1851  1797  0 22:49 pts/0    00:00:00 grep nginx  

使用网络套接字,Nginx和PHP-FPM配置分别如下:

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第1张

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第2张 

压力测试test.php脚本

[php] view plain copy
 
  1. <?php  
  2.   
  3. phpinfo();  
压测结果:

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第3张


再来看看 Nginx和PHP-FPM 用本地套接字方式的通信,Nginx 和 PHP-FPM 的配置稍作修改:

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第4张

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第5张


压测结果:

【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)第6张

以上测试都是多次压测后取得结果,从结果可以看到,本地套接字要比网络套接字的QPS平均高了100多,和我们预期基本一致。

PHP的本地套接字编程

其实PHP的本地套接字编程和网络套接字基本一致,只是传的参数不一样。

PHP为socket编程提供了两套API,一套是 socket_* 系列方法,这在我们前面的系列文章里演示过了,另一套是 stream_socket_* 系列方法,而后者使用起来更加的方便,这里我们采用后者来演示。

stream_socket_*  方法列表:

[plain] view plain copy
 
  1. •stream_socket_accept — 接受由 stream_socket_server 创建的套接字连接  
  2. •stream_socket_client — Open Internet or Unix domain socket connection  
  3. •stream_socket_enable_crypto — Turns encryption on/off on an already connected socket  
  4. •stream_socket_get_name — 获取本地或者远程的套接字名称  
  5. •stream_socket_pair — 创建一对完全一样的网络套接字连接流  
  6. •stream_socket_recvfrom — Receives data from a socket, connected or not  
  7. •stream_socket_sendto — Sends a message to a socket, whether it is connected or not  
  8. •stream_socket_server — Create an Internet or Unix domain server socket  
  9. •stream_socket_shutdown — Shutdown a full-duplex connection  

具体方法的使用请参阅PHP手册,这里直接演示代码:

server端代码:

[php] view plain copy
 
  1. <?php  
  2. //stream_server.php  
  3.   
  4. $sockfile = '/dev/shm/unix.sock';  
  5. // 如果sock文件已存在,先尝试删除  
  6. if (file_exists($sockfile))  
  7. {  
  8.     unlink($sockfile);  
  9. }  
  10.   
  11. $server = stream_socket_server("unix://$sockfile", $errno, $errstr);  
  12.   
  13. if (!$server)  
  14. {  
  15.         die("创建unix domain socket fail: $errno - $errstr");  
  16. }  
  17.   
  18. while(1)  
  19. {  
  20.         $conn = stream_socket_accept($server, 5);  
  21.   
  22.         if ($conn)  
  23.         {  
  24.                 while(1)  
  25.                 {  
  26.                     $msg = fread($conn, 1024);  
  27.                     if (strlen($msg) == 0) //客户端关闭  
  28.                     {  
  29.                         fclose($conn);  
  30.                         break;  
  31.                     }  
  32.                     echo "read data: $msg";  
  33.                     fwrite($conn, "read ok!");  
  34.                 }  
  35.         }  
  36.   
  37. }  
  38. fclose($server);  


client端代码:
[php] view plain copy
 
  1. <?php  
  2. //stream_client.php  
  3.   
  4. $client = stream_socket_client("unix:///dev/shm/unix.sock", $errno, $errstr);  
  5.   
  6. if (!$client)  
  7. {  
  8.         die("connect to server fail: $errno - $errstr");  
  9. }  
  10.   
  11. while(1)  
  12. {  
  13.     $msg = fread(STDIN, 1024);  
  14.   
  15.     if ($msg == "quit ")  
  16.     {  
  17.         break;  
  18.     }  
  19.   
  20.     fwrite($client, $msg);  
  21.     $rt = fread($client, 1024);  
  22.   
  23.     echo $rt . " ";  
  24. }  
  25.   
  26. fclose($client);  

运行

server端:

[plain] view plain copy
 
  1. [root@localhost html]# php stream_server.php   
  2. read data: hello unix domain socket  
  3. read data: are you ok?  
  4. read data: I'm fine!  
  5. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  
  6. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  
  7. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  
  8. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  
  9. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  
  10. PHP Warning:  stream_socket_accept(): accept failed: Connection timed out in /usr/share/nginx/html/stream_server.php on line 13  

client端:
[plain] view plain copy
 
  1. [root@localhost html]# php stream_client.php   
  2. hello unix domain socket  
  3. read ok!  
  4. are you ok?  
  5. read ok!  
  6. I'm fine!   
  7. read ok!  
  8. ^C  

以上是一个最简单的本地套接字的代码演示,细心的读者可能注意到了server端报的warning,服务器如果长时间没有客户端过来连接,超过了stream_socket_accept 方法设置的timeout,服务器端便会报这个警告,事实上,真正的服务端代码是不会是像这样写的,因为这种方式同一时间只能处理一个客户端连接,如果要实现并发,一种方式就是使用IO多路复用,如同 socket_* 系列方法中有socket_select 方法 (参考系列文章第一篇http://blog.csdn.net/zhang197093/article/details/77366407),stream_socket_* 系列方法提供了 stream_select 方法来实现多路复用,使用方法也很相似。

[php] view plain copy
 
  1. int stream_select ( array &$read , array &$write , array &$except , int $tv_sec [, int $tv_usec = 0 ] )  

The stream_select() function accepts arrays of streams and waits for them to change status. Its operation is equivalent to that of the socket_select() function except in that it acts on streams.

详细的方法介绍请参考PHP手册 : http://php.net/manual/zh/function.stream-select.php

优化后的代码如下:

[php] view plain copy
 
  1. <?php  
  2. //stream_server.php  
  3.   
  4. $sockfile = '/dev/shm/unix.sock';  
  5. // 如果sock文件已存在,先尝试删除  
  6. if (file_exists($sockfile))  
  7. {  
  8.     unlink($sockfile);  
  9. }  
  10.   
  11. $server = stream_socket_server("unix://$sockfile", $errno, $errstr);  
  12.   
  13.   
  14. if (!$server)  
  15. {  
  16.         die("创建unix domain socket fail: $errno - $errstr");  
  17. }  
  18.   
  19. $listen_reads = array($server);  
  20. $listen_writes = array();  
  21. $listen_excepts = NULL;  
  22.   
  23. while(1)  
  24. {  
  25.         $can_reads = $listen_reads;  
  26.         $can_writes = $listen_writes;  
  27.         $num_streams = stream_select($can_reads, $can_writes, $listen_excepts, 0);  
  28.   
  29.         if ($num_streams)  
  30.         {  
  31.                 foreach ($can_reads as &$sock)  
  32.                 {  
  33.                         if ($server == $sock)  
  34.                         {  
  35.                                 $conn = stream_socket_accept($server, 5); //此时一定存在客户端连接,不会有超时的情况  
  36.                                 if ($conn)  
  37.                                 {  
  38.                                         // 把客户端连接加入监听  
  39.                                         $listen_reads[] = $conn;  
  40.                                         $listen_writes[] = $conn;  
  41.                                 }  
  42.                         }  
  43.                         else  
  44.                         {  
  45.                                 $msg = fread($sock, 1024);  //此时一定是可读的  
  46.                                 if (strlen($msg) == 0) //读取到0个字符,说明客户端关闭  
  47.                                 {  
  48.                                         fclose($sock);  
  49.                                         // 从sock监听中移除  
  50.                                         $key = array_search($sock, $listen_reads);  
  51.                                         unset($listen_reads[$key]);  
  52.                                         $key = array_search($sock, $listen_writes);  
  53.                                         unset($listen_writes[$key]);  
  54.                                         echo "客户端关闭 ";  
  55.                                 }  
  56.                                 else  
  57.                                 {  
  58.                                    echo "read data: $msg";  
  59.                                     // 是否可写  
  60.                                     if (in_array($sock, $can_writes))  
  61.                                     {  
  62.                                         fwrite($conn, "read ok!");  
  63.                                     }  
  64.                                 }  
  65.                         }  
  66.                 }  
  67.         }  
  68.   
  69.   
  70. }  
  71. fclose($server);  

此时这个server就不会有前面那个Warning了,并且支持并发
[plain] view plain copy
 
  1. [root@localhost html]# php stream_server.php   
  2. read data: hello world  
  3. read data: hello unix domain socket  
  4. read data: harry up  
  5. read data:   
  6. read data:   
  7. read data: I'm another client  
  8. 客户端关闭  
  9. 客户端关闭  
  10. read data: I'm the third client  
  11. 客户端关闭  

That‘s all!

版权声明:本文为博主原创文章,未经博主允许不得转载。 //blog.csdn.net/zhang197093/article/details/78143687

免责声明:文章转载自《【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇Scala 安装 Exception in thread "main" java.lang.VerifyError: Uninitialized object exists on backward branch 96VC++ ListCtrl Report使用下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

php实现SESSION跨域

稍微大一点的网站,通常都会有不只一个服务器,每个服务器运行着不同的功能模块或者不同的子系统,他们使用不同的二级域名,比如www.a.com、i.a.com、bbs.a.com。而一个整体性强的网站,用户系统是统一的,即一套用户名、密码在整个网站的各个子系统中都是可以登录使用的。各个服务器共享用户数据是比较容易实现的,只需要在后端放个数据库服务器,各个服务器...

nginx配置phpcms v9伪静态规则 phpcms伪静态 404 Not Found

location / { if (!-f $request_filename){ rewrite (.*) /index.php; } rewrite ^/caipu-([0-9]+)-([0-9]+)-([0-9]+).html /index.php?m=content&c=index&a=show&catid=$1&am...

Nginx优化:CPU篇

CPU 1.worker进程数量应当等于cpu核心数配置语法:worker_processes number | auto;比如:worker_processes auto;配置位置:main 2.worker进程绑定cpu配置语法:worker_cpu_affinity cpumask 1000 0100 0010 0001; # 4核为例默认配置:wo...

PHP 将某个http地址的远程图片下载到本地的某个目录

代码: function getImage($url,$save_dir='',$filename='',$type=0){ if(trim($url)==''){ return array('file_name'=>'','save_path'=>'','error'=>1); } if(trim($save_dir)==''){ $...

php 文件 创建 剪切 复制 常用函数

<?phpheader('content-type:text/html;charset=utf-8');/** 注 touch 如文件已存在,设定文件的访问和修改时间 如果文件不存在,则会被创建。* touch(文件名,时间戳); 时间戳默认为当前时间 返回布尔值* */////创建文件 123.txt(如果文件不存在)if(!file_exists...

Thinkphp6笔记二:开启多应用模式

开启多应用模式1.首先刪除app目录下所有其他文件2.安装多应用模式扩展: composer require topthink/think-multi-app3.创建三大应用index(前端),admin(后台),common(公共应用)命令:php think build index命令:php think build admin命令:php think...