【问题标题】:ZeroMQ publisher not binding after forever restartallZeroMQ 发布者在永远重启后未绑定
【发布时间】:2017-05-07 20:19:19
【问题描述】:

场景

我有多个 NodeJS 脚本在 Ubuntu 操作系统中以永久模式运行。其中一个文件 (start.js) 导入一个文件,该文件通过将 ZMQ 发布者绑定到指定端口来启动它。当我以永久模式单独启动这个 start.js 文件时,它会绑定并启动发布者,并且我能够通过连接到该端口的 ZMQ 订阅者获取该发布者发布的数据。

我正在通过检查退出、SIGINT 和 SIGUSR 事件来优雅地关闭发布者。

每当我使用forever restart 单独重新启动这个 start.js 文件时,发布者就会绑定并成功启动。如果我手动停止它(使用forever stop)并使用forever start重新启动它,它也可以正常工作[也适用于我手动停止(使用forever stopall)并一一启动所有永久脚本的情况]。

注意:所有永久停止和重新启动命令都使用 CLI 选项 --killSignal=SIGINT 运行。

问题

但是当我执行forever restartall --killSignal=SIGINT 时,发布者无法绑定。它说该地址已经在使用中(我已经使用netstat 进行了检查,并且该端口上没有 tcp 套接字)。当我停止所有脚本并一一启动它们时,它会正常绑定并成功启动。

我检查了这些终止信号是否被发布者脚本捕获并在退出之前关闭发布者套接字。

尝试失败:

  • 降低了 tcp 套接字的 TIME_WAIT 状态。

  • 启用 TIME_WAIT 套接字的重用。

  • 我认为 tcp 套接字需要时间才能从 TIME_WAIT状态,1000ms后尝试绑定发布者 每次绑定失败,但脚本正在尝试绑定并失败 每次尝试。

  • 尝试使用 SIGINT、SIGUSR1 kill 永久重启所有脚本 信号并在绑定发布者的脚本中处理它们 插座。

这就是我在发布者中处理 SIG* 事件的方式:

process.stdin.resume();
function exitHandler(options, err){
    if (options.cleanup) console.log('pub-clean');
    if (err) console.log("pub--" + err.stack);
    if (options.exit){
        socket.close();
        console.log("Publisher Closed")
        process.exit();
    }
}

process.on('exit', exitHandler.bind(null,{cleanup:true}));
process.on('SIGINT', exitHandler.bind(null, {exit:true}));
process.on('uncaughtException', exitHandler.bind(null,{exit:true}));
process.on('SIGUSR2', exitHandler.bind(null, {exit:true}));
process.on('SIGTERM', exitHandler.bind(null, {exit:true}));

为什么永远重启所有脚本会导致发布者脚本绑定失败?

如何使发布者脚本在永远重启时绑定?

【问题讨论】:

  • 您如何在代码中实现 SIG*-s 处理程序以优雅地关闭/终止/退出所有 ZeroMQ 资源和上下文?
  • @user3666197 >> 我已经包含了用于处理上述 SIG* 事件的 code-sn-p。
  • 好吧,正如所见,您的 SIG*-handlers 代码在正常关闭/终止 ZeroMQ 资源方面没有任何作用。您能否在 ZeroMQ 初始化的设置部分发布您的代码?
  • @user3666197 > 为什么 SIG* 处理程序的代码什么都不做?如果没有此代码,当进程收到 SIGINT 或 SIGTERM 或退出信号时,它不会关闭套接字。
  • 恕我直言,ZeroMQ 资源的优雅释放不是通过系统级 SIG* / *KILL 完成,而是通过执行 ZeroMQ-优雅的释放步骤。正如到目前为止所发布的,您在代码中根本没有这样做,因此 ZeroMQ 资源可能而且很可能仍然挂起(至少 I/O 线程似乎如此)。

标签: node.js sockets zeromq forever


【解决方案1】:

ZeroMQ-resources 建议以受控方式释放

正如上面 cmets 中所讨论的,ZeroMQ 资源的真正优雅的释放不是通过系统级的SIG* / *KILL,但通过执行 ZeroMQ 推荐的优雅释放步骤。

到目前为止,您在代码中根本没有这样做,因此 ZeroMQ 资源可能而且很可能仍然挂起(至少 I/O 线程似乎如此)。

检查您在(尚未发布)设置中使用的 ZeroMQ 套接字设置(.setsockopt() 在设置阶段使用的调用)并添加:

  1. 确保所有套接字设置(无论是否使用)的非阻塞.close()设置
  2. 只有在[1]确定有效后才执行.close()
  3. 最后,执行显式ZeroMQ Context实例.term()

这被认为是保证所有(内部处理的)资源的 ZeroMQ-graceful-release。



关于示例代码请求:

优雅的释放

void  msLIB.deinit() {
      aComment.ADD( "msLIB.INFO: msLIB.deinit() TracePOINT.<BoPROC>|", False );

   // --------------------------------------------------------------------------------------<THANKS>
   // DO NOT EDIT: the below  IS TRULY NEEDED or else one might get some nice memory leaks!
   // ------------            |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||   


   // -----------------------------------------------------------------------
   // ZMQ-IMPERATIVE ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
   // _______________________________________________________________________ msMOD: ZMQ_Safe&CleanCODE_IMPERATIVE
      zmq_setsockopt(   zmqSpeaker,  ZMQ_LINGER, 0 );                         // no Sending QUEUE .... on ZMQ_PUBLISHER end
      zmq_close(        zmqSpeaker  );                                        // Protect against memory leaks on shutdown.
                        aComment.ADD( "<1>", False );
                        aComment.ADD( " [[[ZMQ]]]<speaker_socket>.set( ZMQ_LINGER ) / .close()-ed ", True );

      zmq_setsockopt(   zmqListener, ZMQ_LINGER, 0 );                         // aKBD.PUB 
      zmq_close(        zmqListener );                                        // Protect against memory leaks on shutdown.
                        aComment.ADD( "<2>", False );
                        aComment.ADD( " [[[ZMQ]]]<listener_socket>.set( ZMQ_LINGER ) / .close()-ed ", True );      

      zmq_term(         zmqContext  );                                        // Protect against memory leaks on shutdown.
                        aComment.ADD( "<3>", False );
                        aComment.ADD( " [[[ZMQ]]]<context>.term()-ed ", True );

   // _______________________________________________________________________ msMOD: ZMQ_Safe&CleanCODE_IMPERATIVE

   // ------------
   // DO NOT EDIT: the above  IS TRULY NEEDED or else one might get some nice memory leaks!               
   // --------------------------------------------------------------------------------------<THANKS>

      aComment.ADD( "|<EoPROC>", False );
      msLIB.aSnapshot.MAKE();
      aComment.ADD( "|aSnapshot.MAKE()-<DONE>", True );
      return;
   }


缺少“内置”控件

可以扩展架构,以便包含自己的软信令代码,以应对所有情况,这些情况需要比通过 SIGKILL 等进行更柔和的处理。

【讨论】:

  • @user36666197 >> 感谢您的回复 :),您能否提供一些关于如何在 NodeJS 中执行此操作的示例代码?我只能在 ZMQ 指南 here 中找到有关处理 CTRL-C 中断信号的部分
  • @saikiranboga 检查更新。将代码设计为主要是非阻塞的(这是 ZeroMQ 最大的准则之一)简化了对系统级信令的需求和对其(如果可用)处理程序的依赖 + 允许您围绕非阻塞层构建所有代码阻塞(同样是非阻塞的)进程之间的软信号。
  • @user36666197 >> 我能够将 linger 选项设置为 0 的 setsockopt 并关闭套接字,但我看不到在 NodeJS 中关闭 zmq 上下文的选项。我检查了模块代码here,他们将上下文设置为空,而不是关闭它,不知道这是否可能导致问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多