【发布时间】:2014-09-07 14:35:22
【问题描述】:
我正在使用 Python3+bottle/UWSGI 开发一个实时 REST API。我的代码出现延迟,有时是 100 毫秒,这在我的应用中很重要。
使用logging 模块,我试图识别我的代码的慢速部分,打印单个代码块运行的时间。我知道这是一种非常糟糕的代码分析方法,但有时它能够很好地完成这项工作。
尽管我发现了一些缓慢的部分,但我仍然遗漏了一些东西——单个部分似乎需要 10 毫秒,但通常它们整体需要 100 毫秒。在一些越来越疯狂的实验让我几乎完全发疯之后,我得出了以下结论:
t = round(100*time.time())
logging.info('[%s] Foo' % t)
logging.info('[%s] Bar' % t)
令人惊讶的是,它给出了:
2014-07-16 23:21:23,531 [140554568353] Foo
2014-07-16 23:21:24,312 [140554568353] Bar
尽管这似乎令人难以置信,但还是有两个后续的logging.info() 调用,并且由于某种原因,它们之间存在近 800 毫秒的间隔。谁能告诉我发生了什么事?值得注意的是,如果有多个 info() 调用,则延迟在整个 API 方法中只出现一次,最常见的是在其最开始(第一次调用之后)。我唯一的假设是磁盘延迟,有几个(但不是那么多!)工作人员同时运行,我使用的是旋转磁盘,没有 SSD。但我认为有缓冲区,操作系统会为我异步执行磁盘刷新。我的假设错了吗?我应该完全避免记录以避免延迟吗?
编辑
根据 Vinay Sajip 的建议,我切换到以下初始化代码:
log_que = queue.Queue(-1)
queue_handler = logging.handlers.QueueHandler(log_que)
log_handler = logging.StreamHandler()
queue_listener = logging.handlers.QueueListener(log_que, log_handler)
queue_listener.start()
logging.basicConfig(level=logging.DEBUG, format="%(asctime)s %(message)s", handlers=[queue_handler])
它似乎工作正常(如果我评论queue_listener.start(),则没有输出),但仍然出现相同的延迟。我看不出这怎么可能,呼叫应该是非阻塞的。我还在每个请求的末尾加上gc.collect(),以确保问题不是由垃圾收集器引起的——没有任何影响。我还尝试关闭一整天的日志记录。延迟消失了,所以我认为它们的来源必须在logging 模块中......
【问题讨论】:
-
我怀疑是系统问题而不是代码问题。例如,如果您在 ubuntu 上并且没有定义交换文件,或者如果您在 Windows 上并且您的硬盘驱动器上的空间小于 20GB,您将体验到响应能力的损失
-
如果您可以将其简化为显示问题的小型独立脚本,请发布。否则,很难看到多线程环境中发生了什么。您能否确定在您的系统中(例如在第三方库中)anywhere 中没有定义同步处理程序?这可能会影响延迟。除了
NullHandler,库不应该添加处理程序,但开发人员并不总是按照他们应该做的事情。 -
另请注意,
basicConfig()的format=参数仅适用于在该调用中添加的处理程序,即在这种情况下适用于QueueHandler。这不使用格式化程序,StreamHandler不会设置格式化程序,因为它只传递给QueueListener。您需要明确设置格式化程序。 -
@VinaySajip 你说得对,这是由 UWSGI 的“神奇”进程分叉引起的。当我打印当前使用的处理程序时,
QueueHandler消失了,默认的StreamHandler再次出现。我现在正在尝试使用延迟初始化来在进程分叉后创建处理程序。
标签: python performance logging latency