一、Redis架構的方案經歷階段node
1.1. 客戶端分片nginx
客戶端分片:redis
優勢算法
不依賴於第三方中間件,實現方法和代碼本身掌控,可隨時調整後端
這種分片機制的性能比代理式更好(少了一箇中間分發環節)緩存
可控的分發請求,分發壓力落在客戶端,無服務器壓力增長安全
缺點性能優化
不能平滑的水平擴展節點,擴容/縮容時,必須手動調整分片程序服務器
出現故障,不能自動轉移,運維性不好網絡
客戶端得本身維護一套路由算法
升級複雜
1.2. Twemproxy
Twemproxy:
優勢
運維成本低。業務方不用關心後端Redis實例,跟操做Redis同樣
Proxy 的邏輯和存儲的邏輯是隔離的
缺點
代理層多了一次轉發,性能有所損耗
進行擴容/縮容時候,部分數據可能會失效,須要手動進行遷移,對運維要求較高,並且難以作到平滑的擴縮容
出現故障,不能自動轉移,運維性不好
升級複雜
1.3. Redis Cluster
Redis Cluster:
優勢
無中心節點
數據按照Slot存儲分佈在多個Redis實例上
平滑的進行擴容/縮容節點
自動故障轉移(節點之間經過Gossip協議交換狀態信息,進行投票機制完成Slave到Master角色的提高)
下降運維成本,提升了系統的可擴展性和高可用性
缺點
嚴重依賴外部Redis-Trib
缺少監控管理
須要依賴Smart Client(鏈接維護, 緩存路由表, MultiOp和Pipeline支持)
Failover節點的檢測過慢,不如「中心節點ZooKeeper」及時
Gossip消息的開銷
沒法根據統計區分冷熱數據
Slave「冷備」,不能緩解讀壓力
1.4. Proxy+Redis Cluster
Smart Client vs Proxy:
優勢
Smart Client:
a. 相比於使用代理,減小了一層網絡傳輸的消耗,效率較高。
b. 不依賴於第三方中間件,實現方法和代碼本身掌控,可隨時調整。
Proxy:
a. 提供一套HTTP Restful接口,隔離底層存儲。對客戶端徹底透明,跨語言調用。
b. 升級維護較爲容易,維護Redis Cluster,只須要平滑升級Proxy。
c. 層次化存儲,底層存儲作冷熱異構存儲。
d. 權限控制,Proxy能夠經過祕鑰控制白名單,把一些不合法的請求都過濾掉。而且也能夠控制用戶請求的超大Value進行控制,和過濾。
e. 安全性,能夠屏蔽掉一些危險命令,好比Keys、Save、Flush All等。
f. 容量控制,根據不一樣用戶容量申請進行容量限制。
g. 資源邏輯隔離,根據不一樣用戶的Key加上前綴,來進行資源隔離。
h. 監控埋點,對於不一樣的接口進行埋點監控等信息。
缺點
Smart Client:
a. 客戶端的不成熟,影響應用的穩定性,提升開發難度。
b. MultiOp和Pipeline支持有限。
c. 鏈接維護,Smart客戶端對鏈接到集羣中每一個結點Socket的維護。
Proxy:
a. 代理層多了一次轉發,性能有所損耗。
b.進行擴容/縮容時候對運維要求較高,並且難以作到平滑的擴縮容。
二、爲何選擇Nginx開發Proxy
1.單Master多Work模式,每一個Work跟Redis同樣都是單進程單線程模式,而且都是基
於Epoll事件驅動的模式。
2.Nginx採用了異步非阻塞的方式來處理請求,高效的異步框架。
3.內存佔用少,有本身的一套內存池管理方式,。將大量小內存的申請彙集到一塊,可以比Malloc 更快。減小內存碎片,防止內存泄漏。減小內存管理複雜度。
4. 爲了提升Nginx的訪問速度,Nginx使用了本身的一套鏈接池。
5. 最重要的是支持自定義模塊開發。
6. 業界內,對於Nginx,Redis的口碑可稱得上兩大神器。性能也就不用說了。
三、Proxy+Redis Cluster介紹
3.1 Proxy+Redis Cluster架構方案介紹
1. 用戶在ACL平臺申請集羣資源,若是申請成功返回祕鑰信息。
2. 用戶請求接口必須包含申請的祕鑰信息,請求至LVS服務器。
3. LVS根據負載均衡策略將請求轉發至Nginx Proxy。
4. Nginx Proxy首先會獲取祕鑰信息,而後根據祕鑰信息去ACL服務上獲取集羣的種子信息。(種子信息是集羣內任意幾臺IP:PORT節點)
而後把祕鑰信息和對應的集羣種子信息緩存起來。而且第一次訪問會根據種子IP:PORT獲取集羣Slot對應節點的Mapping路由信息,進行緩存起來。最後根據Key計算SlotId,從緩存路由找到節點信息。
5. 把相應的K/V信息發送到對應的Redis節點上。
6. Nginx Proxy定時(60s)上報請求接口埋點的QPS,RT,Err等信息到Open-Falcon平臺。
7. Redis Cluster定時(60s)上報集羣相關指標的信息到Open-Falcon平臺。
3.2 Nginx Proxy功能介紹
目前支持的功能:
HTTP Restful接口:
解析用戶Post過來的數據, 而且構建Redis協議。客戶端不須要開發Smart Client, 對客戶端徹底透明、跨語言調用
權限控制:
根據用戶Post數據獲取AppKey,Uri, 而後去ACL Service服務裏面進行認證。若是認證經過,會給用戶返回相應的集羣種子IP,以及相應的過時時間限制等信息
限制數據大小:
獲取用戶Post過來的數據,對Key,Value長度進行限制,避免產生超大的Key,Value,打滿網卡、阻塞Proxy
數據壓縮/解壓:
若是是寫請求,對Value進行壓縮(Snappy),而後在把壓縮後的數據存儲到Redis Cluster。
若是是讀請求,把Value從Redis Cluster讀出來,而後對Value進行解壓,最後響應給用戶。
緩存路由信息:
維護路由信息,Slot對應的節點的Mapping信息
結果聚合:
MultiOp支持
批量指令支持(Pipeline/Redis+Lua+EVALSHA進行批量指令執行)
資源邏輯隔離:
根據用戶Post數據獲取該用戶申請的NameSpace,而後以NameSpace做爲該用戶請求Key的前綴,從而達到不一樣用戶的不一樣NameSpace,進行邏輯資源隔離
重試策略:
針對後端Redis節點出現Moved,Ask,Err,TimeOut等進行重試,重試次數可配置
鏈接池:
維護用戶請求的長鏈接,維護後端服務器的長鏈接
配額管理:
根據用戶的前綴(NameSpace), 定時的去抓取RANDOMKEY,根據必定的比率,估算出不一樣用戶的容量大小值,而後在對用戶的配額進行限制管理
過載保護:
經過在Nginx Proxy Limit模塊進行限速,超過集羣的承載能力,進行過載保護。從而保證部分用戶可用,不至於壓垮服務器
監控管理:
Nginx Proxy接入了Open-Falcon對系統級別,應用級別,業務級別進行監控和告警
例如: 接口的QPS,RT,ERR等進行採集監控,而且展現到DashBoard上
告警閾值的設置很是靈活,配置化
待開發的功能列表:
層次化存儲:
利用Nginx Proxy共享內存定製化開發一套LRU本地緩存實現,從而減小網絡請求
冷數據Swap到慢存儲,從而實現冷熱異構存儲
主動Failover節點:
因爲Redis Cluster是經過Gossip通訊, 超過半數以上Master節點通訊(cluster-node-timeout)認爲當前Master節點宕機,才真的確認該節點宕機。判斷節點宕機時間過長,在Proxy層加入Raft算法,加快失效節點斷定,主動Failover
3.3 Nginx Proxy性能優化
3.3.1 批量接口優化方案
1. 子請求變爲協程
案例:
用戶需求調用批量接口mget(50Key)要求性能高,吞吐高,響應快。
問題:
因爲最先用的Nginx Subrequest來作批量接口請求的處理,性能一直不高,CPU利用率也不高,QPS提不起來。經過火焰圖觀察分析子請求開銷比較大。
解決方案:
子請求效率較低,由於它須要從新從Server Rewrite開始走一遍Request處理的PHASE。而且子請求共享父請求的內存池,子請求同時併發度過大,致使內存較高。
協程輕量級的線程,佔用內存少。通過調研和測試,單機一兩百萬個協程是沒有問題的,
而且性能也很高。
優化前:
a) 用戶請求mget(k1,k2)到Proxy
b) Proxy根據k1,k2分別發起子請求subrequest1,subrequest2
c) 子請求根據key計算slotid,而後去緩存路由表查找節點
d) 子請求請求Redis Cluster的相關節點,而後響應返回給Proxy
e) Proxy會合並全部的子請求返回的結果,而後進行解析包裝返回給用戶
優化後:
a) 用戶請求mget(k1,k2)到Proxy
b) Proxy根據k1,k2分別計算slotid, 而後去緩存路由表查找節點
c) Proxy發起多個協程coroutine1, coroutine2併發的請求Redis Cluster的相關節點
d) Proxy會合並多個協程返回的結果,而後進行解析包裝返回給用戶
2. 合併相同槽,批量執行指令,減小網絡開銷
案例:
用戶需求調用批量接口mget(50key)要求性能高,吞吐高,響應快。
問題:
通過上面協程的方式進行優化後,發現批量接口性能仍是提高不夠高。經過火焰圖觀察分析網絡開銷比較大。
解決方案:
由於在Redis Cluster中,批量執行的key必須在同一個slotid。因此,咱們能夠合併相同slotid的key作爲一次請求。而後利用Pipeline/Lua+EVALSHA批量執行命令來減小網絡開銷,提升性能。
優化前:
a) 用戶請求mget(k1,k2,k3,k4) 到Proxy。
b) Proxy會解析請求串,而後計算k1,k2,k3,k4所對應的slotid。
c) Proxy會根據slotid去路由緩存中找到後端服務器的節點,併發的發起多個請求到後端服務器。
d) 後端服務器返回結果給Proxy,而後Proxy進行解析獲取key對應的value。
e) Proxy把key,value對應數據包裝返回給用戶。
優化後:
a) 用戶請求mget(k1,k2,k3,k4) 到Proxy。
b) Proxy會解析請求串,而後計算k1,k2,k3,k4所對應的slotid,而後把相同的slotid進行合併爲一次Pipeline請求。
c) Proxy會根據slotid去路由緩存中找到後端服務器的節點,併發的發起多個請求到後端服務器。
d) 後端服務器返回結果給Proxy,而後Proxy進行解析獲取key對應的value。
e) Proxy把key,value對應數據包裝返回給用戶。
3. 對後端併發度的控制
案例:
當用戶調用批量接口請求mset,若是k數量幾百個甚至幾千個時,會致使Proxy瞬間同時發起幾百甚至幾千個協程同時去訪問後端服務器Redis Cluster。
問題:
Redis Cluster同時併發請求的協程過多,會致使鏈接數瞬間會很大,甚至超過上限,CPU,鏈接數忽高忽低,對集羣形成不穩定。
解決方案:
單個批量請求對後端適當控制併發度進行分組併發請求,反向有利於性能提高,避免超過Redis Cluster鏈接數,同時Redis Cluster 波動也會小不少,更加的平滑。
優化前:
a) 用戶請求批量接口mset(200個key)。(這裏先忽略合併相同槽的邏輯)
b) Proxy會解析這200個key,會同時發起200個協程請求併發的去請求Redis Cluster。
c) Proxy等待全部協程請求完成,而後合併全部協程請求的響應結果,進行解析,包裝返回給用戶。
優化後:
a) 用戶請求批量接口mset(200個key)。 (這裏先忽略合併相同槽的邏輯)
b) Proxy會解析這200個key,進行分組。100個key爲一組,分批次進行併發請求。
c) Proxy先同時發起第一組100個協程(coroutine1, coroutine100)請求併發的去請求Redis Cluster。
d) Proxy等待全部協程請求完成,而後合併全部協程請求的響應結果。
e) Proxy而後同時發起第二組100個協程(coroutine101, coroutine200)請求併發的去請求Redis Cluster。
f) Proxy等待全部協程請求完成,而後合併全部協程請求的響應結果。
g) Proxy把全部協程響應的結果進行解析,包裝,返回給用戶。
4.單Work分散到多Work
案例:
當用戶調用批量接口請求mset,若是k數量幾百個甚至幾千個時,會致使Proxy瞬間同時發起幾百甚至幾千個協程同時去訪問後端服務器Redis Cluster。
問題:
因爲Nginx的框架模型是單進程單線程, 因此Proxy發起的協程都會在一個Work上,這樣若是發起的協程請求過多就會致使單Work CPU打滿,致使Nginx 的每一個Work CPU使用率很是不均,內存持續暴漲的狀況。(nginx 的內存池只能提早釋放大塊,不會提早釋放小塊)
解決方案:
增長一層緩衝層代理,把請求的數據進行拆分爲多份,而後每份發起請求,控制併發度,在轉發給Proxy層,避免單個較大的批量請求打滿單Work,從而達到分散多Work,達到Nginx 多個Wrok CPU使用率均衡。
優化前:
a) 用戶請求批量接口mset(200個key)。(這裏先忽略合併相同槽的邏輯)
b) Proxy會解析這200個key,會同時發起200個協程請求併發的去請求Redis Cluster。
c) Proxy等待全部協程請求完成,而後合併全部協程請求的響應結果,進行解析,包裝返回給用戶。
優化後:
a) 用戶請求批量接口mset(200個key)。(這裏先忽略合併相同槽的key的邏輯)
b) Proxy會解析這200個key,而後進行拆分分組以此來控制併發度。
c) Proxy會根據劃分好的組進行一組一組的發起請求。
d) Proxy等待全部請求完成,而後合併全部協程請求的響應結果,進行解析,包裝返回給用戶。
總結,通過上面一系列優化,咱們能夠來看看針對批量接口mset(50個k/v)性能對比圖,Nginx Proxy的響應時間比Java版本的響應時間快了5倍多。
Java版本:
3.3.2 網卡軟中斷優化
irqbalance根據系統中斷負載的狀況,自動遷移中斷保持中斷的平衡。可是在實時系統中會致使中斷自動漂移,對性能形成不穩定因素,在高性能的場合建議關閉。
一、首先關閉網卡軟中斷
service irqbalance stop
service cpuspeed stop
二、查看網卡是隊列
grep eth /proc/interrupts | awk '{print $1, $NF}'
77: eth0
78: eth0-TxRx-0
79: eth0-TxRx-1
80: eth0-TxRx-2
81: eth0-TxRx-3
82: eth0-TxRx-4
83: eth0-TxRx-5
84: eth0-TxRx-6
85: eth0-TxRx-7
三、綁定網卡軟中斷到CPU0-2號上
(注意這裏的echo 是十六進制)
echo "1" > /proc/irq/78/smp_affinity
echo "1" > /proc/irq/79/smp_affinity
echo "2" > /proc/irq/80/smp_affinity
echo "2" > /proc/irq/81/smp_affinity
echo "2" > /proc/irq/82/smp_affinity
echo "4" > /proc/irq/83/smp_affinity
echo "4" > /proc/irq/84/smp_affinity
echo "4" > /proc/irq/85/smp_affinity
3.3.3 綁定進程到指定的CPU
綁定nginx或者redis的pid到cpu3-cpu10上:
taskset -cp 3 1900
taskset -cp 4 1901
taskset -cp 5 1902
taskset -cp 6 1903
taskset -cp 7 1904
taskset -cp 8 1905
taskset -cp 9 1902
taskset -cp 10 1902
或者經過Nginx Proxy配置:
worker_cpu_affinity 綁定CPU親緣性
3.3.4 性能優化神器火焰圖
3.4 Redis Cluster運維
3.4.1 運維功能
1. 建立集羣
2. 集羣擴容/縮容
3. 節點宕機
4. 集羣升級
5. 遷移數據
6. 副本遷移
7. 手動failover
8. 手動rebalance
以上相關運維功能,目前是經過腳本配置化一鍵式操做,依賴於官方的redis-rebalance.rb進行擴展開發。運維很是方便快捷。
3.5 性能測試報告
3.5.1 測試環境
軟件:
Jmeter
Nginx Proxy(24核)
Redis集羣(4 Master,4 Slave)
測試Key(100000)
硬件:
OS: Centos6.6
CPU:24核
帶寬:千兆
內存:62G
測試結果:場景:普通K/V
QPS:18W左右
RT: 99都在10ms之內
CPU:Nginx Proxy CPU在50%左右
四、監控告警
4.1 系統級別
經過Open-Falcon Agent採集服務器的CPU、內存、網卡流量、網絡鏈接、磁盤等信息。
4.2 應用級別
經過Open-Falcon Plugin採集Nginx/Redis進程級別的CPU,內存,Pid等信息。
4.3 業務級別
經過在Proxy裏面埋點監控業務接口QPS,RT(50%,99%,999%),請求流量,錯誤次數等信息,定時的上報給Open-Falcon。
經過Open-Falcon Plugin採集Redis Cluster集羣信息,QPS,鏈接數等相關指標指標信息。