对于长轮询场景是比较常见的,客户端同步的不断的在同步请求,而服务端根据请求来决定是否响应或者hold请求以继续检查数据.
由于php-fpm是单进程单线程的,一个进程同时只能处理一个请求,所以对于这种长轮询的场景,需要启动特别多的php-fpm进程来应对高的并发,这是特别浪费资源的,因为大部分请求都是hold在服务器上。
现在我们用openresty来轮询php-fpm,php-fpm会很快的处理完成,不会hold住,减少了php-fpm的压力,配置文件如下:
location ~ /longpolling(/.*) {
content_by_lua_block {
local request_uri_without_args = ngx.re.sub(ngx.var.request_uri, "\\?.*", "")
request_uri_without_args = ngx.re.gsub(request_uri_without_args,"/longpolling","")
local url = {request_uri_without_args}
local args = ngx.req.get_uri_args()
local query = ngx.encode_args(args)
if query ~= "" then
url[#url + 1] = "?"
url[#url + 1] = query
end
url = table.concat(url)
for i = 1, 3 do
local sleep = 1
local res = ngx.location.capture(url)
for k, v in pairs(res.header) do
if type(v) == "table" then
if k == 'sleep' and v == 0 then
ngx.header[k] = table.concat(v, ",")
sleep = table.concat(v, ",")
end
else
if k == "sleep" and v == "0" then
ngx.header[k] = v
sleep = v
end
end
end
if sleep == 1 then
ngx.sleep(1)
else
ngx.say(res.body)
break
end
end
}
}
将url中以”longpolling”开始的请求转换为长轮询.比如:/longpolling/aa 实际访问是/aa. 长轮询总共时间是3秒,每次停止一秒,根据php-fpm响应头sleep来决定是否输出还是继续轮询.PHP代码如下.
<?php
header('Content-type: application/json');
$time = $_GET["time"];
$data = get_new_data($time);
if ( $data ){
header('sleep:0');
echo json_encode(array("data"=>$data));
}else{
echo json_encode(array("data"=>""));
}
这样我们就做了一个相对通用的仅支持get请求的优化接口。