[英]Logging in an asynchronous Tornado (python) server
我正在開發一個應用程序,我可能需要在其中記錄到達服務器的整個流量。 此功能可以打開或關閉,也可以在捕獲異常時使用。
無論如何,我擔心磁盤 I/O 操作的阻塞性質及其對服務器性能的影響。 處理請求(主要是 POST http 請求)時應用的業務邏輯是異步的,因此每個網絡或數據庫調用都是異步執行的。
另一方面,我擔心線程在等待磁盤 IO 操作完成時的延遲。 記錄的消息可以是幾個字節到幾 KB,但在某些情況下可能只有幾 MB。 數據寫入磁盤時線程沒有真正需要暫停,http請求肯定可以在此時完成,並且在數據寫入磁盤時ioloop線程沒有理由不處理另一個任務。
所以我的問題是:
非常感謝任何意見或建議,
埃雷茲
對於“正常”日志記錄(每個請求幾行),我總是發現直接記錄到文件就足夠了。 如果您將所有流量都記錄到服務器,則情況可能並非如此。 有一次我需要做類似的事情,我只是用tcpdump
從外部捕獲流量,而不是修改我的服務器。
如果您想在進程中捕獲它,請從主線程寫入文件開始。 與往常一樣,在采取激烈行動之前衡量您自己環境中的事情( IOLoop.set_blocking_log_threshold
可用於確定您的日志記錄是否有問題)。
如果從主線程阻塞太長時間,您可以寫入由另一個線程處理的隊列,或者異步寫入管道或套接字到另一個進程(系統日志?)。
“異步寫入管道或套接字到另一個進程(系統日志?”
怎么會這樣? log_request
是一個普通函數 - 不是協程,並且所有默認的 python 處理程序都不是由 asyncio 事件循環驅動的,因此它們不是真正的異步。 恕我直言,這是導致 Tornado 性能低於 ie 的因素之一。 aiohttp。 寫入內存或使用 udp 速度很快,但無論如何它都不是異步的。
從大約 2016 年的問題開始,我就有了運行在像樣的裸機機器上的 Python 3.4 Tornado 4 應用程序的操作經驗。 該應用程序與很少的 3rd 方 HTTP API 交互,並記錄了一些交互,以便將來進行潛在的故障排除(這類似於 OP 的要求)。 這台機器有一個 HDD RAID。 據我所知,該應用程序的流量並不高。
Tornado 4 有自己的 IO 循環實現(Tornado 5+ 現在使用asyncio
),並且有一個有趣的代碼檢測,由IOLoop.set_blocking_log_threshold
控制。 基本上,只要循環被阻塞的時間超過threshold
秒,它就會在堆棧跟蹤中記錄一條WARNING
記錄。 我可以從 Sentry 時間線中找到當時的幾個屏幕截圖,用於將閾值設置為 1 秒的警告。
大多數警告都有以日志文件處理程序刷新結束的堆棧跟蹤。 這是一個旋轉和 gzip 文件處理程序。 后者可能會解釋什么可能需要更長的時間,但無論如何對於應用程序來說,希望對日志記錄負全部責任。 解決方案是 stdlib 對logging.handlers.QueueHandler
和logging.handlers.QueueListener
。
Python 日志記錄手冊有專門的部分介紹處理阻止. 這是它的示例(其中listener.start
啟動一個線程,讀取隊列並將記錄委托給handler
):
que = queue.Queue(-1) # no limit on size
queue_handler = QueueHandler(que)
handler = logging.StreamHandler()
listener = QueueListener(que, handler)
root = logging.getLogger()
root.addHandler(queue_handler)
formatter = logging.Formatter('%(threadName)s: %(message)s')
handler.setFormatter(formatter)
listener.start()
# The log output will display the thread which generated
# the event (the main thread) rather than the internal
# thread which monitors the internal queue. This is what
# you want to happen.
root.warning('Look out!')
listener.stop()
對於涵蓋邊緣情況的QueueHanlder
實現的實際參考,可以使用chronologer.client.QueueProxyHandler
。
asyncio
檢測 asyncio
有一個調試模式。
默認情況下 asyncio 在生產模式下運行。 為了方便開發,asyncio 有一個調試模式。 [...] 啟用調試模式時:
- asyncio 檢查未等待的協程並記錄它們; 這減輕了“忘記等待”的陷阱。
- 許多非線程安全的異步 API(例如
loop.call_soon()
和loop.call_at() methods
)如果從錯誤的線程調用它們會引發異常。- 如果執行 I/O 操作所需的時間過長,則會記錄 I/O 選擇器的執行時間。
- 記錄超過 100 毫秒的回調。
loop.slow_callback_duration
屬性可用於設置被視為“慢”的最小執行持續時間(以秒為單位)。
它可能看起來比 Tornado 4 更豐富,但實際上並非如此。 首先,它不適用於生產(並且缺少一個非常重要的指標)。 此外,這是一個沒有堆棧跟蹤的事后警告,而 Tornado 的實現基於signal.SIGALRM
並在閾值 hit 處提供堆棧跟蹤。
您是否注意到警告並沒有完全消失? 但我可以向您保證,日志記錄問題已修復。 令我驚訝的是,導致這些罕見問題的原因是uuid.uuid4
,它可能會阻塞在具有空熵池的機器上,但那是另一回事了。
asyncio
維護者之間關於異步文件 IO、日志記錄和aiofiles
庫的Python-tulip 小組討論
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.