歲月不居,時節如流,在習大大的新年致辭中我們又漲了一歲,轉眼間做程序員已經六年時間了,這些年做了很多項目也有很多收穫,自己也總結了一套理論。下面就結合自己的工作經驗,學習認知及實際實踐來談下怎麼才能構造一個高性能完備的技術架構。
談到高性能大家首先想到的是應用服務的qps,併發數,響應時間。其實一個系統的性能每一個指標都可能影響整體系統的性能,併成爲瓶頸,所以要打造一個高性能的系統架構就需要一個完整的架構方案,解決好每個方面的痛點。以下每個點都有可能成爲系統瓶頸。
緩存
一個高性能的服務離不開緩存的助力,緩存大體可以分爲本地緩存、分佈式緩存。
本地緩存主要存在本地memory中,使用本地緩存需要注意jvm參數配置垃圾回收算法配置,根據不同的使用場景緩存大小配置適當的回收算法和heap分配策略。
分佈式緩存目前比較流行的是redis,redis又分爲幾種模式,有單點模式、主從模式(master/slaver)、哨兵模式(sentinel)。
單點模式:單節點模式就是單個redis實例,適合對性能要求不強,對服務持續輸出能力不敏感的應用。
主從模式(master/slaver):主從模式是使用一個redis實例作爲主機,多個實例作爲備份機,即一個master主節點,多個slaver從節點。master節點可以進行讀寫操作,slaver則是同步master主機的數據,slaver節點提供讀取操作。主從模式很好的解決了數據備份問題,實現了讀寫分離。且某個slaver節點掛掉後不影響其他slaver節點讀操作和master節點讀寫操作,master節點掛掉後,slaver不會競選成爲master,redis不在提供寫服務,直到master節點恢復。
Sentinel(哨兵模式):Sentinel模式是在主從模式的基礎上加入了哨兵機制,一個master節點,多個slaver節點,多個sentinel節點。每個sentinel節點其實就是一個redis實例,但與master節點和salver節點不同的是sentinel節點用來監控redis數據節點,當master節點掛點之後,sentinel會在slaver節點中選擇一個作爲master,保證了redis能持續對外提供服務。
以上三種模式只是解決了數據備份問題,但要高性能輸出還要解決資源分配、負載均衡問題,值得高興的是目前已有幾種分案可以解決此問題,redis cluster、代理分片(proxy)、客戶端分片這三種redis集羣解決方案都得到了廣泛應用。
客戶端分片
客戶端採用哈希算法將Redis數據的key進行散列,通過hash函數,特定的key會映射到特定的Redis節點上
代理分片
代理分片(proxy)處於客戶端和服務器的中間,將客戶端發來的請求,先進行處理(如sharding),再根據規則轉發給後端真正的Redis服務器。客戶端不直接訪問Redis服務器,而是通過proxy代理中間件間接訪問。
Redis cluster
官方集羣方案,Redis3.0版本以上開始支持,它把多個Redis實例整合在一起,形成一個集羣。集羣的整個數據庫被分爲16384個槽(slot),數據庫中的每個鍵都屬於這16384個槽的其中一個。集羣是一個無中心的結構,每個節點都保存數據和整個集羣的狀態。每個節點都會保存其它節點的信息,知道其它節點所負責的槽,客戶端可以連接任意一個node進行操作,當客戶端操作的key沒有分配到該node上時,Redis會返回轉向指令,指向正確的node。
選擇好合適的模式集羣后還要注意緩存雪崩、緩存穿透、緩存預熱、緩存更新、緩存降級等問題。
數據庫調優
數據庫優化是多方面的,可以從表設計,索引創建,程序代碼邏輯,鏈接資源管理,分庫分表分區等多方面進行優化。
分庫方案有sharding-jdbc(shardingsphere)、mycat。mycat是一箇中間件的第三方應用,它基於Proxy複寫了MySQL協議,將Mycat Server僞裝成一個MySQL數據庫。sharding-jdbc是一個jar包,它在Java的JDBC層以對業務應用零侵入的方式額外提供分片,讀寫分離,柔性事務和分佈式治理能力。使用mycat時不需要改代碼,而使用sharding-jdbc時需要修改代碼。
引入分庫分表解決了查表性能問題同時也要注意帶來的新問題,例如分佈式事務的如何處理,誇節點Join 的問題,跨節點合併排序分頁等聚合類SQL問題,多數據源管理問題,擴容縮容,數據遷移等問題。
監控
硬件監控可以使用zabbix,java診斷工具Arthas、pinpoint鏈路跟蹤、監控系統性能,jvm監控Prometheus,cat服務接口監控,除了這些常用的工具對服務進行監控,還需開發自己業務的監控,制定自定義監控指標,做好數據的採集、加工、做到全鏈路數據監控。
分佈式統一調度中心
爲什麼要使用分佈式統一調度中心,有以下幾點。
- 分佈式調度解決定時任務統一調度管理,降低開發和運維成本。
- 保證任務只執行一次,實現任務高可用,可伸縮和負載均衡,提高容錯。
- 通過控制檯部署和管理定時任務,方便靈感高效。
- 有完善的任務失敗重做機制和詳細的任務跟蹤及告警策略。
下面是幾個主流解決方案的對比
feature |
quartz |
elastic-job-cloud |
xxl-job |
antares |
opencron |
依賴 |
mysql |
jdk1.7+, zookeeper 3.4.6+ ,maven3.0.4+ ,mesos |
mysql ,jdk1.7+ , maven3.0+ |
jdk 1.7+ , redis , zookeeper |
jdk1.7+ , Tomcat8.0+ |
HA |
多節點部署,通過競爭數據庫鎖來保證只有一個節點執行任務 |
通過zookeeper的註冊與發現,可以動態的添加服務器。 支持水平擴容 |
集羣部署 |
集羣部署 |
— |
任務分片 |
— |
支持 |
支持 |
支持 |
— |
文檔完善 |
完善 |
完善 |
完善 |
文檔略少 |
文檔略少 |
管理界面 |
無 |
支持 |
支持 |
支持 |
支持 |
難易程度 |
簡單 |
較複雜 |
簡單 |
一般 |
一般 |
公司 |
OpenSymphony |
噹噹網 |
個人 |
個人 |
個人 |
高級功能 |
— |
彈性擴容,多種作業模式,失效轉移,運行狀態收集,多線程處理數據,冪等性,容錯處理,spring命名空間支持 |
彈性擴容,分片廣播,故障轉移,Rolling實時日誌,GLUE(支持在線編輯代碼,免發佈),任務進度監控,任務依賴,數據加密,郵件報警,運行報表,國際化 |
任務分片, 失效轉移,彈性擴容 , |
時間規則支持quartz和crontab ,kill任務, 現場執行,查詢任務運行狀態 |
缺點 |
沒有管理界面,以及不支持任務分片等。不適用於分佈式場景 |
需要引入zookeeper , mesos, 增加系統複雜度, 學習成本較高 |
調度中心通過獲取 DB鎖來保證集羣中執行任務的唯一性, 如果短任務很多,隨着調度中心集羣數量增加,那麼數據庫的鎖競爭會比較厲害,性能不好。 |
不支持動態添加任務 |
不適用於分佈式場景 |
使用企業 |
大衆化產品,對分佈式調度要求不高的公司大面積使用 |
36氪,噹噹網,國美,金柚網,聯想,唯品會,亞信,平安,豬八戒 |
大衆點評,運滿滿,優信二手車,拍拍貸 |
— |
— |
分佈式統一配置中心
分佈式統一配置中心將配置從代碼中分離出來,實現熱更新,統一管控,目前解決方案主要有spring-cloud-config、appllo、disconf。
服務熔斷限流降級
一個良好的服務離不開服務熔斷、限流降級措施。服務熔斷非常重要,它保證了服務在出現問題例如流量高峯、服務雪崩、緩存擊穿等,保證服務能持續對外提供服務。Hystrix是Netflix開源的一款容錯框架,包含常用的容錯方法:線程池隔離、信號量隔離、熔斷、降級回退。在高併發訪問下,系統所依賴的服務的穩定性對系統的影響非常大,依賴有很多不可控的因素,比如網絡連接變慢,資源突然繁忙,暫時不可用,服務脫機等。構建一套穩定、可靠的分佈式系統,就必須要有這樣一套容錯方法。
服務降級是在服務器壓力陡增的情況下,可利用資源有限,根據當前業務情況,關閉某些服務接口或者頁面,以此釋放服務器資源以保證核心任務的正常運行。
服務限流是對併發請求進行限速或者同一個時間窗口內的請求進行限速來保護系統,一旦達到限速標準則可以拒絕服務或排隊。常見的限流算法有令牌桶、漏桶、計數器。
池化技術
服務中有些資源是比較稀缺或者比較佔用資源,需要限制使用,這時候就需要池化技術來限制總資源數,比如連接池、線程池、資源池GenericObjectPool。保證資源得到有效利用且不浪費資源,同時獲取資源速度快。
微服務化
微服務就是分離業務,可以按照業務不同或者服務功能相同對服務進行分離整合,服務間經過http或dubbo進行通訊,多個微服務組合一起提供完整的服務。微服務實現方式可以根據自身業務量身定製,貼合自己業務的就是最好的。具體怎麼實現目前沒有唯一標準但是卻能帶來以下這些好處。
- 模塊即是服務,業務解耦,降低業務風險
- 獨立開發、測試、發佈便於發部維護且進行敏捷開發
- 單獨配置資源、熔斷、降級,做到最優利用資源
- 服務共享,輕量級通訊(如dubbo)減少重複工作
- 跨語言,不在侷限於某一種語言開發業務
異步化
系統架構在怎麼優化,某些場景的請求處理也不能做到實時響應,異步執行可以很好的解決這一問題,可以通過mq、多線程來實現異步,但是異步也帶來了系統的複雜性。