前端體驗優化(3)——後端

  前端很多時候是不會接觸到後端的工作,不過我們公司由於歷史原因,維護了大量的 Node.js 服務。

  所以也積累了一些後端優化的經驗,主要分兩塊 Node.js 和數據庫。

一、Node.js

  Node.js 的監控沒有從 0 開始,業務邏輯的日誌直接記錄在阿里雲中,性能監控部署的是阿里雲提供的系統。

  業務邏輯包括自定義的埋點,以及數據庫的增刪改查操作,還有內部接口訪問的日誌信息等。

  在性能監控中,可以導出內存快照,方便排查內存泄漏的問題。

1)服務

  Node.js 提供了定時任務、SSR、壓縮圖像、消息隊列等服務。

  定時任務可以將一些比較費時的計算按指定的間隔執行,以免直接訪問造成接口的慢響應。

  SSR 就是服務端渲染,是一種常見的頁面優化手段,qwik 是一款語法接近 React 的前端 SSR 框架。

  其目標是延遲加載所有的代碼,例如一個按鈕在沒有點擊它之前,就不會去加載點擊的邏輯,甚至不會去加載 React 相關的代碼。

  理念很好,但是 qwik 還不夠穩定,應該會有比較多的坑。

  消息隊列是一種限流削峯的有效手段,一般由於瞬時訪問量過大,導致流量暴增,系統無法處理請求甚至發生崩潰。

  而在加入消息隊列後,系統可以從消息隊列中取數據,相當於消息隊列做了一次緩衝,極大地減少了業務處理的壓力。

2)接口

  在業務迭代和人員流動的過程中,就會出現一些冗餘接口,而這些接口的訪問量還可能比較大。

  在對業務進行分析後,可以將其合併到另一個接口中,或者就是將接口響應直接寫到前端腳本中,直接減少一次通信。

  比較影響接口查詢時間的是與數據庫的交互,可以減少交互次數,例如增加一層緩存,無必要就不去查詢。

  或者要查詢多條記錄時,用一條 SQL 語句完成,可以在 SQL 使用 in 語法,例如 select * from user where id in (1,2,3)。

  對於提交到服務器的數據,需要進行安全測試,即排除潛在隱患,防止刷接口,例如增加身份驗證。

  當代碼出現異常時(例如調的內部接口沒有響應),得避免接口奔潰,一直加載中,可以給到統一的響應。

  在統一響應的 JSON 格式後(例如 { code:0, data: {}, msg: "xxx"}),就可以比較方便的統計接口異常和規範前端代碼。

  規定 code 爲 0 是正常響應,非 0 爲異常後,就能統計各種異常響應的數量以及佔比,進行鍼對性的優化。

  諸如監控、埋點等非業務且量比較大的接口,推薦單獨分離出來(可根據請求路徑進行服務轉發),以免出現錯誤時,影響線上業務,

3)版本升級

  公司之前所有的 Node 項目,其環境都是 8.9.4 版本,發佈於 2018 年的一個比較古老的版本。

  老版本有兩個比較明顯的問題:

  1. Node 高版本的特性和方法都無法使用。
  2. 有些第三方新版本的包無法安裝和升級,該包可能依賴比較高的 Node 版本。

  之前在開發項目時就遇到第三方包自身的問題,必須升級或換個包才能解決,但因爲 Node 版本的原因,無法替換,只能用其他方式來修補漏洞。

  2022 年的 7 月份,纔有機會將版本升級到 16.15,總共有 4 個 Node 環境需要升級。

  在 4 個待升級的項目中,有 3 個是對外的項目,有 1 個是對內的項目。

  那麼先升級對內項目的 Node 版本,這樣有兩個好處:

  1. 即使出問題了,影響範圍也能最小。
  2. 響應也能最及時,因爲有問題的話,在公司內部能馬上反饋到我們組。

  我們每個項目都會有 3 套運行環境:測試、預發和生產。

  首先將測試環境升級,測試環境都是開發人員使用,影響最小,反饋最快。

  觀察一段時間後,再升級預發,預發環境與生產環境最爲接近,數據庫採用的也是一套。

  最後纔是生產,給真實用戶使用,再獲取反饋。從開始升級到全部項目升級完成,前前後後操作了 20 多天。

4)框架

  Node.js 的框架衆多,公司使用的是 KOA2,它非常輕量,諸如路由、模板等功能默認都不提供,需要自己引入相關的中間件。

  洋蔥模型的中間件非常強大,我們將異常響應、身份驗證、發送站內信等各類操作都交由中間件處理。

  各類框架都有其自身的特點和適用場景,選擇最合適自己團隊的框架纔是正解。

  在公司的倉庫中,發現之前有用基於 next.js 建立的一個 SSR 項目,不過後面沒人維護,再之後就被運維釋放掉了。

  如果選擇了某一框架,那還是要儘量維護起來,投入到實際生產中,發揮其作用。

二、數據庫

  數據庫被分爲關係型和非關係型數據庫,公司用的 MySQL 就屬於前者,而 Redis、MongoDB、ElasticSearch 等數據庫就屬於後者。

  線上數據庫也需要有個監控平臺運行着,瞭解數據庫的 CPU,定位慢查詢等。

1)MySQL

  在某張表的數據量上去後,性能瓶頸就會浮現出來。

  常見的優化包括創建索引、縮小查詢範圍等,利用執行計劃,可以觀察索引創建前後影響的行數。

  每次索引創建後,都能提高几百、甚至幾萬倍的查詢速度,當然,創建索引也是有講究的,也不是隨便創建的。

  之前就遇到創建太長的索引,被拒絕了。

  另一種優化手段是將數據歸檔,就是將比較舊或不用的數據遷移至別處,另一個數據庫或直接下載到本地。

  當然,將數據存儲在雲服務中,是會產生經濟成本的,所以還有種辦法就是直接刪除,這類通常是冗餘數據,不會用到。

  對於非業務的數據表,也是像接口那樣需要分離,以免出現容量上限、慢查詢等問題而拖垮線上服務。

2)MongoDB

  自研的前端監控系統中的表字段有很多是 JSON 格式,這類數據其實很適合存儲在 MongoDB 中。

  因爲 MySQL 需要先將對象進行字符串序列化,然後再存儲,而此時若需要以對象中的某個屬性作爲查詢條件,MySQL 就難以實現了。

  不過,最終並沒有將數據存儲在 MongoDB 中,因爲雲服務只提供了 MySQL 的可視化操作界面和監控平臺。

  爲了便於自己排查異常,還是選擇了 MySQL 作爲存儲媒介,但公司有許多遺留業務都存儲在 MongoDB 中,日常還需要維護。

  所以後期自研了 MongoDB 可視化查詢工具,在後臺可以查詢線上的 MongoDB,省得每次都登錄服務器查看。

3)ElasticSearch

  當需要全文檢索一張千萬級的表時,在 MySQL 中實現就比較困難,很容易將數據庫掛起,並且在 5.7.6 之前的 MySQL 還不支持中文檢索。

  此時可用 ElasticSearch 替代,這是一款基於 Lucene 的分佈式、可擴展、RESTful 風格的全文檢索和數據分析引擎,擅長實時處理 PB 級別的數據。

  前端監控數據每日會增加 70W 條記錄,在 ES 中用關鍵字檢索這批數據會非常快。

  若數據中有比較大的字段,那麼生成的倒排索引也會比較巨大,當時我存儲了接口的響應,結果整個容量直接膨脹了 3 倍。

  若要聯立好幾張表的 MySQL 數據,那麼也比較適合存到 ES 中。

  例如用戶發佈的日誌,分成了 100 張表存儲在 MySQL 中,現在需要關鍵字檢索,就可以將這 100 張表合併到 ES 中。

4)Redis

  Redis 是一種支持 key-value 等多種數據結構的存儲系統,基於內存,可持久化,用於緩存、消息隊列、集羣等場景。

  Redis 之所以快,一方面是因爲大部分操作在內存中完成,以及採用高效的數據結構,例如哈希表、跳錶等。

  另一方面,就是 Redis 採用了多路複用機制,使其在網絡 IO 操作中能併發處理大量的客戶端請求,實現高吞吐率。

  而 Redis 的併發處理能力(每秒處理請求數)能達到萬級別,甚至更高。

  前面也講到過,在接口中增加一層緩存,可加速接口響應,這也是緩存的常規用途,但是要注意,當緩存被穿透時,數據要能自動恢復。

  也就是說,緩存不保障數據的一致性,不能因爲緩存的異常,而影響線上業務,將錯誤信息存儲到數據庫中。

  適當的將小部分特定的數據提前存入到 Redis 中,可起到預熱的作用,增加體驗。

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