來聊聊react-native應用的健康監控

jonnyF發表於2018-04-06

監控什麼

今天我們來聊聊如何監控你的應用程式,這裡的監控說的不是讓我們去監控使用者,而是監控應用的健康狀態,什麼是健康狀態呢?對於後端的同學來說,在微服務的架構下,每個子服務是否正常工作、返回的結果是否滿足預期,這些就算是健康狀態,再舉個例子,你的桌上型電腦,對於作業系統來說,每個硬體是否能正常的工作、工作的穩定性,這些都是需要關注的健康狀態。

既然我們關心健康狀態,那麼我們該如何衡量一個“裝置”的健康狀態呢?對於上面的例子,CPU執行的溫度、硬碟讀取的速度、子服務執行的效率,這些都可以作為健康狀態的參考標準。而對於我們前端來說,一個服務的響應速度、某個頁面渲染的時間、外接裝置是否正常執行、以及正常執行的時間比,這些都可以作為我們衡量一個“裝置”是否健康的標準。

上面說的了要監控什麼指標,那這些指標具體的例項又是什麼呢?由於我主要做react-native應用的開發,我今天就基於react-native來討論一下這件事,而對於傳統的web,相對來說就簡單一些了,但具體的思路不會差太多。

在我遇到的實際場景中,我的應用程式常常需要連結多個外接裝置,例如:鍵盤、掃碼槍、各種個感應器,所以我需要時刻關注這些裝置的健康狀態,一旦發現某個裝置不能正常工作或者在未來的某個時刻不能正常工作,就需要馬上反饋出來,而這只是一部分,這些物理裝置有著很明確的“指標”。

另一方面,諸如網路狀態、電池電量,這些應用內的“指標”也需要我時刻的關注,什麼時候處於弱網環境、什麼時候出現低電量等各種各樣的異常情況都會讓我們的應用程式變得不健康。所以,我們的目標就是圍繞著這兩塊展開監控,那麼接下來我 們說說該從什麼地方下手。

生命週期

將所有想要監控的服務收集到一起,作為一個總控制,然後在總控中對各個伺服器的各個生命週期埋點。

1、主動式:手動的從各個生命週期中hook想要的資料,然後通過計算,收集上報。

2、被動式: 在各個生命週期中埋點,等待某一類事件的觸發。

可是這麼多裝置,如果我們一個個的去監控、去適配,那就和給windows系統的硬體寫驅動一樣繁雜了,這對於我們前端開發來說工作量實在是太大了,所以為了方便我們進行統一的管理,和複用統一的程式碼,我們需要一個“模式”去規範和統一我們的裝置。

現在我們用一個統一的class去集中監控我們的裝置,另外一個問題就是眾多的裝置,無論是“物理”的裝置還是“虛擬”的裝置,如果我們專門為每一種去寫監控程式碼,工作量實在是太大了,所以我們可以讓這些裝置在上層表現的“一致”,為此,我們引入“生命週期”這個概念,在一個裝置啟動、執行、暫停、解除安裝的各個階段,我們都可以進行監控,無論是掃碼器,還是網路請求的發起,各種各樣的形態都逃不過這個步驟,所以,只要能在這個上面做好文章,那麼監控各種資料就易如反掌了。

以下我會從兩個方面來介紹一下我總結的“基礎”的監控項,個人認為,無論你的專案為了應對什麼樣的場景,下面的這些例子基本都會是“必備”的選項了,即便你的專案比我的專案更加精簡,但是下面介紹的思路也會是不錯的參考。

主動式監控

上面說了這麼多,那麼我們來具體的看看需要監控些什麼呢?

在一個專案剛上線的時候,存在著很多隱藏的問題,所以我們需要更多的日誌去監控應用程式是否正常的執行,以及是否按照我們自身設定的路線去執行,一旦專案穩定,一些模組或邏輯被證實是沒有問題的了,那麼相應的我們也要去移除一些埋點日誌,另外一方面,在開始上線的時候由於使用者量少,一旦出現線上bug,由於缺少案例,導致定位和分析的難度都會增大,所以,這個時候我們就需要主動的埋一些監控點,來應對這種情況的發生,在出現緊急bug的時候我們可以通過日誌點去推測和演算使用者的行為,以及當時的情況。

但是,說到底這些監控都是臨時的,不一個長期的監控,所以我的原則也是儘可能的不去侵入到業務程式碼中,無論是通過切面程式設計還是高階元件封裝,都要保證這些監控程式碼可以通過一個或一組規則快速的關閉統計,解放算力。

因此,凡是主動監控都要具備自動判斷和動態策略這兩個特點,可以動態的感知到當前的狀態,從而做出對主業務影響最小的行為,即便監控再重要,也不能因為監控的行為而影響業務程式的工作,這隻能是一個錦上添花行為。

被動式監控

為什麼要有被動式的監控呢?首先,有些監控的相對“獨立”的,每一次的監控點並不會100%的觸發,每次的觸發並不存在上下文或者前後的必然聯絡。也就是說,這些點的觸發都是單獨存在的,所以我們不需要對其進行諸如計算、格式化等分析操作,也不需要儲存它的上下文,比如開始、結束時間。

相對於上面介紹的主動式監控,被動式監控的程式碼和邏輯都會長期的執行,因為在專案的中後期,在移除了大多數異常、效能監控後,這些僅存的程式碼就成了我們排查問題的關鍵所在了。因此,這些監控要保證自身的穩定,以及所積累的資訊準確、及時,誰都不希望看到奔潰或者邏輯錯誤的警報在發生之後很久才上報出來。

那麼有哪些需要我們做被動式的監控呢?在我的專案中,一個外接裝置是否線上,使用者點選鍵盤上某個按鈕等行為就是一個典型的被動式監控,我不知道使用者什麼時候去點鍵盤,我也不知道我的外接裝置什麼時候斷開,我只是需要捕獲到這個行為的發生就可以了。

而且對於這些監控點,我們實際上是不需要開發自己去寫程式碼的,它應該存在於依賴的包中,這就避免了我多次寫重複程式碼的問題,舉個例子,我在A專案中有個對掃碼器掃描到內容的監控,而在B、C專案中我仍舊需要這個功能,那麼我就可以不再反覆的去寫這塊的日誌程式碼了,因為它應該存在於這個掃碼器的包中,我要做的,只是提供一個logger方法而已,返回的格式、內容都不需要我去關心。

而這些通過埋點、使用者輸入、事件回撥等方式收集上來的日誌,我們都要如實的上報到遠端伺服器(這裡和主動式監控有所區別,主動監控的內容可以允許我們自己計算、統計),因為這些都是真正的異常,以後會影響我們debug的日誌要最大程度的保留現場。

客戶端流程圖

來聊聊react-native應用的健康監控

服務端

上面說了那麼多,都是基於客戶端去做的,現在我們在客戶端已經準備好了想要的資料,那麼我們該如何去使用他們呢?

對於傳送上來的日誌,我可以做如下三件事:

  • 監控24小時線上狀態
  • 異常指標的快速報警
  • 視覺化的展示監控資訊

下面我們就圍繞這三點來設計我們的服務端系統。

對於第一點,我們可以通過與客戶端的心跳包來檢查客戶端是否存活,因為在業務場景中,我們的應用為react-native的,所以在檢測心跳的時候就要區分是native模組還是js的業務模組了,同時很多場景下存在無人使用時js業務不會上傳日誌以及在行動網路下業務精簡日誌的情況,因此我們的24小時監控服務就要足夠靈活、多變。所以在報警的時候可以通過讀取配置的方式在伺服器不重啟的情況下動態的變換監控規則。

例如什麼專案需要監控native存活,什麼專案需要監控js環境存活,什麼時候將報警通知給何人,並且可以通知到是什麼地方的什麼裝置出了什麼故障,這些都是基礎功能。

而第二點,需要我們做的除了包含第一點之外的功能外,還有一個異常記錄的功能,因為第二點的異常具有偶發性,並且相對於心跳包來說,日誌內容更加豐富,因此我們可以針對這些日誌做更多的事情,但首先就是將這些日誌分類的儲存起來。同時,在第二點中存在不同的應用對不同的異常有不同的定義的情況,比如說A應用認為資料初始化的介面超時就屬於異常,而B應用則認為即便超時也不影響,那麼就需要為每個應用單獨配置異常指標,從而做到分專案、分閾值的功能。

還有一些額外的擴充,由於上面做的分專案、分閾值處理,就勢必存在一些通用異常,那麼每個專案就應該具有繼承公共異常的功能,以及一些模板異常的設定。這些都是這個服務所需要的功能。

最後一點,在蒐集到異常以及通知到相關負責人之後,這次異常報警就算結束了嘛?當然沒有這麼簡單了,我們可以基於web服務來檢視一些日誌的基本情況,例如什麼專案報警最多,什麼地方報警最多,什麼時間報警最多等等圖表提供我們檢視和分析。更有甚者我們可以根據不同的報警級別給不同的人傳送不同的報警內容。

最後是這個服務端自身的健壯性,由於我們的服務是基於nodejs來做的,因此通過pm2,我可以很方便的在程式掛掉之後馬上恢復服務,同時為了服務相互解耦和最大化cpu效率,通過pm2啟動指令碼來將24小時離線服務、異常指標報警、日誌分析展示服務相互獨立開,併為可以啟動多執行緒的服務提供cluster模式的支援。並且將共享的資料通過redis快取,配置通過資料庫持久化等方式來備份和保證服務的健壯與高效。

服務端流程圖

來聊聊react-native應用的健康監控

總結

至此,一個由客戶端+服務端的健康監控系統初步完成。它們這些服務看似相互依賴,但又相互解耦,不會造成一個環節失效導致整個系統奔潰,同時又可以做到對正常業務最小化的侵入。

當然,這些都只是一個雛形,一個全部由js去完成的專案,相對於大公司那些完整而又系統的監控來說,僅僅只能作為開發業務的我們自查的一個工具。雖然有這些系統來保證我們的專案正常、健康的執行,但是更重要的是我們開發者自己程式碼的健壯和穩定。工具做的再好,也不能替代我們自己寫的優異的程式碼。

相關文章