應用架構演進
這裡的架構演進應該是從服務化的角度來說,應該說隨著業務發展,應用規模擴大,系統的一些公共服務就會抽取出來,獨立開發,部署,維護,用來解決併發,擴充套件,維護的問題。
傳統垂直架構
有的地方也叫單體應用,以mvc模式開發:
- 所有應用程式碼統一打包,程式碼所有介面本地api呼叫,很少存在遠端服務呼叫;
- 單機或主備,應用做叢集部署;
- DB主從等。
這種並沒有什麼不好,發展初期大多是這樣,體量沒那麼大,也不需要考慮高併發大流量可擴充套件性什麼的,簡單粗暴,解決業務需求就好,活下去才能活的更好。
但是必須明白這種簡單架構存在的一些問題:
1. 業務不斷髮展,功能逐漸增多,應用的開發維護成本變高,部署效率降低,隨便改個程式碼,編譯一次十幾分鍾就浪費了。悲劇,我們有個系統才開發3年,就碰到這種情況,一次打包編譯部署,13分鐘結束。
2. 不同的人負責不同的部分,一些通用程式碼、公共程式碼就各寫各的,不能複用,如果只是util還好,但是一些外部服務的都有重複,那就happy了(不過這種情況的出現,不一定是架構問題,更多可能是管理);
3. 不斷地上新需求,不斷地改程式碼,有時測試不到位,指定哪裡埋了bug,上生產後系統就down了,牽一髮而動全身;
4. 可維護性,可靠性,擴充套件性變差。
既然有這些問題,就要解決啊,業務就會提要求,你要解決啊,要不然影響我業務發展,影響我ipo上市啊,苦逼的碼農開始幹活了。
不提應用的拆分主從那些手段,但從拆分後應用互動看,原來的本地api互動變成的遠端api的呼叫,這裡就出現了rpc,當然也有走esb,webservice。其實拆分後挺麻煩的,光一個分散式事務就能折騰死人。
RPC架構
Remote Procedure Call,遠端方法呼叫,遮蔽底層實現細節,像呼叫本地方法一樣呼叫遠端服務。
上個作者的圖:
這個圖對於大多數rpc框架通用,實現的幾個技術點:
1. 服務提供者釋出服務:服務介面定義,資料結構,服務提供者資訊等;
2. 客戶端遠端呼叫:通常是使用jdk的程式碼代理攔截;
3. 底層通訊:現在應該更多是使用netty吧,當然也有走支援http的;
4. 序列化:關注序列化反序列效能,xml,json,hessiaon,pb,protostuff,kryo等;
作者給了個socket實現簡單demo,來實現遠端呼叫,說明上面幾個技術點。
常用的rpc框架
1. Thrift;
2. Hadoop的Avro-RPC;
3. Hessian;
4. gRPC;
單論rpc的話,沒太多可說的,可是如果加上服務治理,那複雜度就幾何倍數增長了。服務治理裡面東西太多了,動態註冊,動態發現,服務管控,呼叫鏈分析等等問題這些問題,單憑rpc框架解決不了,所以現在常用的說的服務化框架,通常指的是rpc+服務治理2個點。
SOA服務化架構
感覺soa架構應該是在rpc之前出現,用來解決異構系統的互動,通常的實現是通過ESB,WSDL來處理。其粒度通常來說是比較粗的。也存在服務治理方面的問題。
微服務
MSA也是一種服務化架構風格,正流行ing,服務劃分
1. 原子服務,粒度細;
2. 獨立部署,主要是容器;
分享篇文章:雲棲肥俠的文章 微服務(Microservice)那點事 。
MSA與SOA的對比:
- 服務拆分粒度:soa首要解決的是異構系統的服務化,微服務專注服務的拆分,原子服務;
- 服務依賴:soa主要處理已有系統,重用已有的資產,存在大量服務間依賴,微服務強調服務自治,原子性,避免依賴耦合的產生;
- 服務規模:soa服務粒度大,大多數將多個服務合併打包,因此服務例項數有限,微服務強調自治,服務獨立部署,導致規模膨脹,對服務治理有挑戰;
- 架構差異:微服務通常是去中心化的,soa通常是基於ESB的;
- 服務治理:微服務的動態治理,實時管控,而soa通常是靜態配置治理;
- 交付:微服務的小團隊作戰。
感覺在有了docker後,微服務這個概念突然火了起來,總結就是微服務+容器+DevOps。
分散式服務框架入門
背景
應用從集中式走向分散式
隨著業務的發展導致功能的增多,傳統的架構模式開發,測試,部署整個流程變長,效率變低,後臺服務的壓力變大,只能通過硬體擴容來暫時緩解壓力,但解決不了根本性問題:
- 應用規模變大,開發維護成本變高,部署效率降低;
- 程式碼複用:原來是本地api呼叫,導致一些公用功能可能是按需開發,不統一,隨意等問題;
- 交付面臨困難:主要是業務變得複雜,新增修改測試變得困難,拉長整個流程。
通用法寶:拆分,大系統拆小系統,獨立擴充套件和伸縮。
- 縱向:分業務模組;
- 橫向:提煉核心功能,公共業務;
需要服務治理
大拆小,核心服務提煉後,服務的數量變多,而且需要一些執行態的管控,這時候就需要服務治理:
- 服務生命週期管理;
- 服務容量規劃;
- 執行期治理;
- 服務安全。
服務框架介紹
Dubbo
阿里開源的Dubbo應該是業界分散式服務框架最出名的了吧,看過公司的rpc框架,Dubbo的擴充套件性比我們的好的多了,我們的框架每次升級,改動都很多,改天要看下Dubbo的原始碼瞭解瞭解擴充套件性。
HSF
淘寶的體量決定了他對極致效能的追求,HSF跨機房特性挺牛。
Coral Service
這個沒聽說過,孤陋寡聞了。
框架設計
架構原理
萬變不離其中,這張圖可以概括rpc的一些通用原理:
細化了下:
- rpc層:底層的通訊框架,通訊協議,序列化和反序列化;
- 服務釋出訂閱;
- 服務治理;
功能
效能
可靠性
分散式的,面試會問,用池子的話講就是,知識點啊。
服務治理
通訊框架
技術點
- 長連線:主要是鏈路的建立過程到最後的關閉,耗時耗資源;每次呼叫都要建立的話,呼叫時延的問題,很可能鏈路建立的耗時比程式碼真正執行時長還多;
- BIO還是NIO:主要是執行緒模型的選擇,推薦篇文章 IO - 同步,非同步,阻塞,非阻塞 (亡羊補牢篇);
- 自研還是使用開源NIO框架:一般來說還是使用開源吧,技術成熟,社群支援,現在netty和mina使用較多了吧。
在功能設計方面,作者基於netty給了demo服務端和客戶端的程式碼,個人理解:
1. 通用性api;
2. 擴充套件性,封裝底層,提供上層介面,隔離協議和底層通訊;
可靠性設計
談分散式系統必談可靠性。
鏈路有效性
通過心跳來確認雙方c、s存活,保證鏈路可用,心跳檢測機制分為3個層面:
1. tcp層面,即tcp的keep-alive,作用於整個tcp協議棧;
2. 協議層的心跳檢測,主要存在於長連線協議中,例如smpp協議;
3. 應用層的心跳,業務雙方的定時傳送心跳訊息;
第2個沒聽說過,常用的是1,3。一般使用netty的話用的是netty的讀寫空閒來實現心跳。
斷連
不管因為網路掛了還是服務端當機,還是心跳超時什麼的,導致鏈路不可用關閉,這時候就需要鏈路重連,需要注意的一點就是短連後,不要立即重連,留時間給系統釋放資源,可以scheduler處理。
訊息快取重發
底層訊息不會立即傳送(也會導致半包粘包),斷鏈後,導致訊息丟失,看有無業務需求,有就支援斷鏈後訊息重發。
資源釋放
主要是斷鏈後,一定要保證資源銷燬和釋放,當然也包括一些執行緒池,記憶體等的釋放。
效能設計
效能差的三宗罪
對於底層通訊框架來說,主要是下面幾個:
1. 通訊模型的選擇,主要是阻塞非阻塞那些東西;
2. 序列化反序列化(後面有章單講序列化);
3. 執行緒模型,主要是服務端選擇什麼樣的執行緒模型來處理訊息。
通訊效能三原則
既然有上面的3個問題,那就針對這些做優化了:
- 傳輸:BIONIOAIO的選擇;
- 選擇自定義協議棧,便於優化;
- 服務端執行緒模型,單執行緒處理還是執行緒池,執行緒池是一個,還是分優先順序,Reactor還是其他什麼的。
高效能之道這節作者講了netty的優勢。
序列化與反序列化
也就是通常所說的編碼、解碼。通常的通訊框架會提供編解碼的介面,也會內建一些常用的序列化反序列化工具支援。
與通訊框架和協議的關係,感覺可以理解為:通訊框架是通道,其上跑的碼流資料是利用各種序列化編碼後的各種協議。
功能設計
各種序列化框架需要考慮的主要有:
- 序列化框架本身的功能的豐富,支援的資料型別;
- 多語言的支援;
- 相容性,往大了說:
- 服務介面的前後相容;
- 協議的相容;
- 支援的資料型別的相容。
- 效能,目的是最少的資源,最快的速度,最大的壓縮:
- 序列化後碼流大小;
- 序列化的速度;
- 序列化的資源佔用。
在擴充套件性這節,作者講了netty的對序列化的一些內建支援,但實際開發中,一般不太會使用這些東西,都會提供序列化反序列介面,自行擴充套件定義,所以擴充套件性特重要。
常用的序列化,xml,json,hessian,kryo,pb,ps,看需求需要支援那種,具體可以搜尋各序列化的效能和壓縮後大小。
協議棧
這一章最主要的是講了自定義協議棧的設計,已經互動的過程,其他講的可靠性設計什麼的跟之前通訊框架一章有重複。
通訊模型
服務提供者和消費者之間採用單鏈路,長連線通訊,鏈路建立流程:
1. 客戶端傳送握手請求,攜帶節點ID等認證資訊;
2. 服務端校驗:節點ID有效性,重複登入,ip地址黑白名單等,通過後,返回握手應答資訊;
3. 鏈路建立後,客戶端傳送業務訊息;
4. 客戶端服務端心跳維持鏈路;
5. 服務端退出時,關閉連線,客戶端感知連線關閉,關閉客戶端連線。
協議訊息定義
通過attachment相容了擴充套件性。作者還講了將訊息頭的通用序列化和訊息體的自定義序列化,看需求吧,我們公司的框架沒做這部分支援,做了簡化,將訊息頭和訊息體統一封裝,然後再加一個序列化方式組成一條訊息傳送。
安全性設計
- 內部的,不一定需要認證,也有基於系統,域名,ip的黑白名單,安全認證的;
- 外部開發平臺的話,基於祕鑰認證;
服務路由
服務路由指的是服務提供者叢集部署,消費端如何從服務列表中選擇合適的服務提供者提供服務進行呼叫。
透明化路由
- 基於zk的服務註冊中心的釋出訂閱;
- 消費者本地快取服務提供者列表,註冊中心當機後,不影響已有的使用,只是影響新服務的註冊和老服務的下線。
負載均衡
- 隨機
- 輪循
- 服務呼叫時延
- 一致性Hash
- 有個一致性hash演算法,挺有意思的,redis的客戶端shard用的
- 黏滯連線
- 這個應該不太常用,服務提供者多數無狀態,一旦有狀態,不利於擴充套件
這些都是點對點的連線,負載均衡大多會在客戶端執行,有種場景會取決於服務端負載,就是服務端服務配置的是域名。
本地路由優先策略
- injvm:jvm也提供了消費端的服務,可以改成優先本jvm,對於消費端來說,不需關注提供者;
- innative:injvm比較少,多得是可能是這種,一個物理機部署多個虛擬機器,或者一個容器部署多個服務提供者,消費者不需遠端呼叫,本機,本地或本機房優先。
路由規則
除了上面提供的各種路由負載均衡,還容許自定義路由規則:
- 條件路由:主要是通過條件表示式來實現;
- 指令碼路由:通過指令碼解析實現。
其實應該還有一種客戶端通過程式碼自定義路由選擇。這些主要是為了擴充套件性。
路由策略定製
自定義路由場景:
1. 灰度;
2. 引流;
路由策略:
1. 框架提供介面擴充套件;
2. 配置平臺提供路由指令碼配置;
配置化路由
- 本地配置:包括服務提供者和消費者,全域性配置3種;
- 註冊中心:路由策略統一註冊到服務註冊中心,集中化管理;
- 動態下發:配置後動態下發各服務消費端。
叢集容錯
指的是服務呼叫失敗後,根據容錯策略進行自動容錯處理。
叢集容錯場景
- 通訊鏈路故障:
- 通訊過程中,對方當機導致鏈路中斷;
- 解碼失敗等原因Rest掉連結;
- 消費者read-write socketchannel發生IOException導致鏈路中斷;
- 網路閃斷故障;
- 交換機異常導致鏈路中斷;
- 長時間Full GC導致;
- 服務端超時:
- 服務端沒有及時從網路讀取客戶端請求訊息,導致訊息阻塞;
- 服務端業務處理超時;
- 服務端長時間Full GC;
- 服務端呼叫失敗:
- 服務端解碼失敗;
- 服務端流控;
- 服務端佇列積壓;
- 訪問許可權校驗失敗;
- 違反SLA策略;
- 其他系統異常;
業務執行異常不屬於服務端異常。
容錯策略
這圖不錯,關係很清晰。
- 失敗自動切換(Failover):
- 呼叫失敗後切換鏈路呼叫;
- 服務提供者的防重;
- 重試次數和超時時間的設定。
- 失敗通知(FailBack):失敗後直接返回,由消費端自行處理;
- 失敗快取(Failcache):主要是失敗後,快取重試重發,注意:
- 快取時間、快取數量;
- 快取淘汰演算法;
- 定時重試的週期T、重試次數;
- 快速失敗(Failfast):失敗不處理,記錄日誌分析,可用於大促期間,對非核心業務的容錯。
容錯策略擴充套件
- 容錯介面的開放;
- 遮蔽底層細節,使用者自定義;
- 支援擴充套件。
其實還有一點,感覺也挺重要,就是支援容錯後本地mcok。呼叫失敗後的鏈路切換和快速失敗肯定要支援,快取重發可以不用。
本篇篇幅過長,相信能堅持看到最後的都是對知識求知如渴的人,將來必定不凡。順便點點關注點點讚唄。
歡迎工作一到五年的Java工程師朋友們加入Java架構開發:878249276
群內提供免費的Java架構學習資料(裡面有高可用、高併發、高效能及分散式、Jvm效能調優、Spring原始碼,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多個知識點的架構資料)合理利用自己每一分每一秒的時間來學習提升自己,不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代!