用 Python 编写的强大的服务器无限循环
Posted
技术标签:
【中文标题】用 Python 编写的强大的服务器无限循环【英文标题】:Robust endless loop for server written in Python 【发布时间】:2016-04-29 07:31:09 【问题描述】:我写了一个服务器来处理事件,在处理事件期间未捕获的异常一定不能终止服务器。
服务器是一个单一的非线程python进程。
我想终止这些错误类型:
键盘中断 内存错误 ...内置异常列表很长:https://docs.python.org/2/library/exceptions.html
我不想重新发明这种异常处理,因为我猜它之前已经做过好几次了。
如何进行?
-
有一个白名单:列出正常的异常并处理下一个事件是正确的选择
有一个黑名单:表示终止服务器是正确选择的异常列表。
提示:这个问题与在后台运行 unix 守护进程无关。这不是关于双叉,也不是关于重定向 stdin/stdout :-)
【问题讨论】:
【参考方案1】:我会以与您想的类似的方式执行此操作,在创建一个应通过并重新引发的异常将set
列入黑名单。
使用 Gandalf 处理程序 将确保 GeneratorExit
、SystemExit
和 KeyboardInterrupt
(所有系统退出异常)通过并在调用中没有其他处理程序存在时终止程序堆。在这里您可以使用type(e)
检查捕获的异常e
的__class__
实际上属于列入黑名单的异常集合,然后重新raise
。
作为一个小示范:
import exceptions # Py2.x only
# dictionary holding exception_name: exception_class
excptDict = vars(exceptions)
exceptionNames = ['MemoryError', 'OSError', 'SystemError'] # and others
# set containing black-listed exceptions
blackSet = excptDict[exception] for exception in exceptionNames
现在blackSet = OSError, SystemError, MemoryError
持有我们想要不处理的非系统退出异常的类。
try-except
块现在可以如下所示:
try:
# calls that raise exceptions:
except Exception as e:
if type(e) in blackSet: raise e # re-raise
# else just handle it
一个使用BaseException
捕获所有异常的示例 可以帮助说明我的意思。 (这样做仅用于演示目的,以便了解这种提升最终将如何终止您的程序)。 请注意:我不建议您使用BaseException
;我使用它是为了演示哪些异常实际上会“通过”并导致终止(即 BaseException
捕获的所有内容):
for i, j in excptDict.iteritems():
if i.startswith('__'): continue # __doc__ and other dunders
try:
try:
raise j
except Exception as ex:
# print "Handler 'Exception' caught " + str(i)
if type(ex) in blackSet:
raise ex
except BaseException:
print "Handler 'BaseException' caught " + str(i)
# prints exceptions that would cause the system to exit
Handler 'BaseException' caught GeneratorExit
Handler 'BaseException' caught OSError
Handler 'BaseException' caught SystemExit
Handler 'BaseException' caught SystemError
Handler 'BaseException' caught KeyboardInterrupt
Handler 'BaseException' caught MemoryError
Handler 'BaseException' caught BaseException
最后,为了使这个 Python 2/3 不可知,您可以 try
和 import exceptions
如果失败(在 Python 3 中就是这样),则退回到导入包含所有 @ 的 builtins
987654350@;我们按名称搜索字典,所以没有区别:
try:
import exceptions
excDict = vars(exceptions)
except ImportError:
import builtins
excDict = vars(builtins)
我不知道是否有更聪明的方法来实际执行此操作,另一种解决方案可能不是使用带有信号 except
的 try-except
,而是使用 2 个处理程序,一个用于黑名单异常,另一个用于对于一般情况:
try:
# calls that raise exceptions:
except tuple(blackSet) as be: # Must go first, of course.
raise be
except Exception as e:
# handle the rest
【讨论】:
【参考方案2】:最上面的例外是BaseException
。其下有两组:
Exception
派生
其他一切
Stopiteration
、ValueError
、TypeError
等都是Exception
的例子。
GeneratorExit
、SystemExit
和 KeyboardInterrupt
之类的东西不是 Execption
的后代。
所以第一步是捕获Exception
而不是BaseException
,这将允许您轻松终止程序。我还建议将GeneratorExit
捕获为 1) 除非手动提升它,否则它永远不会被实际看到; 2)您可以记录它并重新启动循环; 3) 它的目的是表示生成器已经退出并且可以清理,而不是程序应该退出。
下一步是用足够详细的信息记录每个异常,以便您有可能找出问题所在(当您稍后开始调试时)。
最后,您必须自己决定要终止的Exception
派生异常中的哪一个(如果有):我建议您使用RuntimeError
和MemoryError
,尽管您可以通过以下方式解决这些问题只需停止并重新启动您的服务器循环。
所以,真的,这取决于你。
如果有其他严重到足以退出的错误(例如尝试加载配置文件时的IOError
),那么负责加载配置文件的代码应该足够聪明,可以捕捉到IOError
并改为提高SystemExit
。
至于白名单/黑名单 - 使用黑名单,因为应该只有少数(如果有的话)基于 Exception
的异常需要实际终止服务器。
【讨论】:
这也可能有助于决定管理哪个Exception
s:内置异常层次结构:docs.python.org/3.5/library/exceptions.html#exception-hierarchy以上是关于用 Python 编写的强大的服务器无限循环的主要内容,如果未能解决你的问题,请参考以下文章
我在 Python 中编写了一个代码,即使我使用了 break 语句并继续,它仍然会进入 Python 中的无限循环: