Nginx Comet: 基于 HTTP 长连接的“服务器推”技术

nginx_http_push_module (不建议使用)

这个模块功能上没有问题,网上介绍的文章相对比较多,但是存在严重的内存泄露问题,而且发现使用kill -HUP的方式优雅重启nginx虽会释放一部分内存,但nginx错误日志显示有共享内存锁相关的冲突,我们不得不每小时彻底重启一次nginx。简单说一下就是它使用一个全局的内存池来分配订阅者及响应需要的内存空间,但是从nginx内存池分配的小内存块(< pagesize,4096)是不会释放的也不会归还到池中进行重用,具体可查看nginx源码的ngx_palloc和ngx_pfree函数进行验证。

可google "nginx中mod_push模块内存分配改造",在作者的网站正在改版暂时找不到该文章。

这里也有人指出该问题,同时该文作者也fork了一个分枝,但是我试了一下,除了不支持push_channel_timeout特性外,还是一样有内存泄露。

参考配置
location ~ ^/publish$ {
    allow 127.0.0.1;
    deny all;
    set $push_channel_id $arg_id;
    push_publisher;
    push_delete_oldest_received_message on;
    push_message_timeout 5s;
    #push_channel_timeout 60s;
    push_store_messages off;
}

location ~ ^/activity$ {
    if ($args ~ "callback=(.+)" ) {
        rewrite ^/activity "/activity_jsonp" last;
    }
    push_subscriber;
    push_subscriber_timeout 60s;
    push_subscriber_concurrency first;
    push_max_channel_subscribers 1;
    set $push_channel_id $arg_id;
    default_type application/json;
}

location ~ ^/activity_jsonp$ {
    push_subscriber;
    push_subscriber_timeout 60s;
    push_subscriber_concurrency first;
    push_max_channel_subscribers 1;
    set $push_channel_id $arg_id;
    default_type application/json;
    echo_before_body $arg_callback "(";
    echo_after_body ")";
}

nginx-push-stream-module (建议使用)

由于 nginx_http_push_module 存在内存泄露问题,同时没有人进行正式的修复,我们决定尝试一下nginx-push-stream-module,这个模块功能更强大同时文档更完整,看起来也更活跃。

优点
  • 更成熟有内存消耗说明文档,便于决定共享内存容量配置。有统计功能。可对响应内容进行再处理。
  • 测试中未发现明显的内存泄露
  • 内置支持jsonp 返回的jsonp是这样的格式callback([text]),可以通过修改ngx_http_push_stream_module_utils.h中定义的NGX_HTTP_PUSH_STREAM_CALLBACK_INIT_CHUNK和NGX_HTTP_PUSH_STREAM_CALLBACK_END_CHUNK去除多余的中括号。
参考配置
push_stream_store_messages off;
push_stream_max_subscribers_per_channel 1;
push_stream_subscriber_connection_ttl 60s;
push_stream_longpolling_connection_ttl 60s;

server {
    listen 80;
    server_name localhost 127.0.0.1;
    
    ...

    location ~ ^/publish$ {
        allow 127.0.0.1;
        deny all;
        push_stream_publisher admin;
        set $push_stream_channel_id $arg_id;
    }
    
    location ~ ^/activity$ {
        push_stream_subscriber long-polling;
        set $push_stream_channels_path $arg_id;
        push_stream_content_type "application/json";
        push_stream_message_template "~text~";
    }

    ...
}