nginx single process模式下(master_process off)不能很好的支持reload操作(reload后无法再提供服务)
删除

邀请回答
提问于 2020-06-04 21:54
473 次浏览
共5个回答

发表评论
  • 陶辉
    最佳答案
    2020-06-05 08:06

    single是单进程模式,而nginx是通过新启一个进程,由新进程重载nginx.conf文件,实现reload操作并提供服务的。因此single无法完成。

    master/worker模式可以,因为master是父进程,worker是子进程,而父进程打开了80/443等端口,worker子进程可以共享。这样除了老的worker外,新的reload后的worker子进程也在监听相同的端口,这样老worker就可以在停止listen新连接后,服务完所有的老连接,优雅的退出。

    具体你可以看下我的这篇文章:https://www.nginx-cn.net/article/70

    4
    回复
    举报
    • 贺红杰 2020-06-05 11:52
      谢谢陶老师。 还想确认一下:nginx single单进程模式是否实现完整的配置更新(reload),想要做到的效果是:不启动新的worker,而是让老的worker加载使用新的配置。 因为f-stack版本下的nginx,协议栈是与nginx worker进程绑定在一起的,一旦关闭老的worker进程,就会导致协议栈内部的连接信息丢失,然而又存在reload加载使用新配置的需求。 我觉得reload操作要保证的两个目标是:不直接丢失老的连接; 对新的连接即时提供服务。 这样的话,如果能够让老的worker的使用新的配置,相对来说是一个比较优雅的解决方案。但目前我还不能够确定这种方式是否能够做到,所以想再次请教一下陶老师。
      0
      回复
      举报
    • 陶辉 2020-06-05 14:05
      @376:不可以,新配置目前只能用新worker进程
      0
      回复
      举报
    • 贺红杰 2020-06-05 14:29
      @31: 在目前nginx架构的基础上是否可行呢?如果不可行,存在哪些无法解决的问题? 如果让worker进程支持reload操作,目前我看到的问题点: - 创建新文件时存在多次打开问题 - 创建共享内在时存在多次创建问题 - 监听端口存在多次绑定失败问题
      0
      回复
      举报
    • 陶辉 2020-06-05 14:50
      @376:对的,你说的点都对。最核心的原因,是实现成本太高了! 比如共享内存如果变化 了,就直接打开两份; 比如端口如果不变,那么加2个分支,1个走老配置,1个走新配置,这样也不存在重复绑定的问题。 比如创建新文件时也一样,用引用计数维护,不会重复打开。 但问题是,一旦这样实现,会让Nginx正常处理请求的性能下降,代码逻辑也会复杂许多,因此Igor没有采用这种方式,而是用平滑reload的方式实现灰度升级配置文件。
      0
      回复
      举报
    • 贺红杰 2020-06-05 15:14
      @31: 就是说非要这么蛮干,这条路也可能走的通,但会引入过高的实现和维护成本。 我有一点不明白:这个reload操作应该是配置阶段做的事情,为什么会给nginx运行阶段带来性能损失呢? 刚刚我在openresty-1.13.6.2基础上做了点修改(代码另附),然后发现nginx single单进程模式,现在可以通过reload操作增减server实例
      0
      回复
      举报
    • 贺红杰 2020-06-05 15:15
      @31: diff --git a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c index 76aee08..5883b04 100644 --- a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c +++ b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c @@ -625,6 +625,10 @@ ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags) c->fd, op, ee.events); if (epoll_ctl(ep, op, c->fd, &ee) == -1) { + if (ngx_errno == EEXIST) { + return NGX_OK; + } + ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno, "epoll_ctl(%d, %d) failed", op, c->fd); return NGX_ERROR; diff --git a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c index 2696b62..7d48716 100644 --- a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c +++ b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c @@ -398,6 +398,24 @@ ngx_single_process_cycle(ngx_cycle_t *cycle) } ngx_cycle = cycle; + + /* + * disable deleting previous events for the listening sockets because + * in the worker processes there are no events at all at this point + */ + ngx_listening_t *ls = cycle->listening.elts; + for (i = 0; i < cycle->listening.nelts; i++) { + ls[i].previous = NULL; + } + + for (i = 0; cycle->modules[i]; i++) { + if (cycle->modules[i]->init_process) { + if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) { + /* fatal */ + exit(2); + } + } + } } if (ngx_reopen) {
      0
      回复
      举报
    • 贺红杰 2020-06-05 15:20
      @31: 我现在这样做,是否存在问题? 如果这样子就可以让nginx single单进程模式比较好的支持reload操作,为什么nginx官方一直没有实现该功能?
      0
      回复
      举报
    • 陶辉 2020-06-05 15:21
      @376:因为在正常的逻辑里,要多出很多判断。 你至少需要一个flag,表明现在是平滑过渡状态,还是正常状态。在很多场景中都需要判断这个flag。 例如建立连接时,80端口上过来的连接,本来直接设定callback的handler就行了,但现在就得判断flag,这是用新配置的连接,还是老配置的连接,再设定不同的callback。 这种判断的地方数量还不少,这就会带来性能损耗,远不如直接两个worker进程来得简单直接。
      0
      回复
      举报
    • 陶辉 2020-06-05 15:22
      @376:nginx single单进程模式不能用于生产环境,因为在worker进程意外挂掉后,没有进程快速拉起它,而且也不支持灰度升级Nginx,特别是无法使用多核心CPU系统的能力
      0
      回复
      举报
    • 贺红杰 2020-06-05 15:27
      @31: 就是说因为这个模式仅限用于调试目的,所以没有引发Nginx官方的改善动力喽
      0
      回复
      举报
    • 贺红杰 2020-06-05 15:33
      @31: 不过这样子似乎就验证了这个想法:可以做到不启动新的worker,老的worker可以加载使用新配置,因为nginx single单进程模式就是这样一种场景
      0
      回复
      举报
  • 皮皮鲁
    2020-06-19 09:25

    我也考虑过这个问题,为什么配置改变以后一定要启动新的process来进行平滑处理。为什么不能在老的process中另外在内存中保存新的配置。 我现在的理解是,这样做对nginx来说可能复杂了。nginx本身框架和各个模块都需要额外的逻辑去处理新旧两份甚至多份内存中的配置,这样对于拥有大量第三方模块的nginx几乎是不可能的。而且现有的模块都需要为此做修改。

    2
    回复
    举报
  • 贺红杰
    2020-06-18 15:12

    谢谢陶老师。

    还想确认一下:nginx single单进程模式是否实现完整的配置更新(reload),想要做到的效果是:不启动新的worker,而是让老的worker加载使用新的配置。

    因为f-stack版本下的nginx,协议栈是与nginx worker进程绑定在一起的,一旦关闭老的worker进程,就会导致协议栈内部的连接信息丢失,然而又存在reload加载使用新配置的需求。

    我觉得reload操作要保证的两个目标是:不直接丢失老的连接; 对新的连接即时提供服务。


    这样的话,如果能够让老的worker的使用新的配置,相对来说是一个比较优雅的解决方案。但目前我还不能够确定这种方式是否能够做到,所以想再次请教一下陶老师。

    1
    回复
    举报
  • 贺红杰
    2020-06-18 15:12

    diff --git a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c
    index 76aee08..5883b04 100644
    --- a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c
    +++ b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/event/modules/ngx_epoll_module.c
    @@ -625,6 +625,10 @@ ngx_epoll_add_event(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags)
                        c->fd, op, ee.events);
         if (epoll_ctl(ep, op, c->fd, &ee) == -1) {
    +        if (ngx_errno == EEXIST) {
    +            return NGX_OK;
    +        }
    +
             ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_errno,
                           "epoll_ctl(%d, %d) failed", op, c->fd);
             return NGX_ERROR;
    diff --git a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c
    index 2696b62..7d48716 100644
    --- a/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c
    +++ b/src/openresty-1.13.6.2/bundle/nginx-1.13.6/src/os/unix/ngx_process_cycle.c
    @@ -398,6 +398,24 @@ ngx_single_process_cycle(ngx_cycle_t *cycle)
                 }
                 ngx_cycle = cycle;
    +
    +            /*
    +             * disable deleting previous events for the listening sockets because
    +             * in the worker processes there are no events at all at this point
    +             */
    +            ngx_listening_t *ls = cycle->listening.elts;
    +            for (i = 0; i < cycle->listening.nelts; i++) {
    +                ls[i].previous = NULL;
    +            }
    +
    +            for (i = 0; cycle->modules[i]; i++) {
    +                if (cycle->modules[i]->init_process) {
    +                    if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
    +                        /* fatal */
    +                        exit(2);
    +                    }
    +                }
    +            }
             }
             if (ngx_reopen) {


    0
    回复
    举报
  • zongzw
    2020-06-11 21:08

    single process 现实情况中一般不会被使用。

    我知道的single process的使用场景是nginx代码调试。

    详细的用vscode调试方法可以参考:

    https://github.com/zongzw-nginx/vscode-nginx


    NGINX 配置中使用的配置参数和内容为:

     

    参数:      -g "daemon off; master_process off;"

    内容:

    worker_processes 1;

     

    events {}

     

    http {

        server {

            listen 80;

     

            location / {

                return 200 "hello world!\n";

            }

        }

    }

     


    0
    回复
    举报
提问者

暂无个人介绍

  • 文章

  • 粉丝

  • 被赞

您已邀请位用户
版权所有©F5 Networks,Inc.保留所有权利。京ICP备16013763号-5