在上一篇文章: 「php之CGI、FastCGI 和 php-fpm」 中说明了,
cgi和fastcgi的关系和用途以及php-cgi和php-fpm又分别是什么,这篇文章主要说明一下nginx 和 php-fpm 是如何通信的。
一、进程间的通信方式
nginx 和 php-fpm 作为两组 master-worker 模式的进程服务,两者之间的交互必然涉及 进程间通信,先来看下 常见的 6 中进程通信 方式。
1. 管道:pipe 和 named pipe
- 两者都是半双工通信,即:通信只能有一个方向,一个写另一个读,不能双向读写。
 - 是一种特殊的文件,使用 read 和 write 进行读写,区别是 
pipe不属于操作系统,只存在于内存当中。 - 
pipe和named pipe的区别是:- 
pipe只能在父子进程或兄弟进程间使用 - 
named pipe则在任何两个毫无关系的进程间使用 
 - 
 
2. 信号:signal
- 信号是在软件层次上对中断机制的一种模拟。在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。
 - 信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
 - 进程可以通过三种方式来响应一个信号:
- 忽略信号,即对信号不做任何处理,其中,有两个信号不能忽略:SIGKILL及SIGSTOP;
 - 捕捉信号。通过 
signal和sigaction定义信号处理函数,当信号发生时,执行相应的处理函数; - 执行缺省操作,Linux对每种信号都规定了默认操作。
 
 
3. 消息队列:message queue
- 消息队列是消息的链表,存放在内核中,并由消息队列标识符标识(队列ID)
 - 消息队列克服了上面两种方式的缺点:
- 信号传递信息少
 - 管道只能承载无格式字节流以及缓冲区大小受限
 
 - 消息队列是UNIX下不同进程之间可实现共享资源的一种机制。UNIX允许不同进程将格式化的数据流以消息队列形式发送给任意进程.
 - 通过使用消息类型,进程可以按任何顺序读信息,或为消息安排优先级顺序。
 - 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。
 
4. 信号量:semaphore
- 在内核中创建一个信号量数组,数组的元素(信号量)都是1。
 - 使用P操作进行-1,使用V操作+1:
- P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执⾏。
 - V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运⾏。如果没有进程因等待sv⽽挂起,就给它加1。
 
 - PV操作用于同一进程,实现互斥。PV操作用于不同进程,实现同步。
 
5. 共享内存:shared memory
- 多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。
 - 需要解决两个问题:
- 怎样提供共享内存?
 - 怎样维护共享内存的互斥访问?
 
 - 通常和信号量一起使用,来同步对共享内存的访问。
 
6. 套接字:socket
- 客户/服务器(即要进行通信的两个进程)既可以在本地单机上进行,也可以跨网络进行。
 - 套接字的特性由3个属性确定,它们分别是:域、类型和协议
- 域,指定套接字通信中使用的网络介质。
- 
AF_INET表示Internet网络,需要用到服务器的ip和port作为通信的重点。当然,服务器必须在开始通信前,绑定好端口,并等待客户端连接。 - 
AF_UNIX表示UNIX文件系统,本质上是对文件的读写,需要指明文件的路径。 
 - 
 - 类型包含流 
stream和数据报datagram两种:- 
SOCK_STREAM表示stream类型,是一个有序、可靠、双向字节流连接,可以确保不会丢失、重复或乱序到达,而且还包含一个出错后重新发送的机制 - 
SOCK_DGRAM表示datagram类型,不需要建立和维持一个连接,以UDP/IP协议实现。对数据长度有限制,且可能会丢失、复制或错乱到达,但速度很高。 
 - 
 - 协议。只要底层的传输机制允许不止一个协议来提供要求的套接字类型,我们就可以为套接字选择一个特定的协议。通常只需要使用默认值。
 
 - 域,指定套接字通信中使用的网络介质。
 
二、 nginx 和 php-fpm 中 socket 的两种域
nginx 和 php-fpm 间的通信使用的就是上述的 套接字(socket) 方案,根据 socket 中 域 的不同,分为 unix socket 和 tcp socket 两种配置方案。
1. unix socket
- nginx 方面需要指定 
fastcgi_pass unix:/xxx/xxx/xxx.sock,如: 
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
}
- 相应地,fpm 方面需要指定 
listen = /xxx/xxx/xxx.sock,如: 
listen = /var/run/php-fpm.sock
2. tcp socket
- nginx 方面需要指定 
fastcgi_pass ip:port,如: 
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
}
- 相应地,fpm 方面需要指定 
listen = ip:port,如: 
listen = 127.0.0.1:9000
- 值得一提的是:在 
tcp socket下,fastcgi_pass可以是一个带有权重的集群,如: 
upstream api_fpm { 
      server ip1:port1 weight=x1; 
      server ip2:port2 weight=x2; 
}
location ~ \.php$ {
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;;
    fastcgi_pass api_fpm;
    fastcgi_index index.php;
}
3. unix socket 和 tcp socket 方案对比
- 
unix socket基于本地.sock文件通信,省去底层网络协议传输的消耗,更高效。 - 
tcp socket配合nginx 的 upstream实现了集群部署,更利于水平扩展。 - 基于上述特点:
- 不需要集群,追求快速影响时,选择 
unix socket。 - 更注重服务稳定性和可扩展性时,选择 
tcp socket。 
 - 不需要集群,追求快速影响时,选择