uwsgi多程式配合kafka-python訊息無法傳送

lotus_ruan發表於2021-09-09

在工作中,使用uwsgi部署專案,其中uwsgi設定為多程式,並且python中使用了kafka-python模組作為生產者不斷產生資料,但上線不久後幾乎所有的生產者訊息都報:KafkaTimeoutError這個錯誤,並且在kafka伺服器中並沒有發現收到任何訊息。

於是看了看kafka-python原始碼,發現在執行send方法後,訊息並沒有立即傳送,而是放到本地的快取中,在生成KafkaProducer例項時,有個選項buffer_memory設定了快取的大小,預設為32M,然後如果這個buffer滿了就會報KafkaTimeoutError,所以初步判斷兩個原因:

  1 生產者訊息並沒有傳送出去,

  2 或者訊息傳送相對於訊息生成來說過於緩慢導致

同時又因為看到kafka伺服器中並沒有接收到任何訊息,遂排除第二個原因。也就是說生產者訊息沒有傳送出去。於是採用同樣的配置用寫了一個指令碼發現kafka伺服器可以接收到訊息,鑑定是我的生產者有問題,遂谷歌解決問題,找到該帖子:。釋出人情況和我差不多,作者回復到:

You cannot share producer instances across processes, only threads. I expect that is why the master process pattern is failing.

Second, producer.send() is async but is not guaranteed to deliver if you close the producer abruptly. In your final example I suspect that your producer instances are so short-lived that they are being reaped before flushing all pending messages. To guarantee delivery (or exception) call producer.send().get(timeout) or producer.flush() . otherwise you'll need to figure out how to get a producer instance per-uwsgi-thread and have it shared across requests (you would still want to flush before thread shutdown to guarantee no messages are dropped)

 

大體上說明了兩點:

  1 多程式共享同一個生產者例項有問題

  2 send方法是非同步的,當執行完send後立即關閉生產者例項的話可能會導致傳送失敗。

第二點錯誤我沒有犯,沾沾自喜,繼續看評論:

Aha, thanks! After looking more closely at uWSGI options I discovered the lazy-apps option, which causes each worker to load the entire app itself. This seems to have resolved my issue.

提問者說他解決了該問題,於是查一查uwsgi中的lazy-apps,發現改文章:,其中說到:

預設情況下,uWSGI在第一個程式中載入整個應用,然後在載入完應用之後,會多次 fork() 自己。

我看看了我自己的程式碼我確實是在app生成之前生成了生產者例項,這就導致該例項被父程式與其子程式共享。問題終於明白,開始解決:

  1 使用lazy-apps,這樣就可以了。

  2 不使用lazy-apps,在程式碼層面解決問題: 

圖片描述

# producer.py檔案import jsonfrom kafka import KafkaProducerclass Single(object):    """單例模式"""
    def __new__(cls, *args, **kwargs):        if not hasattr(cls, "_instance"):
            cls._instance = super().__new__(cls)            if hasattr(cls, "initialize"):
                cls._instance.initialize(*args, **kwargs)        return cls._instanceclass MsgQueue(Single):    """
    這個整成單例模式是因為:uwsgi配合kafka-python在多程式下會有問題,這裡希望每個程式單獨享有一個kafka producer例項,
    也就是說當初始化app物件後,並不會生成producer例項,而是在執行時再生成,
    具體參考:    """
    app = None    def initialize(self):
        self.producer = KafkaProducer(bootstrap_servers=self.app.config["MQ_URI"],
                                      api_version=self.app.config["KAFKA_API_VERSION"])

    @classmethod    def init_app(cls, app):
        cls.app = app    def send(self, topic, data):        """
        :param topic:
        :param data:
        :return:        """
        data = json.dumps(data, ensure_ascii=True)
        self.producer.send(topic, data.encode())# app.py檔案from producer import MsgQueue
...
MsgQueue.init_app(app)# 業務邏輯中用到生產者的檔案from producer import MsgQueue
...
MsgQueue().send(msg)

圖片描述

 

作者:

原文出處:https://www.cnblogs.com/MnCu8261/p/10482031.html  

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/1762/viewspace-2822049/,如需轉載,請註明出處,否則將追究法律責任。

相關文章