【问题标题】:How to add a SIGUSR1 handler to a Twisted program?如何将 SIGUSR1 处理程序添加到 Twisted 程序?
【发布时间】:2017-03-19 22:34:21
【问题描述】:

我正在寻找一种方法来检查基于 Twisted 的程序的状态,以便确定连接的客户端数量并查看我收集的其他指标(例如连接建立时间或最后一次联系)。

我的想法是为进程添加SIGUSR1 的信号处理程序,这样当进程接收到它时,它会将状态转储到已知位置的文件中。这有几个问题:

  • Twisted 使用自己的处理程序覆盖信号处理程序,因为当信号到达时,进程以stdout 上的“收到用户信号 1”结束,并且我的代码没有被调用
  • 考古研究on a mailing list 显示Twisted 不会覆盖SIGINT 的处理程序;然而事实并非如此(Python 2.7.11 和 Twisted 16.1.1),进程在stdout 上以“KeyboardInterrupt”退出。
  • 其他资源建议reactor.run(installSignalHandlers=False)reactor.run(installSignalHandlers=0),但这似乎没有效果。

因此我有几个问题:

  1. 处理信号的意识形态正确方法是什么? (如果可能的话)
  2. 为 Twisted 服务器实施这种“状态自省”工具的推荐方法是什么? (我正在考虑让它监听另一个端口上的 TCP 连接,并将其用作 POSIX 信号的替代方案 - 但我觉得我让事情变得太复杂了)。

感谢您抽出宝贵时间阅读本文,我期待着来自这个星球的蜂巢思维的提示。

这是邮件列表的相关摘录:

> 如果你这样做,你会破坏 spawnProcess。幸运的是,如果你只是 > 安装一个 SIGINT 处理程序,Twisted 不会踩它: > exarkun 魅力:~$ python > Python 2.5.2(r252:60911,2008 年 7 月 31 日,17:28:52)[GCC 4.2.3 > (Ubuntu 4.2.3-2ubuntu7)] 在 linux2 上 > 输入“帮助”、“版权”、“学分”或“许可”以获取更多信息。 > >>> def f(*a): > ...打印'信号' > ... >>> 导入信号 > >>> signal.signal(signal.SIGINT, f) > > >>> 从 twisted.internet 导入反应器 > >>> reactor.run() > 信号 > 信号 > 信号 > 退出

【问题讨论】:

    标签: python linux twisted


    【解决方案1】:

    从 Twisted 17.1 开始,只有 twistd 安装 SIGUSR1 处理程序,如果您已经为该信号安装了处理程序,它将跳过安装。

    当您将 SIGUSR1 发送到您的进程时,您在标准输出上看到的消息:

    user signal 1 received
    

    是操作系统默认系统处理程序。该默认值的行为是退出进程。这表明您和 Twisted 都没有为 SIGUSR1 安装处理程序。当您认为它正在运行并且您没有使用twistd时,您的处理程序安装代码似乎没有运行。

    不过……

    处理信号的意识形态正确方法是什么? (如果可能的话)

    这很可能根本不可能。信号是一个兔子洞。如果您在这件事上有任何选择,请远离他们。在任何 Python 程序中引入异步信号传递都是令人担忧的。 可能在某些情况下可以正确可靠地执行,但我不会打赌。由于 POSIX 的缺点和 Python 在这些之上添加的其他复杂性,信号与多线程的交互很复杂。信号处理程序是一个共享的全局资源(如您所述),因此在尝试使用它们的不同代码库之间存在复杂的交互。

    最后,您可以使用多种替代方案来回避这些问题。

    为 Twisted 服务器实现这种“状态自省”功能的推荐方法是什么? (我正在考虑让它监听另一个端口上的 TCP 连接,并将其用作 POSIX 信号的替代方案 - 但我觉得我让事情变得太复杂了)。

    使用 POSIX 信号以外的机制可能看起来更复杂,但我建议它更简单,尽管它可能需要更多的输入。

    我没有关于“状态自省”的一般建议,但这里有一个针对这种情况的具体建议:将 HTTP 服务器添加到您的进程中。

    举个简单的例子:

    from json import dumps
    
    from twisted.web.server import Site
    from twisted.web.resource import Resource
    from twisted.internet.endpoints import serverFromString
    
    class StateResource(Resource):
        def render(self, request):
            return dumps({"your": "state info"})
    
    root = Resource()
    root.putChild(b"", StateResource())
    serverFromString("tcp:12345").listen(Site(root))
    

    是的,它比等效的基于信号的代码长了 10 行。但是,它没有奇怪的多线程或多进程交互。它独立于平台。它不会踩踏共享资源(也不会踩踏)——除了我随机选择的那个 tcp 端口(如果你不喜欢这样,你可能可以在 UNIX 套接字上运行它——也授予访问控制,如果这对您很重要)。

    当您想要向您的应用程序添加健康检查以便您的主管可以对其进行监控时,这个真正开始成为明显的优越解决方案的地方。然后您想添加一个指标报告器,以便您的监控系统可以提取该数据并将其呈现给您的 SRE 或运营团队。等等。可以处理该附加功能的基于信号的解决方案变得更加复杂,并且我怀疑最终的总 SLOC 计数高于 HTTP 解决方案。即,HTTP 服务器可以很好地适应新的功能需求。基于信号的解决方案不会。

    【讨论】:

    • 感谢您快速而详细的回复。我继续制作了一个 Web 仪表板以实现相同的效果(并且还给它增加了一些花里胡哨的东西)。
    • 我正要问一个类似的问题,关于如何编写由SIGHUP 触发的配置重新加载工具,并在我写我的问题时发现了这个。这个解决方案很完美,谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-01
    • 1970-01-01
    • 2019-01-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-02-26
    相关资源
    最近更新 更多