最新一直在跟著 毛劍 大佬去學習 GO 的一些知識點 做了自己的筆記
你可以把微服務想成是 SOA 你可以把微服務想成是 SOA 的一種實踐的一種實踐
- 小即是美:小的服務程式碼少,bug 也少,易測試,易維護,也更容易不斷迭代完善的精緻進而美妙。
- 單一職責:一個服務也只需要做好一件事,專注才能做好。
- 儘可能早地建立原型:儘可能早的提供服務 API,建立服務契約,達成服務間溝通的一致性約定,至於實現和完善可以慢慢再做。
- 可移植性比效率更重要:服務間的輕量級互動協議在效率和可移植性二者間,首要依然考慮相容性和移植性。
微服務定義
圍繞業務功能構建的,服務關注單一業務,服務間採用輕量級的通訊機制,可以全自動獨立部署,可以使用不同的程式語言和資料儲存技術。微服務架構通過業務拆分實現服務元件化,通過元件組合快速開發系統,業務單一的服務元件又可以獨立部署,使得整個系統變得清晰靈活:
優點
- 原子服務
- 獨立程式
- 隔離部署
- 去中心化服務治理
缺點:
- 基礎設施的建設、複雜度高
元件服務化
通過服務來實現元件,意味著將應用拆散為一系列的服務執行在不同的程式中,那麼單一服務的區域性變化只需重新部署對應的服務程式。我們用 Go 實施一個微服務:
- kit:一個微服務的基礎庫(框架)。
- service:業務程式碼 + kit 依賴 + 第三方依賴組成的業務微服務
- rpc + message queue:輕量級通訊
本質上等同於,多個微服務組合(compose)完成了一個完整的使用者場景(usecase)。
按業務組織服務
YOU BUILD IT , YOU FIX IT
開發團隊對軟體在生產環境的執行負全部責任
事實上傳統應用設計架構的分層結構正反映了不同角色的溝通結構。所以若要按微服務的方式來構建應用,也需要對應調整團隊的組織架構。每個服務背後的小團隊的組織是跨功能的,包含實現業務所需的全面的技能。
去中心化
- 資料去中心化
- 治理去中心化
- 技術去中心化
每個服務獨享自身的資料儲存設施(快取,資料庫等),不像傳統應用共享一個快取和資料庫,這樣有利於服務的獨立性,隔離相關干擾
基礎設定自動化
無自動化不微服務,自動化包括測試和部署。單一程式的傳統應用被拆分為一系列的多程式服務後,意味著開發、除錯、測試、監控和部署的複雜度都會相應增大,必須要有合適的自動化基礎設施來支援微服務架構模式,否則開發、運維成本將大大增加。
- CICD:Gitlab + Gitlab Hooks + k8s
- Testing:測試環境、單元測試、API自動化測試
- 線上執行時:k8s,以及一系列Prometheus、ELK、Conrtol Panle
可用性 & 相容器
著名的 Design For Failure 思想,微服務架構採用粗粒度的程式間通訊,引入了額外的複雜性和需要處理的新問題,如網路延遲、訊息格式、負載均衡和容錯,忽略其中任何一點都屬於對“分散式計算的誤解”。
- 隔離
- 超時控制
- 負載保護
- 限流
- 降級
- 重試
- 負載均衡
一旦採用了微服務架構模式,那麼在服務需要變更時我們要特別小心,服務提供者的變更可能引發服務消費者的相容性破壞,時刻謹記保持服務契約(介面)的相容性。
Be conservative in what you send, be liberal in what you accept.
傳送時要保守,接收時要開放。按照伯斯塔爾法則的思想來設計和實現服務時,傳送的資料要更保守,意味著最小化的傳送必要的資訊,接收時更開放意味著要最大限度的容忍冗餘資料,保證相容性。
API Gateway
需要一個 Backend for frontend 資料聚合
在服務內進行大量的dataset join,按照業務場景來設計粗粒度的 API,給後續服務的演進帶來的很多優勢:
- 輕量互動:協議精簡、聚合。
- 差異服務:資料裁剪以及聚合、針對終端定製化API。
- 動態升級:原有系統相容升級,更新服務而非協議。
- 溝通效率提升,協作模式演進為移動業務+閘道器小組。
BFF 可以認為是一種適配服務,將後端的微服務進行適配(主要包括聚合裁剪和格式適配等邏輯),向無線端裝置暴露友好和統一的 API,方便無線裝置接入訪問後端服務。
Mircoservice**劃分**
在實際專案中通常會採用兩種不同的方式劃分服務邊界,即通過業務職能(Business Capability)或是 DDD 的限界上下文(Bounded Context)。
Business Capability
由公司內部不同部門提供的職能。例如客戶服務部門提供客戶服務的職能,財務部門提供財務相關的職能。
Bounded Context
限界上下文是 DDD 中用來劃分不同業務邊界的元素,這裡業務邊界的含義是“解決不同業務問題”的問題域和對應的解決方案域,為了解決某種型別的業務問題,貼近領域知識,也就是業務。
這本質上也促進了組織結構的演進:Service per team
CQRS(查詢職責分離),將應用程式分為兩部分:命令端和查詢端。
- 命令端處理程式建立,更新和刪除請求,並在資料更改時發出事件。
- 查詢端通過針對一個或多個物化檢視執行查詢來處理查詢,這些物化檢視通過訂閱資料更改時發出的事件流而保持最新
Mircoservice 安全
在 API Gateway 進行統一的認證攔截,一旦認證成功,我們會使用 JWT 方式通過 RPC 後設資料傳遞的方式帶到 BFF 層,BFF 校驗 Token 完整性後把身份資訊注入到應用的 Context 中,BFF 到其他下層的微服務,建議是直接在 RPC Request 中帶入使用者身份資訊(UserID)請求服務。
API Gateway -> BFF -> Service
Biz Auth -> JWT -> Request Args
對於服務內部,一般要區分身份認證和授權。
- Full Trust
- Half Trust
- Zero Trust
GRPC
gRPC 是什麼可以用官網的一句話來概括
“A high-performance, open-source universal RPC framework”
- 多語言:語言中立,支援多種語言。
- 輕量級、高效能:序列化支援 PB(Protocol Buffer)和 JSON,PB 是一種語言無關的高效能序列化框架。
- 可插拔
- IDL:基於檔案定義服務,通過 proto3 工具生成指定語言的資料結構、服務端介面以及客戶端 Stub。
- 設計理念
- 移動端:基於標準的 HTTP2 設計,支援雙向流、訊息頭壓縮、單 TCP 的多路複用、服務端推送等特性,這些特性使得 gRPC 在移動端裝置上更加省電和節省網路流量
- 服務而非物件、訊息而非引用:促進微服務的系統間粗粒度訊息互動設計理念。
- 負載無關的:不同的服務需要使用不同的訊息型別和編碼,例如 protocol buffers、JSON、XML 和 Thrift。
- 流:Streaming API。
- 阻塞式和非阻塞式:支援非同步和同步處理在客戶端和服務端間互動的訊息序列。
- 後設資料交換:常見的橫切關注點,如認證或跟蹤,依賴資料交換。
- 標準化狀態碼:客戶端通常以有限的方式響應 API 呼叫返回的錯誤。
HealthCheck
gRPC 有一個標準的健康檢測協議,在 gRPC 的所有語言實現中基本都提供了生成程式碼和用於設定執行狀態的功能。
主動健康檢查 health check,可以在服務提供者服務不穩定時,被消費者所感知,臨時從負載均衡中摘除,減少錯誤請求。當服務提供者重新穩定後,health check 成功,重新加入到消費者的負載均衡,恢復請求。health check,同樣也被用於外掛方式的容器健康檢測,或者流量檢測(k8s liveness & readiness)。
服務發現
客戶端發現
一個服務例項被啟動時,它的網路地址會被寫到登錄檔上;當服務例項終止時,再從登錄檔中刪除;這個服務例項的登錄檔通過心跳機制動態重新整理;客戶端使用一個負載均衡演算法,去選擇一個可用的服務例項,來響應這個請求
直連,比服務端服務發現少一次網路跳轉,Consumer 需要內建特定的服務發現客戶端和發現邏輯。
服務端發現
客戶端通過負載均衡器向一個服務傳送請求,這個負載均衡器會查詢服務登錄檔,並將請求路由到可用的服務例項上。服務例項在服務登錄檔上被註冊和登出(Consul Template+Nginx,kubernetes+etcd)。
總結
微服務的核心是去中心化,我們使用客戶端發現模式。
Eureka
通過 Family(appid)和Addr(IP:Port) 定位例項,除此之外還可以附加更多的後設資料:權重、染色標籤、叢集等。
appid: 使用三段式命名 :business.service.xxx
Provider 註冊後定期(30s)心跳一次,註冊, 心跳,下線都需要進行同步,註冊和下線需要進行長輪詢推送。
新啟動節點,需要 load cache,JVM 預熱。
故障時,Provider 不建議重啟和釋出。
Consumer啟動時拉取例項,發起30s長輪詢。
故障時,需要 client側 cache 節點資訊。
Server定期(60s) 檢測失效(90s)的例項,失效則剔除。短時間裡丟失了大量的心跳連線(15分鐘內心跳低於期望值*85%),開啟自我保護,保留過期服務不刪除。
多叢集
- 從單一叢集考慮,多個節點保證可用性,我們通常使用 N+2 的方式來冗餘節點
- 從單一叢集故障帶來的影響面角度考慮冗餘多套叢集
- 多套冗餘的叢集對應多套獨佔的快取,帶來更好的效能和冗餘能力。
- 儘量避免業務隔離使用或者 sharding 帶來的 cache hit 影響(按照業務劃分叢集資源)。
業務隔離叢集帶來的問題是cache hit ratio 下降,不同業務形態資料正交,我們推而求其次整個叢集全部連線。
缺點
統一為一套邏輯叢集(物理上多套資源池),即gRPC 客戶端預設忽略服務發現中的cluster資訊,按照全部節點,全部連線。能不能找到一種演算法從全叢集中選取一批節點(子集),利用劃分子集限制連線池大小。
- 長連線導致的記憶體和 CPU 開銷,HealthCheck 可以高達30%。
- 短連線極大的資源成本和延遲。
解決方案
合適的子集大小和選擇演算法
通常20-100個後端,部分場景需要大子集,比如大批量讀寫操作。
後端平均分給客戶端。
客戶端重啟,保持重新均衡,同時對後端重啟保持透明,同時連線的變動最小。
多租戶
本質是 流量染色
給入站請求繫結上下文(如: http header),in-process使用context傳遞,跨服務使用metadata傳遞(如: opentracing baggage item),在這個架構中每一個基礎元件都能夠理解租戶資訊,並且能夠基於租戶路由隔離流量,同時在我們的平臺中允許對執行不同的微服務有更多的控制,比如指標和日誌。在微服務架構中典型的基礎元件是日誌,指標,儲存,訊息佇列,快取以及配置。基於租戶資訊隔離資料需要分別處理基礎元件。
多租戶架構本質上描述為:
跨服務傳遞請求攜帶上下文(context),資料隔離的流量路由方案。
利用服務發現註冊租戶資訊,註冊成特定的租戶。
本作品採用《CC 協議》,轉載必須註明作者和本文連結