隨著幫助台應用程式規模不斷擴大,單體架構的維護難度和擴充套件性瓶頸日益凸顯。本文旨在探討如何將現有的單體幫助台應用程式逐步遷移至微服務架構,以提升系統的彈性、可維護性和可擴充套件性。遷移過程涉及多個方面,包括商品目錄、票務系統和搜尋系統等核心模組的拆分,以及資料函式庫切割、API Gateway 和 Service Mesh 的引入。同時,本文也探討了身份驗證與授權機制、批次任務的優先順序轉換演算法、分頁機制設計等關鍵技術細節。根據 JWT 和 RBAC 的身份驗證與授權流程設計,有效保障了系統的安全性,並能根據不同角色的許可權進行細粒度的存取控制。批次任務優先順序轉換演算法的應用,則能有效提升系統資源利用率,確保關鍵任務的優先執行。此外,分頁機制的匯入,有效解決了大量資料顯示的效能瓶頸,提升了使用者經驗。
案例研究:單體幫助台應用程式轉移至微服務架構
應用概述
幫助台應用程式是一款旨在協助客戶解決技術問題的一體化解決方案。該應用程式涵蓋了多種功能模組,如票務管理、產品目錄、預約管理、訊息板以及搜尋功能等。
應用架構設計
幫助台應用程式採用單體架構設計模式。這意味著所有功能模組都在同一個程式碼函式庫中開發和佈署。雖然單體架構在初期開發階段具有較低的複雜度和較快速度上線能力優勢,但隨著應用程式規模擴大且需求增多後期維護難度顯著上升。
身份驗證與授權流程設計考量
身份驗證與授權流程設計對於任何應用都至關重要。幫助台應用採取了多層次安全措施以保護敏感資料和操作行為安全性,
身份驗證流程及演算法選擇考量
身份驗證流程包括了登入驗證以及OAuth2授權兩大主要部分, 玄貓選擇了JWT(JSON Web Token)作為身份驗證機制, 其優勢包括可擴充套件性高以及易於實作跨網域名稱請求, 從而實作安全且無縫地完成客戶端與伺服端之間身份驗證。 此外也避免了每次請求都傳送密碼或其他敏感資訊帶來風險。 由於JWT本身不儲存任何客戶端資訊, 因此無法直接從JWT中反向推匯出原始資料, 從而進一步提高了整體安全性。 同時我們也考慮到JWT本身可能存在篡改風險, 因此在生成時會加上時間戳並在伺服端進行驗證, 以確保每次請求都是合法且未被篡改過。 我們選擇HMAC SHA-256作為簽名演算法, 其優點在於兼顧安全性與效率, 確保即使攻擊者擁有簽名但無法逆向推匯出原始資料, 從而進一步增強安全防護層次。 此外我們還會根據不同客戶端需求設定不同有效時間(expire time),以確保每次登入都是最新且有效期限內完成認證,
授權流程及許可權細粒度設計考量
授權流程則涉及到不同角色之間許可權管理與存取控制, 玄貓選擇RBAC(Role-Based Access Control)模型作為授權框架, 其優勢包括便於管理且易於擴充套件, 因為RBAC根據角色層次結構實作細粒度存取控制, 因此當業務需求變動時只需更新角色定義而非逐項修改每個客戶端許可權設定。 因此RBAC允許我們根據組織結構或業務需求定義不同角色, 例如管理員、客服人員及普通使用者等。 針對每個角色我們會設計對應存取許可權, 例如管理員擁有最高許可權且能夠操作所有功能模組, 而普通客戶則僅限於基本操作。 此外我也會針對敏感操作設定額外防護措施, 例如多因素驗證(MFA)或者操作記錄稽核, 以確保即使許可權被濫用也能夠及時發現並採取補救措施。 此外我們還會針對高風險操作設定單獨審批流程, 例如敏感資料修改或高金額交易,以增加防範風險可能性, 從而提升整體安全性,確保業務營運不受影響,
註冊功能設計與流程描述解說:
註冊流程中包含了簡單表單校驗與資料函式庫檢查兩大部分, 首先玄貓會檢查輸入資料格式正確性,如電子郵件是否符合規範格式, 接著檢查資料函式庫中是否已存在相同電子郵件帳號,避免重複註冊情況發生. 如果註冊成功則會傳送歡迎電子郵件,並附上啟用連結以完成最終驗證. 以便確保該帳號由真實人士所建立而不是自動化指令碼生成, 如果啟用成功則表示註冊完成,反之則需要重新註冊.
登入功能設計與流程描述解說:
登入流程則涵蓋了三大部分——表單校驗、密碼加密以及session管理, 首先玄貓會檢查輸入資料格式正確性,如電子郵件或手機號碼是否符合規範格式. 接著會進行密碼加密處理,防止明文密碼直接儲存,避免被窺探風險. 當驗證成功後會建立session進行狀態追蹤, 並且設定過期時間以提升安全性防止長時間未操作後仍保持登入狀態. 此外玄貓還會針對頻繁失敗登入嘗試進行IP封鎖措施, 以防止暴力破解攻擊,確保系統安全.
忘記密碼重置功能設計與流程描述解說:
忘記密碼重置流程中包含了電子郵件校驗與重置連結兩大部分, 首先玄貓會檢查輸入電子郵件地址格式正確性及是否存在資料函式庫中. 如果存在則會生成重置連結並傳送至該電子郵件地址, 重置連結內含唯一token以及過期時間,避免被重複利用風險. 當點選重置連結時會進入密碼修改頁面,要求輸入新密碼並進行加密儲存處理. 最後更新資料函式庫紀錄完成重置操作.
######## 構建應用及佈署介紹:
Eclipse IDE 工具介紹:
Eclipse 是一款廣泛使用的 Java IDE 工具 ,提供豐富外掛支援強大除錯能力 , 在本次專案中 Eclispe 處理框架程式碼 , Eclipse 主要負責 Spring Boot 框架程式碼處理 , Java SE SDK 語言 , Maven 組態依賴項 , Eclipse 本身既有 GUI 特徵也是 Java 語言 , Eclipse IDE 主要包括桌面版本和 Web版本 , 桌面版本本身非常方便 , GUI 特徵非常適合開始學習 Java 語言 , Eclipse IDE 在桌面版本下主要包括 GUI 輔助除錯程式碼 , Maven 支援第三方函式庫 , 主要負責 Maven 的依賴項 , Eclipse IDE 構建 Spring Boot 框架程式碼 , 包括 Maven 組態 , Tomcat 組態 , Eclipse IDE Web版本主要負責 GitHub 上傳程式碼 , 自動編譯封裝 , GitHub 在 Eclispe 上安裝 GitHub 外掛 , Eclipse IDE 負責 GitHub 上傳程式碼 , 自動編譯封裝 , Eclipse IDE 在 Windows 主要負責封裝 JAR 列舉包 , Eclipse IDE 安裝 Maven 外掛 ,主要負責 Maven 組態依賴項 , Eclipse IDE 主要負責 Maven 組態依賴項 , Maven 支援第三方函式庫 .
列舉包介紹:
列舉包主要負責 Java SE SDK 中包含一些常見方法 , 列舉包固定值欄位包含常見常數 : 列舉包固定值欄位常見常數 : public static final String FOO = “FOO”; public static final String BAR = “BAR”; public static final String BAZ = “BAZ”; public static final String QUUX = “QUUX”;
列舉包主要負責 Java SE SDK 中包含一些常見方法 , Java SE SDK 中列舉包類別似 Python 語言中的 Dict . dict 在 Python 語言中表示字典 . dict 在 Python 語言中表示字典 .
微服務轉移計劃及執行方法介紹:
隨著企業業務不斷增長與變化 , 單體應用逐漸暴露出維護困難 、樣式不靈活 、擴充套件力差等缺陷 , 因此企業逐漸轉向微服務架構 , 微服務架構以小而獨立服務為基本單元 , 各服務之間透過 HTTP 或訊息佇列互相通訊 . 微服務架構解耦程度更高 , 各服務之間獨立開發維護 , 增強了靈活性與擴充套件力 . 玄貓在此次專案中負責幫助企業完成單體幫助台軟體轉移至微服務架構 . 具體步驟如下 :
第 一步 : 分析現有單體應用系統結構 :
單體應用系統通常將所有業務邏輯封裝成一個單獨應用執行 . 這種方式初期開發簡單快捷 , 不過隨著業務複雜度增加 , 增加維護困難與擴充套件難題 . 需要對現有系統進行詳細調查與設計評估 , 分析各個業務模組間關聯關係 .
第二步 : 模組拆分 :
根據業務需求與現狀調查結果將單體應用拆分為獨立微服務 . 此過程需要細緻識別各個業務模組邊界與依賴關係 . 確保拆分後各個微服務獨立開發與維護同時還滿足業務需求 .
第三步 : 資料函式庫拆分 :
單體應用通常會有一個巨大資料函式庫同時滿足所有業務邏輯讀寫要求 . 這會導致資料函式庫成為瓶頸影響整體系統效能與擴充套件能力 . 透過資料函式庫垂直拆分將巨大資料函式庫切割為多個較小獨立資料函式庫 . 每個微服務對應一個資料函式庫 . 同時透過資料函式庫水平拆分配對某些關鍵表進行切割增強處理效能 .
第四步 : 引入API Gateway :
為了簡化與協調各個微服務間互動關係引入API Gateway作為統一入口 . API Gateway負責請求轉發與負載平衡 . 提供統一認證與授權機制確保各個微服務間安全互動 .
第五步 : 引入Service Mesh :
隨著微服務數量不斷增加 . 各個微服務間網路通訊變得複雜 . 需要引入Service Mesh網路對網路通訊進行精細化管理與監控 . Service Mesh透過sidecar代理模式對各個微服務進行增強 . 提供負載平衡 . 流量管控 . 安全防護等能力 .
商品目錄微服務轉移:
第一步 : 商品目錄獨立開發 :
將商品目錄相關業務邏輯從單體應用中抽離出來獨立開發成一個新微服務 , 此過程中需要認真設計該微服務 API 介面確保與其他相關業務邏輯協同工作 .
第二步 : 資料函式庫切割 :
將原有單體應用中的商品目錄相關表從主資料函式庫中切割出來獨立成一個新資料函式庫 . 每個表獨立名稱空間 .
第三步 : 分散式事務處理 :
由於現階段商品目錄還會與其他業務模組有互動關係 . 需要引入CQRS設計模式來處理跨微服務事務情況 .
機票系統微服務轉移:
第一步 : 摧毀權威態度 :
將票據系統相關業務邏輯從單體應用中抽離出來獨立開發成一個新微服務 , 此過程中需要認真設計該微服務 API 介面確保與其他相關業務邏輯協同工作.
第二步 : 資料函式庫切割 :
將原有單體應用中的票據系統相關表從主資料函式庫中切割出來獨立成一個新資料函式庫 . 每張表獨立名稱空間.
第三步 : 分散式事物處理 :
由於目前階段票據系統仍然需要與其他業務模組互動 . 需要引入 CQRS 設計模式來處理跨微服務事物情況 .
搜尋系統微服務轉移:
第一步 : 比對引領 :
將搜尋系統相關業務邏輯從單體應用中抽離出來獨立開發成新的一個微服務. 此過程中需要認真設定該 micro service api 介面確保其它業務邏輯協同工作.
第二步 : 資料函式庫切割 :
將原有單體應用中的搜尋系統相關表從主資料函式庫中切割出來獨立成為一新資料函式庫. 每張表獨立名稱空間.
第三步 : 分散式事物處理 :
由於目前階段票據系統仍然需要與其他業務模組互動 . 需要引入 CQRS 設計模式來處理跨 microservice 事物情況 .
架構圖示:
以下為幫助台應用從單體架構轉換為微服務架構後對應元件關係圖表 :
graph TD; A[幫助台前端] -->|API呼叫| B[API閘道器]; B --> C[商品目錄微服務]; B --> D[票據系統微服務]; B --> E[搜尋系統微服務]; subgraph 商品目錄微服務 { C --> F[商品目錄資料函式庫]; } subgraph 票據系統微服務 { D --> G[票據系統資料函式庫]; } subgraph 搜尋系統微服務 { E --> H[搜尋系統資料函式庫]; }
跑批稽核併發任務優先順序轉換演算法概述:
在幫助台應用專案過程中有時會遇到一些批處理任務處理情況 比如說批處理任務壓力過載時我們需要一種穩定確定任務優先順序進行高效分配時間段處理來實作資源最大程度利益 。我們主要針對批處理任務進行稽核併發優先順序轉換演算法概述來實作這個目標 。
執行緒池:執行緒池是一種資源複用機制 ,透過預先建立一定數量執行緒放入執行緒池裡 ,當任意時刻只允許最多 N 個執行緒同時存在 。執行緒池會根據當前任務數量動態調整執行緒數來最大限度利益CPU資源 。
任務佇列:採取先進先出 FIFO 原則實作公平排程機制來分配任務優先順序 ,當執行緒池空閒時直接啟動對應線城並開始執行任務 。如果無空閒線城則將任伍放進佇列裡等待線城空閒時再執行 。 公平排程原則也就意味著後進佇列裡必須按先進先出FIFO原則進行分配 。但是如果想實作按優先順序別區別對待情況我們就應該採取優先順序佇列來實作這種分配 。根據任職型別不同劃分不同優先順序別 。
時間輪 :時間輪也是一種稽核併發任務優先順序轉換演算法 常見於網路程式設計比如說負責網路IO事件輪詢時 ,這時就得到了時間輪這個概念 ,它會根據時間間隔來排程線城執行任職 。透過設定時間間隔長短我們就可以實作按固定時間段來分配CPU資源 。
延遲佇列 :也叫延時佇列就是一種將訊息投放到延時佇列裡 ,當達到設定時間的閾值後才會執行其中內容 。這是一種典型場景比如說放棄購買成功之後又沒付款退款處理延遲5分鐘 。 這種場景就得到了延時佇列這個概念 他透過記錄每個訊息投放時間點然後比較當前時間點來決定消費何時執行 。
5.RPC :RPC 全稱 Remote Procedure Call 中文叫做遠過程除錯它主要負責兩台裝置之間RPC通訊除錯處理 用於遠距離無狀態通訊 , RPC負責遠距離無狀態網路呼叫通訊 。RPC呼叫一般都是透過遠距離無狀態網路呼叫實作 。
此圖示展示幫助台應用從單體架構轉換為微傳承架構後對應元件關係圖
此圖示代表幫助台應運專案從傳統幫助台結構轉變為現代幫助台結構後對於四種結構之間主題替換內容之關係說明圖
分頁機制設計(Pageable)概述:
隨著幫助台專案規模逐漸變大 ,內容越來越豐富的時候我們勢必就會遇到內容過多時候顯示內容時候就會遇到頁面載入慢或者內容顯示過長問題 。 為了最佳化這類別問題我們一般會採取一種叫做頁面機制 (Paging) 的設計方式 。 頁面機制 主要就是為了減少一次請求傳回內容過多時候導致頁面顯示過長影響UI顯示的時候故意設計這種機制讓每一次傳回結果顯示一定數目的結果頁數 。 通常我們叫做頁碼 (pageNumber) 和頁碼數 (pageSize) 每次請求帶上這兩個引數告訴後台傳回第幾頁以及第幾頁顯示幾條內容 。
Pageable pageable = PageRequest.of(pageNumber -1 ,pageSize);
Page<Content> contentList = repository.findAll(pageable);
其中 repository.findAll() 是假設我們有一個Content型別內容頻道 。 PageRequest.of(pageNumber -1 ,pageSize) 生成一個PageRequest 物件 用於傳遞給 repository.findAll()函式 。 repository.findAll() 函式就會傳回第幾頁顯示幾條內容給前端頁面 。 其中 pageNumber 是頁碼代表傳回第幾頁 pageSize代表傳回每頁顯示幾條內容 。 這樣我們就利好了Pageable引數來進行Paging Machine設計然後達到減少一次請求傳回內容太長影響UI顯示問題 。