基於MQTT的實時日誌系統

  • 2019 年 12 月 4 日
  • 筆記

MQTT是一個物聯網傳輸協議,它被設計用於輕量級的發佈/訂閱式消息傳輸,旨在為低帶寬和不穩定的網絡環境中的物聯網設備提供可靠的網絡服務。MQTT是專門針對物聯網開發的輕量級傳輸協議。MQTT協議針對低帶寬網絡,低計算能力的設備,做了特殊的優化,使得其能適應各種物聯網應用場景。

而我卻拿MQTT來做實時日誌系統。

主要基於這幾個點,輕量,實現簡單,支持QoS,支持TLS,是個發佈/訂閱協議,支持消息推送。

其實公司日誌系統那麼多,像ULS,nlog什麼的,為什麼又又又要造輪子呀?因為要麼接入麻煩,要麼語言不支持,要麼需要資源,要麼支持不到位,要麼丟消息等。而我的需求背景是,有幾台服務器,目前的日誌是打印到本地文件中,每次要查日誌都要登錄運營機器,而且還要登錄兩台,不大方便。因此,我想要的就是,只在一處地方統一實時查看所有日誌,而且對現有系統最小的改動。

而了解到MQTT也實在是偶然,在一個ruff的群里有人基於MQTT對物聯設備做實時控制,而線上的機器完全也可以當作一個物聯設備,我用MQTT來做遠程控制,控制內容就是把寫到文件的日誌直接轉發到中心服務器就可以了。架構圖長這樣子:

中心服務器是一個MQTT服務器,而其它機器都其實是個MQTT客戶端。控制端發佈訂閱消息,要求收集日誌,通過中心服務器轉發到了日誌源機器;日誌源機器接收到收集命令,一打文件日誌就直接發佈消息,由中心服務器轉發。這個過程其實是個多對多的廣播過程,容易出現的問題就是消息重複,當然這個可以通過一些唯一標識來消除,另外,再設定QoS(0,1,2,至少選1),可以保證不丟消息,然而時序並不能保證,所以發佈每條日誌的時候需要把時間戳也帶上,在必要的時候可作為時序參考依據。

那這個就是基本原理,MQTT協議的使用保證了數據傳輸的可靠性,而在這個基礎上,我們也可以有進一步的發揮,定製更多的命令來對各個日誌源機器進行控制。日誌機器可以進行橫向擴展,在MQTT服務器眼中就只是一些物聯設備,中心服務器也可以按照協議進行橋接,而控制端也可以是各種有MQTT實現的終端和web頁面(websocket實現的長連接)。

在這個大而虛的解決思路指導下,我們的業務需求是這樣子實現的。

MQTT服務器採用mosquitto,客戶端採用mqtt.js。這樣子的選擇原因,作為前端開發首選語言自然是javascript,不僅支持服務器開發,也適用瀏覽器端開發,遺憾的是mosca.js作為一個服務器對QoS的實現不到位,所以選擇另一個實現即mosquitto,當然其它選擇也不會差到哪裡去。

mosquitto的事情主要是配置,支持websocket需要指定配置:

listener 2883 0.0.0.0  protocol websockets

需要注意的問題是需要編譯安裝libsockets來支持websockets。

日誌源和控制端需要分別進行開發,互相派發和執行命令。

因為需要打印日誌的已有系統有五個之多,考慮到自己維護的成本,能夠增加一個旁路不動聲色地把本地日誌給傳輸出去是最理想的。想到對文件修改的監聽,linux系統有inotify接口,而node.js也有個watchFile的封裝,而且很好用,簡直完美~~用node.js實現一個tail -f 的功能就好了,而tail的結果實時傳輸給訂閱者,訂閱者再按序進行拼接就大功告成了。

然而~~現實畢竟是現實,全量日誌一天會有個幾G吧,雖然硬盤也不是很值錢,但是想着實際在排查問題看日誌的時候,只會查看指定用戶,不會看這讓人頭暈的流水,所以,在已有系統的代碼里植入一個函數,只傳輸指定用戶(比如說appid)的日誌,即實現了染色日誌的功能。

至此,核心功能完成。

邊邊角角,卻還是有很多細節,比如說在瀏覽器端實現控制,MQTT是否能傳輸存量的大文件?

傳輸大文件簡單的測試就掛了,貌似是mqtt.js的bug,然而暫時無法深究,直接還是用傳統的http搞定。

最後的總結,這個系統鄙人命之為 fishing,把日誌像釣魚一樣給釣上來。不同於ULS,並不用預先申請存儲資源,是充分利用了機器的閑置存儲資源,而且實時展示,對於排查指定用戶問題迅速有效。