【问题标题】:Restarting a program after exception异常后重新启动程序
【发布时间】:2020-03-01 12:50:12
【问题描述】:

我有一个每隔几秒查询一次 API 的程序。每个响应都会触发一些函数,这些函数本身会调用网站等——我不想盲目相信会成功的调用。例如,如果我在foo() 中捕获异常,甚至在foo() 调用的函数中捕获异常,是否可以完全在except 块中重新启动程序?本质上,我想在它的一个子函数中出现异常时调用queryRepeatedly(),而不是将先前的调用保留在堆栈中。

当然,我可以返回标记值并以另一种方式解决这个问题,但程序的结构使得上述方法看起来更简单、更清晰。

# Sample "main" function that I want to call
def queryRepeatedly():
    while True:
        foo()
        bar()
        baz()
        time.sleep(15)

def foo():
    # do something
    try:
        foo2() # makes a urllib2 call that I don't trust
    except:
        #restart queryRepeatedly

queryRepeatedly()

欢迎提出任何建议!谢谢。

【问题讨论】:

  • 异常情况下调用queryRepeatedly()
  • @GrijeshChauhan 将使用干净的堆栈执行?
  • 不,从foo 调用queryRepeatedly 会导致堆栈随着每个异常抛出而增长,直到内存不足。

标签: python function


【解决方案1】:

要重新启动任何东西,只需在try 之外使用while 循环。例如:

def foo():
    while True:
        try:
            foo2()
        except:
            pass
        else:
            break

如果您想将异常向上传递,只需在外部函数而不是内部函数中执行此操作:

def queryRepeatedly():
    while True:
        while True:
            try:
                foo()
                bar()
                baz()
            except:
                pass
            else:
                break
        time.sleep(15)

def foo():
    foo2()

所有这些缩进有点难以阅读,但很容易重构:

def queryAttempt()
    foo()
    bar()
    baz()

def queryOnce():
    while True:
        try:
            queryAttempt()
        except:
            pass
        else:
            break

def queryRepeatedly():
    while True:
        queryOnce()
        time.sleep(15)

但如果你想一想,你也可以将两个while 循环合并为一个。 continue 的使用可能会有点混乱,但看看你是否更喜欢它:

def queryRepeatedly():
    while True:
        try:
            foo()
            bar()
            baz()
        except:
            continue()
        time.sleep(15)

【讨论】:

  • 谢谢,非常彻底。出于记录目的,您是否建议使用 try,除了 foobarbaz 中记录错误的块然后只调用 raise
  • @rogaos:这取决于。有什么foo 知道关于foo2 的、更高级别不/不应该知道的吗?如果没有,请不要放置较低级别的try。例如,如果你只是要去except Exception as e: logging.error('Caught {!r}'.format(e),不要重复三遍;只需在更高级别执行一次即可。
【解决方案2】:

重构这个 - 如果你有足够多的失败,你迟早会得到一个 stackoverflow 错误。

queryRepeatedly 应该只是 query。它应该返回 void 并在失败时抛出异常。

用类似这样的东西,你真正的queryRepeatedly 函数?

while True:
    try:
        query()
    except:
        #handle
    time.sleep(15)

全部循环,不需要递归。

请注意,您必须仔细考虑需要重新启动程序的多少。从您的问题看来,您的实际问题是确保查询在偶尔失败时可以重试,这是我的解决方案所确保的。但是,如果您想清理程序资源——例如,可能已中断的反弹 SQL 连接——那么您需要更仔细地考虑您需要“重新启动”多少程序。一般来说,您需要了解为什么您的查询不知道要解决什么问题,在极端情况下,正确的做法是向可以检查情况并写信的待命人员发送电子邮件或短信适当的补丁或修复。

【讨论】:

  • 但是他可以在#handle 部分中添加什么?这有点棘手,你还没有回答。
  • void?你说的void是什么?
  • @abarnert OP 没有就此提出任何问题。可能会登录 AWS SNS,然后设置一个监视器,当某个时间单位内出现太多故障时发送电子邮件警报。
  • @user2357112 表示不返回任何内容。 void 是类 C 语言中的类型。
  • @djechlin:OP 询问如何在异常情况下重新启动整个事情。您的代码不会这样做——除非您在#handle 部分中添加一些内容来实现它。你没有。
【解决方案3】:

首先制作两个文件。

一个名为 run.py 的文件和一个名为 forever.py 的文件,并将它们放在同一个文件夹中。

转到该文件夹​​中的终端并输入 chmod +x forever.py

run.py

whatever code you want to run

forever.py

#!/usr/local/lib/python3.7
from subprocess import Popen
import sys

filename = sys.argv[1]
while True:
    print("\nStarting " + filename)
    p = Popen("python3 " + filename, shell=True)
    p.wait()

从文件夹中打开一个终端窗口并输入:

python3 ./forever.py retweet.py

启动run.py,如果失败或出现异常,它将重新开始。

您现在有一个模板来确保如果文件崩溃或出现异常,您可以在不离开的情况下重新启动它。如果这对你有帮助,请给我投票!

【讨论】:

    【解决方案4】:

    在您的异常中进行递归调用

    except:
          queryRepeatedly()
    

    【讨论】:

    • 这有点用,但设计很糟糕,因为它会导致无限递归,比如几天。
    • 这会造成堆栈溢出,因为queryRepeatedly() 包含while True 循环。
    • 谢谢,但我要求不要递归
    • 我真的很感谢这个答案,因为它警告了如何不要这样做
    猜你喜欢
    • 2018-10-24
    • 2014-02-13
    • 1970-01-01
    • 2019-11-04
    • 2014-09-14
    • 2023-03-30
    • 2020-09-21
    • 2019-08-10
    • 2011-08-27
    相关资源
    最近更新 更多