FCGI协议的heade比较简单只有8个字节,其C语言定义格式如下:
typedef struct
{
unsigned char version; //版本
unsigned char type; //操作类型
unsigned char requestIdB1; //请求id
unsigned char requestIdB0;
unsigned char contentLengthB1; //内容长度
unsigned char contentLengthB0;
unsigned char paddingLength; //填充字节的长度
unsigned char reserved; //保留字节
}FCGI_Header;
下面逐一解释:
- version字段
用来表示FCGI的版本信息,现在fcgi4j的header中写死的为1。
public static final int FCGI_HEADER_LEN = 8;
public static final int FCGI_VERSION_1 = 1;
- type
常见的fcgi type包括下面几个:
1 在与php-fpm建立连接之后发送的第一个消息中的type值就得为1,用来表明此消息为请求开始的第一个消息
2 异常断开与php-fpm的交互
3 在与php-fpm交互中所发的最后一个消息中type值为此,以表明交互的正常结束
4 在交互过程中给php-fpm传递环境参数时,将type设为此,以表明消息中包含的数据为某个name-value对
5 web服务器将从浏览器接收到的POST请求数据(表单提交等)以消息的形式发给php-fpm,这种消息的type就得设为5
6 php-fpm给web服务器回的正常响应消息的type就设为6
7 php-fpm给web服务器回的错误响应设为7
在fcgi4j中常用的有这么几个:
public enum FCGIHeaderType {
FCGI_BEGIN_REQUEST {
@Override
public int getId() {
return 1;
}
},
FCGI_END_REQUEST {
@Override
public int getId() {
return 3;
}
},
FCGI_PARAMS {
@Override
public int getId() {
return 4;
}
},
FCGI_STDIN {
@Override
public int getId() {
return 5;
}
},
FCGI_STDOUT {
@Override
public int getId() {
return 6;
}
};
}
(1)FCGI_BEGIN_REQUEST:第一次向PHP-FPM发起请求的。
(2)FCGI_PARAMS:发送具体请求之前需要写一些参数,如SCRIPT_NAME、REQUEST_METHOD(GET/POST).如果是POST还需要CONTENT_TYPE、CONTENT_LENGTH。
(3)FCGI_STDIN:向PHP-FPM正式发起请求。
(4)FCGI_STDOUT:PHP-FPM返回数据
(5)FCGI_END_REQUEST:PHP-FPM返回数据结束,整个请求完成。
- requestId
这个占两个字节。它表示这某个特有的交互,因为php-fpm(可以理解为服务器)可以同时处理多个交互。但是在fcgi4j中,id默认为1。因为,1个请求总是会建立1个连接。PHP不支持连接池。而且,这个项目的场景是在本机上进行连接的请求和释放。一个进程是fcgi4j的Java进程,一个是PHP-FPM的PHP进程。业务方没有提出性能上的需求,暂时没有使用本地套接字的方式。
Linux和UNIX都拥有一个非常实用的工具–UNIX套接字,或称为本地套接字,它可以被用在进程间通讯(IPC)当中。UNIX套接字的运转机制和Internet套接字类似,主要的区别UNIX套接字只能用在一台计算机中,而Internet套接字则可以在不同的计算机之间使用。UNIX套接字定址的方式是作为本地文件系统里的一个文件。
- contentLength
此字段也占2个字节,它用来表示此消息中的消息体中数据的长度(一条消息包含消息头和消息体。由于占两个字节,所以,最长是65535B。注意,消息内容中包括填充字段),我们可以据此在读消息时,能够知道读多长能读出一条完整的消息。
这里还有必要说下,PHP-FPM返回的数据形式。返回的第一个消息在消息体的前面有一个response header。内容形式如下:
Content-type: text/plain; charset=utf-8^M
Content-language: en^M
Content-Length: 16281^M
^M
这部分内容不属于正式的消息体,所以在读取消息体时要跳过这个内容。但是这个header中包括Content-Length。这个Content-Length表示正式的消息体中包含的数据的长度。注意,如果PHP-FPM对数据进行了分包,有可能先返回这个包含数据长度的response header,然后紧连着发送的数据。实例如下:
第一个response数据包header:
2017-04-12 10:18:39,531 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:39) MOABizProcessor-thread-2 - version :1
2017-04-12 10:18:39,531 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:41) MOABizProcessor-thread-2 - type :FCGI_STDOUT
2017-04-12 10:18:39,531 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:43) MOABizProcessor-thread-2 - id :1
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:45) MOABizProcessor-thread-2 - content length: 88
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:47) MOABizProcessor-thread-2 - padding: 0
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.FCGIConnection.bufferStdoutData(FCGIConnection.java:607) MOABizProcessor-thread-2 - dataBuffer in fcgi: 88##Content-type: text/plain; charset=utf-8^M
Content-language: en^M
Content-Length: 16281^M
^M
第二个response数据包header
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:39) MOABizProcessor-thread-2 - version :1
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:41) MOABizProcessor-thread-2 - type :FCGI_STDOUT
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:43) MOABizProcessor-thread-2 - id :1
2017-04-12 10:18:39,532 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:45) MOABizProcessor-thread-2 - content length: 16280
2017-04-12 10:18:39,533 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:47) MOABizProcessor-thread-2 - padding: 0
第三个数据包header
2017-04-12 10:18:39,535 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:39) MOABizProcessor-thread-2 - version :1
2017-04-12 10:18:39,535 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:41) MOABizProcessor-thread-2 - type :FCGI_STDOUT
2017-04-12 10:18:39,535 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:43) MOABizProcessor-thread-2 - id :1
2017-04-12 10:18:39,535 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:45) MOABizProcessor-thread-2 - content length: 1
2017-04-12 10:18:39,535 DEBUG com.googlecode.fcgi4j.message.FCGIHeader.parse(FCGIHeader.java:47) MOABizProcessor-thread-2 - padding: 7
从上面可以看到16281=16280+1。注意不包括最后一个数据包的padding字段。
paddingLength
此字段占1B。由于fcgi协议要求一条消息的长度必须是8的倍数(这里说的消息的长度包括消息头的长度和消息体的长度。由于消息头的长度已经是固定的8B,所以,要求消息体的长度必须是8的倍数。)当消息体的长度不是8的倍数时,需要用这个字段表示在消息体中填充的附加数据的长度。这次线上的问题就跟这个有关系。reserved
此字段占用1B。保留字段,用来最为fcgi协议的扩展使用。