我正在使用Python3+bottle/UWSGI开发实时REST API。我的代码中存在延迟,有时延迟长达100毫秒,在我的应用程序中这确实很重要。我使用logging模块尝试识别代码的慢部分,打印单个代码块运行所需的时间。我知道这是一个极其不精确的代码剖析方法,但有时它能够做好工作。尽管我已经确定了一些慢的部分,但仍然缺少某些东西 - 单独的部分似乎需要10毫秒的时间,但通常需要整体100毫秒。经过一些越来越疯狂的实验,几乎把我逼疯了,我得出了以下结论:
尽管这似乎难以置信,但是有两个连续的
编辑
根据Vinay Sajip的建议,我转换为以下初始化代码:
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
模块中...
basicConfig()
中的format=
参数仅适用于在该调用中添加的处理程序,即在此情况下的QueueHandler
。它不使用格式化程序,并且StreamHandler
不会设置格式化程序,因为它只传递给QueueListener
。您需要显式地在其中设置格式化程序。 - Vinay Sajip