原文: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进程
- [root@localhost~]#ps-ef|grepnginx
- root18381022:48?00:00:00nginx:masterprocess/usr/sbin/nginx-c/etc/nginx/nginx.conf
- nginx18391838022:48?00:00:00nginx:workerprocess
- nginx18401838022:48?00:00:00nginx:workerprocess
- root18511797022:49pts/000:00:00grepnginx
使用网络套接字,Nginx和PHP-FPM配置分别如下:
压力测试test.php脚本
- <?php
- phpinfo();
再来看看 Nginx和PHP-FPM 用本地套接字方式的通信,Nginx 和 PHP-FPM 的配置稍作修改:
压测结果:以上测试都是多次压测后取得结果,从结果可以看到,本地套接字要比网络套接字的QPS平均高了100多,和我们预期基本一致。
PHP的本地套接字编程
其实PHP的本地套接字编程和网络套接字基本一致,只是传的参数不一样。
PHP为socket编程提供了两套API,一套是 socket_* 系列方法,这在我们前面的系列文章里演示过了,另一套是 stream_socket_* 系列方法,而后者使用起来更加的方便,这里我们采用后者来演示。
stream_socket_* 方法列表:
- ?stream_socket_accept—接受由stream_socket_server创建的套接字连接
- ?stream_socket_client—OpenInternetorUnixdomainsocketconnection
- ?stream_socket_enable_crypto—Turnsencryptionon/offonanalreadyconnectedsocket
- ?stream_socket_get_name—获取本地或者远程的套接字名称
- ?stream_socket_pair—创建一对完全一样的网络套接字连接流
- ?stream_socket_recvfrom—Receivesdatafromasocket,connectedornot
- ?stream_socket_sendto—Sendsamessagetoasocket,whetheritisconnectedornot
- ?stream_socket_server—CreateanInternetorUnixdomainserversocket
- ?stream_socket_shutdown—Shutdownafull-duplexconnection
具体方法的使用请参阅PHP手册,这里直接演示代码:
server端代码:
- <?php
- //stream_server.php
- $sockfile=‘/dev/shm/unix.sock‘;
- //如果sock文件已存在,先尝试删除
- if(file_exists($sockfile))
- {
- unlink($sockfile);
- }
- $server=stream_socket_server("unix://$sockfile",$errno,$errstr);
- if(!$server)
- {
- die("创建unixdomainsocketfail:$errno-$errstr");
- }
- while(1)
- {
- $conn=stream_socket_accept($server,5);
- if($conn)
- {
- while(1)
- {
- $msg=fread($conn,1024);
- if(strlen($msg)==0)//客户端关闭
- {
- fclose($conn);
- break;
- }
- echo"readdata:$msg";
- fwrite($conn,"readok!");
- }
- }
- }
- fclose($server);
client端代码:
- <?php
- //stream_client.php
- $client=stream_socket_client("unix:///dev/shm/unix.sock",$errno,$errstr);
- if(!$client)
- {
- die("connecttoserverfail:$errno-$errstr");
- }
- while(1)
- {
- $msg=fread(STDIN,1024);
- if($msg=="quit\n")
- {
- break;
- }
- fwrite($client,$msg);
- $rt=fread($client,1024);
- echo$rt."\n";
- }
- fclose($client);
运行
server端:
- [root@localhosthtml]#phpstream_server.php
- readdata:hellounixdomainsocket
- readdata:areyouok?
- readdata:I‘mfine!
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
- PHPWarning:stream_socket_accept():acceptfailed:Connectiontimedoutin/usr/share/nginx/html/stream_server.phponline13
client端:
- [root@localhosthtml]#phpstream_client.php
- hellounixdomainsocket
- readok!
- areyouok?
- readok!
- I‘mfine!
- readok!
- ^C
以上是一个最简单的本地套接字的代码演示,细心的读者可能注意到了server端报的warning,服务器如果长时间没有客户端过来连接,超过了stream_socket_accept 方法设置的timeout,服务器端便会报这个警告,事实上,真正的服务端代码是不会是像这样写的,因为这种方式同一时间只能处理一个客户端连接,如果要实现并发,一种方式就是使用IO多路复用,如同 socket_* 系列方法中有socket_select 方法 (参考系列文章第一篇http://blog.csdn.net/zhang197093/article/details/77366407),stream_socket_* 系列方法提供了 stream_select 方法来实现多路复用,使用方法也很相似。
- intstream_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
- //stream_server.php
- $sockfile=‘/dev/shm/unix.sock‘;
- //如果sock文件已存在,先尝试删除
- if(file_exists($sockfile))
- {
- unlink($sockfile);
- }
- $server=stream_socket_server("unix://$sockfile",$errno,$errstr);
- if(!$server)
- {
- die("创建unixdomainsocketfail:$errno-$errstr");
- }
- $listen_reads=array($server);
- $listen_writes=array();
- $listen_excepts=NULL;
- while(1)
- {
- $can_reads=$listen_reads;
- $can_writes=$listen_writes;
- $num_streams=stream_select($can_reads,$can_writes,$listen_excepts,0);
- if($num_streams)
- {
- foreach($can_readsas&$sock)
- {
- if($server==$sock)
- {
- $conn=stream_socket_accept($server,5);//此时一定存在客户端连接,不会有超时的情况
- if($conn)
- {
- //把客户端连接加入监听
- $listen_reads[]=$conn;
- $listen_writes[]=$conn;
- }
- }
- else
- {
- $msg=fread($sock,1024);//此时一定是可读的
- if(strlen($msg)==0)//读取到0个字符,说明客户端关闭
- {
- fclose($sock);
- //从sock监听中移除
- $key=array_search($sock,$listen_reads);
- unset($listen_reads[$key]);
- $key=array_search($sock,$listen_writes);
- unset($listen_writes[$key]);
- echo"客户端关闭\n";
- }
- else
- {
- echo"readdata:$msg";
- //是否可写
- if(in_array($sock,$can_writes))
- {
- fwrite($conn,"readok!");
- }
- }
- }
- }
- }
- }
- fclose($server);
此时这个server就不会有前面那个Warning了,并且支持并发
- [root@localhosthtml]#phpstream_server.php
- readdata:helloworld
- readdata:hellounixdomainsocket
- readdata:harryup
- readdata:
- readdata:
- readdata:I‘manotherclient
- 客户端关闭
- 客户端关闭
- readdata:I‘mthethirdclient
- 客户端关闭
That‘s all!
【转】PHP实现系统编程(四)--- 本地套接字(Unix Domain Socket)
原文地址:https://www.cnblogs.com/oxspirt/p/8568835.html