記一次單例模式遇到的坑

若相惜發表於2018-10-26

問題的起因:
我們有一個訊息系統,處理各種非同步訊息。比如:管理人員給使用者的公告,使用者的動態釋出需要通知管理人員稽核,稽核透過後需要通知使用者和更新搜尋引擎的資料。這就存在著在某個地方會發兩個及以上的訊息。

訊息系統基於 nsq ,我們先來看看基本的程式碼, EventSender 類:

    private $sender;

    public static function getInstance()
    {
        if (!(self::$instance && (self::$instance instanceof self))) {
            self::$instance = new self();
        }

        return self::$instance;
    }

    public function sendEvent($eventName,  $topic, $data = [])
    {
        $this->init($topic);
        return $this->sender->sendEvent($eventName, $data);
    }

    private function init($topic)
    {
        $this->sender = new Sender($this->senderName, $this->topicHost, $this->topicName, $this->appEnv);
    }

上面只是抽取了部分程式碼。呼叫發訊息的方法看起來像這樣:

EventSender::getInstance()->sendEvent('UPDATE_INDEX', 'TOPIC_USER', ['user_id' => 1]);
EventSender::getInstance()->sendEvent('MESSAGE', 'TOPIC_MESSAGE', ['user_id' => 1]);

當使用者成功釋出動態以後,我們需要非同步的去更新使用者資訊,並給使用者傳送一個通知,告知他的資訊已被更新。因為更新操作和通知使用者是在兩個 TOPIC 上,所以上述程式碼只能收到更新訊息,通知使用者的資訊不能成功送達。

開始一直以為是不能同時驅動 nsq 的兩個 TOPIC ,在同步的情況下,TOPIC_USER 和 TOPIC_MESSAGE 只有一個能消化。在網上也沒找到相應的回答,無論怎麼除錯都收不到兩個訊息。

自以為對單例模式有一定的認識,之前沒遇到這樣的問題也沒注意。在仔細檢視程式碼後發現,每次傳送訊息都會去執行 init 方法,都會重新例項化 Sender 類,而每次例項化都會傳遞 TOPIC 引數。如果使用單例模式的話,當再呼叫發訊息方法的時候,因為例項已經存在,就不會再去初始化 TOPIC 。所以我們需要這樣來呼叫:

(new EventSender)->sendEvent('UPDATE_INDEX', 'TOPIC_USER', ['user_id' => 1]);
(new EventSender)->sendEvent('MESSAGE', 'TOPIC_MESSAGE', ['user_id' => 1]);
本作品採用《CC 協議》,轉載必須註明作者和本文連結
Persevere,Vtr!

相關文章