1. 目的
撰寫本文的目的是解決微服務架構,對靜態數據資源沒有規整,所有微服務都是從數據庫直接讀取,導致性能較差,用戶體驗不好。通常在高可用的分布式架構中,一般都會采用將這部分數據放到內存當中,提高系統的訪問性能。
如果采用redis這種內存型的緩存數據庫,那么針對分布式架構來說,必然要考慮其高可用,因此我們必然要考慮搭建Redis的集群方案來為微服務平臺提供保障。
2. 技術體系
Redis 3.0之后的版本支持Cluster。許多公司采用的是阿里云提供的Redis服務,使用的單節點的模式。
3. 緩存對象
3.1. 公共數據
公共數據:用戶數據、基礎字典等配置信息。
數據類型:經過對現有公共接口數據結構的分析,基本都是以List集合的形式對外輸出。
3.2. 獨立微服務數據
指的是某個微服務自己使用的數據,如:報表的數據編輯實時填寫緩存到Redis。
數據類型: String、List
4. 技術方案
4.1. 阿里云產品介紹
4.1.1. 規格介紹
序號 |
規格類型 |
描述 |
適用場景 |
1 |
標準版-單副本 |
標準版-單副本采用單個數據庫節點部署架構 |
l 純緩存類業務場景。 說明 對數據可靠性要求較高的敏感性業務,不建議使用單副本版。 l 對Redis協議兼容性要求較高的場景。 l 單個Redis性能壓力可控的場景。 l Redis命令相對簡單,排序和計算之類的命令較少的場景。 |
2 |
標準版-雙副本 |
標準版-雙副本采用主從(Replication)模式搭建。 |
l 對Redis協議兼容性要求較高的業務。 l Redis作為持久化數據存儲使用的業務。 l 單個Redis性能壓力可控的場景。 l Redis命令相對簡單,排序和計算之類的命令較少的場景。 |
3 |
集群版-單副本 |
單副本集群版實例采用集群架構,每個分片服務器采用單副本模式。 |
l 數據量較大的場景。 l 純緩存類業務場景。 說明 對數據可靠性要求較高的敏感性業務,不建議使用集群版-單副本。 l QPS壓力較大的場景。 l 吞吐密集型應用場景。 l 對數據持久化無要求的緩存類型業務場景。 |
4 |
集群版-雙副本 |
雙副本集群版實例采用集群架構,每個分片服務器采用雙副本模式。 |
l 數據量較大的場景。 l QPS壓力較大的場景。 l 吞吐密集型應用場景。 |
5 |
讀寫分離實例 |
Redis讀寫分離版本由代理服務器(Proxy Servers)、主備(Master and Replica)節點及只讀(Read-Only)節點組成。 |
l 讀取請求QPS壓力較大的場景。 l 對Redis協議兼容性要求較高的業務場景。 l Redis作為持久化數據存儲使用的業務場景。
|
4.1.2. 規格性能
4.2. 服務器配置
目前,在阿里云上,由于各位服務對redis使用量較少,甚至有的服務中基本沒有使用,所以當前的配置是1核1G
4.3. 三種架構模式
4.3.1. Redis主從
Redis主從模式是最簡單的一種集群方案配置起來也比較簡單,它的特點主要有:
l 一個master可以擁有多個slave;
l 多個slave鏈接同一個master,也可以鏈接其它slave;
l 主從復制不會阻塞master,在同步數據時,master可以繼續處理client請求;
l slave 配置為slave-read-only on需要升級為主節點或者寫入配置文件中, 而不能在默認slave情況下直接設置master與slave斷開后會檢測心跳, 重新建立連接;
l 可以直接copy DUMP文件重新重啟master,在Master為空以后,slave同步數據會抹掉全部數據。
該方案缺點較多,往Master節點寫數據,同時Master節點會異步寫入slave節點中。這種方案目前使用的越來越少,不過對于個體開發并且對緩存依賴度不高的系統還是可以使用的,畢竟搭建和維護簡單。
應用場景:可用于可穿透的業務場景,如后端有DB存儲,脫機影響不大的應用。
4.3.2. Redis sentinel
鑒于4.1.1節描述的standalone類型的架構缺點較多,故在此模式基礎上,增加sentinel哨兵,用于監控master/slave運行情況、調度Redis主從切換等。下圖中對于sentinel使用了最小粒度的集群模式,最大限度地實現較小規模的高可用緩存。
應用場景:用于高可用需求場景,可用于高可用Cache,存儲等場景。 內存/QPS受限于單機。
4.3.3. Redis Cluster
可直接采用官方給出的推薦方案,將node配置成主從結構。圖下圖所示為最小節點的Redis高并發、高可用集群。
應用場景:用于高可用需求場景,可用于大數據量高可用Cache/存儲等場景。 內存/QPS不受限于單機,可受益于分布式集群高擴展性。
4.4. 數據存儲格式
鑒于我們的微服務眾多,為了規避key一樣的情況發生。因此,需要約定下存儲格式:服務名#業務分類名##key(調用的key)
公共接口數據key約定:服務器ID#user##key(調用的key);
獨立微服務數據key約定:服務器ID#服務名##users##getUsersDetail(調用的key);
以上所有的“cs#user##”或“服務名#”開頭的前綴,統一由公共接口實現,業務方使用時的入口為最后的key即可。
4.5. 可用API
接口:IRedisService
序號 |
接口定義 |
描述 |
|
1 |
boolean set(String key, Object value) |
添加String類型的緩存數據 |
|
2 |
boolean set(String key, Object value, Long expireTime) |
自定義有效時間的String類型緩存數據 |
|
3 |
Object get(String key) |
讀取String類型的緩存 |
|
4 |
void setHash(String key, Map<?,?> map) |
哈希 添加Map |
|
5 |
Object getHash(String key) |
獲取哈希數據 |
|
6 |
void setList(String key, Object value) |
List列表添加 |
|
7 |
List<Object> getList(String key, long start, long end) |
List列表獲取 |
start 開始 end 結束 0 到 -1代表所有值 |
8 |
void addSet(String key, Object value) |
Set集合添加 |
|
9 |
Set<Object> getSet(String key) |
Set集合獲取 |
|
10 |
void addZSet(String key, Object value, double scoure) |
ZSet有序集合添加 |
|
11 |
Set<Object> getZSet(String key, double scoure, double scoure1) |
ZSet有序集合獲取 |
|
12 |
boolean expire(String key,long time) |
指定某key的緩存有效時間 |
|
13 |
long getExpire(String key) |
獲取指定key的過期時間 |
|
14 |
void remove(String... keys) |
刪除一組或單個key的緩存數據 |
|
15 |
boolean exists(String key) |
判斷緩存中是否有對應的value |
|
5. 運行保障
5.1. 監控指標
連接客戶數
阻塞連接數
Redis占用內存
內存峰值
主從角色
master_link_status
執行命令總數和qps
上報時間