同程旅游缓存系统(凤凰)打造Redis时代的完美平台实践
https://blog.csdn.net/qiansg123/article/details/80128077
緩存大家比較熟悉,在各種場景下也用的很多,在同程旅游也一樣,緩存是一個無處不在的精靈,也是抗并發壓力的核心系統之一。今天我們來講一下同程旅游在緩存方面的一些經驗,包括整個緩存架構如何設計。先來看一下緩存我們走過了哪些歷程。最早我們在應用中使用內存對象來存放變化比較小數據,慢慢的發現在應用本地的內存量是不足以這樣折騰的是精貴的,所以開始使用從 memcache 來將緩存從應用中分離出來。之后緩存的場景變的更多了,memcache的一些不足之處開始顯現,如支持的數據結構有單一等。于是又開始使用Redis,對于Redis的使用也從單機開始轉向分片/集群的方式。Redis雖然是一個非常優秀的緩存中間件系統但當應用的使用量越來越多時發現新的問題也來了,首先是對于在應用代碼在一起的Redis客戶端不是太滿意了,于是我們重寫了客戶端。接著在數千個Redis實例(目前我們線上有4千多個部署實例)在線上工作起來如何底成本的高效的運維起是一個大挑戰。于是我們做了針對他的智能化運維平臺。但是這些多不是重大的難題,最大難題是因為Redis太好用了于致于在應用項目中大量的使用,但是往往這些是沒有好好的思考和設計過的,這樣情況使的緩存的使用是混亂的不合理的,也使的原本健壯的緩存變的脆弱的病秧子。所以我們開始做了開發 Redis 調度治理系統來治理緩存。到現在我們開始更加關注整個緩存平臺的高效和低成本所以在平臺的Redis 部署全面 Docker 化,并開始讓比內存廉價的ssd硬盤在緩存中使用起來。
現在讓我們來看一看這個名叫“鳳凰”的緩存平臺是怎么從火中重生的。
一、Redis 遍地開花的現狀及問題
1. Redis 集群的管理
所有互聯網的應用里面,可能訪問最多的就是 cache。一開始時候一些團隊認為 cache 就像西游記里仙丹一樣,當一個系統訪問過大扛不住時,用一下 Redis,系統壓力就解決了。在這種背景下,我們的系統里面 Redis 不斷增多,逐漸增加到幾百臺服務器,每臺上面還有多個實例,因此當存在幾千個 Redis 實例后,使用者也很難說清楚哪個 Redis在哪個業務的什么場景下影響什么功能。
2. 故障
這種背景下會存在什么樣痛苦的場景?正常情況下 cache 是為了增加系統的性能,是畫龍點睛的一筆,但是當時我們 cache 會是什么樣?它掛了就可能讓我們整個系統崩潰。比如說 系統壓力才不到高峰時的 10%,也許就由于緩存問題系統就掛了,又比如系統的訪問量不大,但某個緩存被調用到爆了,因為緩存的亂用后調用量被放大了。
3. 高可用與主從同步問題
因為 cache 有單點,我們一開始想放兩份不就好了嗎,所以就做了主從。這時候坑又來了,為什么呢?比如有些 Redis 實例中的某個鍵值非常大(在亂的場景下我見到過有人一個鍵值放到10G的)。在當偶爾網絡質量不太好時候,就會帶來主從同步基本就別想了,更坑的是當兩邊主和從都死或者出問題的時,重啟的時間非常長。
4. 監控
為了高可用,我們需要全面的監控。當時我們做了哪些監控呢?其實是能做的監控我們多做了,但問題沒有解決,細想來問題到底在哪??
下面是一個接近真實場景運維與開發的對話場景。
- 開發:Redis 為啥不能訪問了?
- 運維:剛剛服務器內存壞了,服務器自動重啟了?
- 開發:為什么 Redis 延遲這么大?
- 運維:不要在 Zset 里放幾萬條數據,插入排序會死人啊
- 開發:寫進去的 key 為什么不見了?
- 運維:Redis 超過最大大小了啊,不常用 key 都丟了啊
- 開發:剛剛為啥讀取全失敗了
- 運維:網絡臨時中斷了一下,從機全同步了,在全同步完成之前,從機的讀取全部失敗
- 開發:我需要 800G 的 Redis,什么時候能準備好?
- 運維:線上的服務器最大就 256G,不支持這么大
- 開發:Redis 慢得像驢,服務器有問題了?
- 運維:千萬級的 KEY,用 keys*,慢是一定了。
看到這樣的一個場景很吃驚,我們怎么在這樣用緩存,因此我們一個架構師最后做了以下總結
“從來沒想過,一個小小的 Redis 還有這么多新奇的功能。就像在手上有錘子的時候,看什么都是釘子。漸漸的,開發規范倒是淡忘了,新奇的功能卻接連不斷的出現了,基于 Redis 的分布式鎖、日志系統、消息隊列、數據清洗等,各種各樣的功能不斷上線,從而引發各種各樣的問題。運維天天疲于奔命,到處處理著 Redis 堵塞、網卡打爆、連接數爆表……”
總結了一下,我們之前的緩存存在哪些問題?
- 使用的者的亂用、爛用、懶用。
- 運維一個幾百臺毫無規則的服務器
- 運維不懂開發,開發不懂運維
- 緩存在無設計無控制中被使用
- 開發人員能力各不相同
- 使用太多的服務器
- 懶人心理(應對變化不夠快)
二、我們需要一個什么樣的完美緩存系統?
我相信上面這些情況在很多大量使用 Redis 的團隊中都存在,如果發展到這樣一個階段后,我們到底需要一個什么樣的緩存?
我們給自己提出幾個要點:
- 服務規模:支持大量的緩存訪問,應用對緩存大少需求就像貪吃蛇一般
- 集群可管理性:一堆孤島般的單機服務器緩存服務運維是個迷宮
- 冷熱區分:現在緩存中的數據許多并不是永遠的熱數據
- 訪問的規范及可控:還有許多的開發人員對緩存技術了解有限,胡亂用的情況很多
- 在線擴縮容:起初估算的不足到用時發現瓶頸了
這個情況下,我們考慮這樣的方案是不是最好。本來我們是想直接使用某個開源方案就解決了,但是我們發現每個開源方案針對性的解決 Redis 上述痛點的某一些問題,每一個方案在評估階段跟我們需求都沒有 100% 匹配。每個開源方案本身都很優秀,也許只是說我們的場景的特殊性,沒有任何否定的意思。
下面我們當時評估的幾個開源方案,看一下為什么當時沒有引入。
- CacheCloud:跟我們需要的很像,它也做了很多的東西,但是它對我們不滿足是部署方案不夠靈活,對運維的策略少了點。
- Codis:這個其實很好,當年我們已經搭好了準備去用了,后來又下了,因為之前有一個業務需要 800G 內存,后來我們發現這個大集群有一個問題,因為用得不是很規范,如果在這種情況下給他一個更大的集群,那我們可能死的機率更大,所以我們也放棄了。另外像這個800G 也很浪費,并不完全都是熱數據,我們想把它存到硬盤上一部分,很多業務使用方的心理是覺得在磁盤上可能會有性能問題,還是放在 Redis 放心一點,其實這種情況基本不會出現,因此我們需要一定的冷熱區分支持。
- Pika:?Pika 可以解決上面的大量數據保存在磁盤的問題,但是它的部署方案少了點,而且 Pika 的設計說明上也表示主要針對大的數據存儲。
- Twemproxy:最后我們想既然直接方案不能解決,那可以考慮代理治理的方式,但是問題是它只是個代理,Redis 被濫用的問題還是沒有真正的治理好,所以后面我們準備自己做一個。
三、全新設計的緩存系統——鳳凰
我們新系統起了一個比較高大上的名字,叫鳳凰,愿景是鳳凰涅磐,從此緩存不會再死掉了。那么,鳳凰是怎么設計的?(如:圖1)
主要是做好了下面的幾件事:
?
圖1: 鳳凰緩存平臺的基礎設計?
1. 自定義客戶端方式與場景配置能力
在支持 Redis 本身的特性的基礎上,我們需要通過自定義的客戶端來實現一些額外的功能。
支持場景配置,我們考慮根據場景來管控它的場景,客戶端每次用 Redis 的時候,必須把場景上報給我,你是在哪里,用這件事兒是干什么的,雖然這個對于開發人員來說是比較累的,他往往嵌在它的任務邏輯里面直接跟進去。曾江場景配置之后,在緩存服務的中心節點,就可以把它分開,同一個應用里面兩個比較重要的場景就會不用同一個 Redis,避免掛的時候兩個一起掛。
同時也需要一個調度系統,分開之后,不同的 Redis 集群所屬的服務器也需要分開。分開以后我的數據怎么復制,出問題的時候我們怎么把它遷移?因此也需要一個復制和遷移的平臺去做。
另外這么一套復雜的東西出來之后,需要一個監控系統;客戶端里也可以增加本地 cache 的支持。在業務上也可能需要對敏感的東西進行過濾。在底層,可以自動實現對訪問數據源的切換,對應用是透明的,應用不需要關心真正的數據源是什么,這就是我們自己做的客戶端。(如:圖2)
?
圖2: 鳳凰緩存平臺客戶端的結構圖?
2. 代理層方式
客戶端做了之后還發生一個問題,很多情況下很難升級客戶端。再好的程序員寫出來的東西還是有 bug,如果 Redis 組件客戶端發現了一個 bug 需要升級,但我們線上有幾千個應用分布在多個業務開發團隊,這樣導致很難驅動這么多開發團隊去升級。另外一個我們獨有的困難,就是我們還有一些很老的應用開發時用的 .net,這些雖然現在基本是邊緣應用了但還在線上,當然我們也把客戶端實現了 .net 版本,但是由于各種原因,要推動這么多歷史業務進行改造切換非常麻煩,甚至有些特別老的業務最后沒法升級。另外我們也推進其(如:go等)其它語言的應用想讓技術的生態做的更豐富。這樣一來我們做維護更多語言的客戶端了,這樣的方式明顯不合適。
因此我們考慮了 proxy 方案,這些業務模塊不需要修改代碼,我們的想法就是讓每一個項目的每一個開發者自己開發的代碼是干凈的,不要在他的代碼里面嵌任何的東西,業務訪問的就是一個 Redis。那么我們就做了,首先它是 Redis 的協議,接下來剛才我們在客戶端里面支持的各種場景配置錄在 proxy 里面,實現訪問通道控制。然后再把 Redis 本身沉在我們 proxy 之后,讓它僅僅變成一個儲存的節點,proxy 再做一些自己的事情,比如本地緩存及路由。冷熱區分方面,在一些壓力不大的情況下,調用方看到的還是個 Redis ,但是其實可能數據是存在 RocksDB 里面了。(如:圖3)
?
圖3: 鳳凰緩存平臺的代理設計?
3. 緩存服務的架構設計
- 多個小集群 + 單節點,我們要小集群的部署和快速的部署,到當時一個集群有問題的時候,快速移到另一個集群。
- 以場景劃分集群
- 實時平衡調度數據
- 動態擴容縮容
?
圖4:以Docker的基礎的鳳凰緩存平臺架構設計?
4. 可擴容能力
- 流量的快速增加必然帶擴容的需求與壓力
- 容量與流量的雙擴容
- 如何做到平滑的擴容?容量動態的數據遷移(集群內部平衡,新節點增加);流量超出時的根據再平衡集群。
?
圖5: 鳳凰緩存平臺的平滑的擴容過程?
5. 多協議支持
還有一塊老項目是最大的麻煩,同程有很多之前是用 memcache 的應用,后來是轉到 Redis 去的,但是轉出一個問題來了,有不少業務由于本身事情較多,沒有時間轉換成 Redis,這些釘子戶怎么辦?同時維護這兩個平臺是非常麻煩的,剛才 proxy 就派到用場了。因為 memcache 本身它的數據支持類型是比較少的,因此轉換比較簡單,如果是一個更復雜的類型,那可能就轉不過來了。所以我們 proxy 就把這些釘子戶給拆掉了,他覺得自己還是在用 memcache,其實已經被轉成了 Redis。
?
圖6: 鳳凰緩存平臺的多協議支持設計)?
6. 管理與可監控能力
最后一塊,我們這樣一個平臺怎么去監控它,和怎么去運維它?
在這塊我們更多的是在做一個智能化的運維平臺。主要的幾點方向:
- 整體的管制平臺,
- 運維操作平臺,讓它可以去操作,動態的在頁面上操作做一件事情,
- 整體監控平臺。我們從客戶端開始,到服務器的數據,全部把它監控起來。
- 自擴容自收縮。動態的自擴容,自收縮。
7. 一些業務應用場景
也是用了一些場景,比如說同程前兩年沖的比較狠的就是一元門票,大家肯定說搶購,這個最大的壓力是什么,早上的九點半,這是我們系統最大的壓力,為什么呢,一塊錢的門票的從你買完票到景區里面去,這件事情是在九點半集中爆發的,你要說這個是系統掛了入不了園了,那十幾萬人不把這個景區打砸了才怪。那個時候系統絕對不能死。搶購沒有關系,入園是我們最大的壓力,
我們是靠新的系統提供了訪問能力及可用性的支持,把類似這種場景支撐下來,其實緩存本身是可以支撐住的,但是如果濫用管理失控,可能碰到這種高可用的需求就廢了。
還有一個是火車票系統,火車票查詢量非常大,我們這主要是用了新系統的收縮容,到了晚上的時候查的人不多了,到了早上的時候特別多,他查詢量是在一個高低跌蕩的,所以我們可以根據訪問的情況來彈性調度。
轉載于:https://www.cnblogs.com/davidwang456/articles/9254130.html
總結
以上是生活随笔為你收集整理的同程旅游缓存系统(凤凰)打造Redis时代的完美平台实践的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis 如何分布式,来看京东金融的设
- 下一篇: 如何基于Redis Replicatio