Kafka For Python Producer.send() 會超時的原因分析

昨天在做方案調研,用python-kafka往 Kafka 裏塞數據時發現了個很奇怪的問題:有的請求會阻塞60s後報錯。
但我們都知道python-kafka是可以完全異步的,他自己用local-thread實現了異步的發送功能。並且看官方Producer API返回的是一個Future,理應不會卡住,但Block卻實實在在的發生了,讓人很費解。

排查問題

胡亂百度

因爲我們用的是gunicorn + gevent,百度了一圈,發現瞭如下問題:

gevent 的 mock 導致pykafka thread staving
https://github.com/dpkp/kafka-python/issues/1867

但這個issue提到該問題已經在1.4.7解決了,我用的是2.0.1,理應沒這問題。

還記得看到了個

uwsgi使用fork的方式來實現多進程

想了想,如果Producer在fork之前被創建會出現線程本身無法被複制的情況,也是有這可能的。

本着死馬當活馬醫的態度,挨個解決這倆問題:

  • gevent 改爲 gthread
  • 在接口第一次調用的時候再生成Producer

結果還是一樣,仍會有Blocking,並且還是60秒

認真分析

胡亂百度果然靠不住,解決問題還是得一點一點來。

首先認真看 例程 與自己寫的代碼。

Producer.send(topic,content).add_errback(err_callback)

好像沒得問題,奇怪的是err竟然沒有調用回調,而是直接在send處就拋出來了

我第一反應是Future這個函數在調用 add_errback 等函數時會block,但感覺這種設計很傻,我自己絕對不會這麼做,於是決定先看send函數的具體實現,果不其然,在send裏果然有Block的地方。

def send(self, topic, value=None, key=None, headers=None, partition=None, timestamp_ms=None):
        ……
        try:
            self._wait_on_metadata(topic, self.config['max_block_ms'] / 1000.0)

            key_bytes = self._serialize(
                self.config['key_serializer'],
                topic, key)
          	 ……
        except Errors.BrokerResponseError as e:
        	……

self._wait_on_metadata(topic, self.config[‘max_block_ms’] / 1000.0)

太明顯了……

看了看Producer 的 __init__ 函數,max_block_ms這個參數可作爲可選參數傳進去,把他設置爲1000,一秒內就可以返回了。

等等,問題好像還沒完誒,你只是改了最大超時,但正常請求不可能block一秒吧?何況你數據都進MQ。

問題原因與解決方法

經過仔細的排查,發現發生Block的數據,都是往不存在的Topic推的。這也能解釋的通,畢竟不存在,就拿不到metadata,就會block。

正常業務雖然不會發生這種往不存在Topic推送的情況,但出於性能考慮,還是加了個 TTLCache,配合2s的超時,基本就可以解決這個問題了。

小插曲

排查完問題很開心,想着打包好Docker鏡像後趕緊提測,但這步又出了問題。

CICD的機器是不能訪問外網的,我已經把pip 的源改成了內網源,但新加的庫有的竟然還是會訪問pypi的源。

搜了一下,在 修改pip/setup.py的源 找到了個解決方法。

看了下應該就是調用了easy_install來安裝其他依賴了,按照文中方法添加對應的文件就可以啦~

PS:百度是真的垃圾,基本上找不到想要的技術文章。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章