天天看點

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

自從cyq.data架構出了資料庫讀寫分離、分布式緩存memcache、自動緩存等大功能之後,就進入了頻繁的細節打磨優化階段。

從以下的更新清單就可以看出來了,3個月更新了100條次功能:

<a href="http://www.cnblogs.com/cyq1162/p/6013140.html#">+ view code</a>

其實更多的時間,是放在asp.net aries 業務開發架構上,上裡下外全部重構了一遍。

前幾天,決定把redis內建進來,一鼓作氣,解決了。

下面分享一下經曆:

一開始我是拒絕的,不願動态調用第三方的用戶端(關聯依賴的dll太多)。

最近打算支援redis,有點妥協了,動态加載就動态加載了吧:

考慮着引入:stackexchange.redis或servicestack.redis?

看着這些dll,太重量級,方法反射起來也費勁!

中間思維停頓了一會。。。

在尋找redis的api資料時,無意發現了這個開源的輕量級bettle.redis。

看到源碼編繹後才46k,感覺就是它了。

不過才幾刻間,發現了以下幾個問題了:

1:自身雖然46k,但代碼引用了另外兩個3個dll(依賴太多):

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

2:使用的方法不符合使用習慣,一個指令類型就對應一個類。

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

3:不支援叢集的水準擴充(沒實作支援一緻性hash)。

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

4:代碼是用.net 4.0 以下版本寫的,(cyq.data 架構是支援2.0起的,改代碼改到我手痛)

是以,以上原因估計是它沒被普及的原因,也是最終沒有被我選擇內建的原因。

但是它開放了源碼、對我還是有點啟發和參考意義。

在決定支援redis的過程中,花了不少時間掃了redis的文檔:

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

更多指令詳情可以看:http://doc.redisfans.com

從這麼一堆的指令中,找到基本指令:get、set、exists、expire、info,可憐沒有add。

其它的指令,多數都是可以用基本指令實作的,就被無視了。

經過短時間内大量的集中思考,決定自己實作了:

架構之前已經內建了memcache,而redis和memcache又大同小異。

一些共性的東西,可以複用:

1:hash算法。

2:一緻性hash(水準擴充)。

3:socketpool。

4:serverpool。

5:序列化(壓縮)

剩下的,就是完成socket和redis的互動及使用方式。

以下是redis的協定規範,不過是我實作redis相關功能後才發現的:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

<code>     </code><code>協定規範</code>

<code>redis允許用戶端以tcp方式連接配接,預設6379端口。傳輸資料都以\r\n結尾。</code>

<code>請求格式</code>

<code>*&lt;number </code><code>of</code> <code>arguments&gt;\r\n$&lt;number </code><code>of</code> <code>bytes </code><code>of</code> <code>argument 1&gt;\r\n&lt;argument data&gt;\r\n</code>

<code>例:*1\r\n$4\r\ninfo\r\n</code>

<code>響應格式</code>

<code>1:簡單字元串,非二進制安全字元串,一般是狀态回複。  +開頭,例:+ok\r\n</code>

<code>2: 錯誤資訊。          -開頭, 例:-err unknown command </code><code>'mush'</code><code>\r\n</code>

<code>3: 整型數字。                            :開頭, 例::1\r\n</code>

<code>4:大塊回複值,最大512m。           $開頭+資料長度。 例:$4\r\mush\r\n</code>

<code>5:多條回複。                           *開頭, 例:*2\r\n$3\r\nfoo\r\n$3\r\nbar\r\n</code>

bettle.redis裡有源碼,看看實作就可以了,是以沒找協定規範:

通過幾個小時的引進和代碼調整,測試。

以為大功告成之際,測試到當set的資料太大時,networkstream報異常:此流不支援seek操作。

懷疑是redis的set有大小限制?:用bettle.redis自身試了下,發現正常,夢b了。

經代碼調試,發現bettle的socket實作(socket.send)和socket池的實作(networkstream.write)不一樣。

bettle.redis是把所有的協定構造好一次性socket.send(byte[])。

懷疑networkstream的預設緩存池太小引發的?:用memcache,set了大量的資料,發現networkstream并沒有抛異常,又夢b了。

懷疑是redis協定的問題了?:改造代碼,把協定分拆,先發送:$長度 ,再發送資料,發現竟然正常了,無語問蒼天了!

經過一夜一天的折騰,cache目錄下補了4個類,同時進行了算法優化,清掉一些沒用的代碼。

支援redis後,發現cyq.data.dll的大小竟然沒變化,結果超出了預期,很好!

最後改造成的源碼結構是:

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:
CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:
CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

結果:

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

由于redis的get隻支援字元串,為了達到支援任意類型,我必須改進算法:

1:存檔:目标是對象時=》進行序列化(對于&gt;128k的會進行壓縮)

2:資料的第1個位元組:存檔資料類型。

3:擷取資料時:根據第1個位元組,進行準确的資料類型還原。

(aaa是通過指令行set的,而a0是通過代碼設定的,是以多了\x02的類型辨別)

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

是以:架構靠set與get能支援任意類型的存取檔!

内部已經實作了一緻性hash算法,是以省了不少工作:

簡單的描述為:把ip1産生n個hash ,ip2産生n個hash,... 然後排序(最後就看key的hash值離誰最新就粘誰了)

借用一張圖表示為:

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

在測試的過程中,我填寫了一台異常的主機,發現被配置設定到異常的主機的key的讀寫都沒反應了:

(我潛意識預設以為會自動轉移到相鄰的主機中)

1:沒有自動切換相鄰的主機【用思考代碼疑問:主動切換可能導緻雪崩效應,(累積的壓力可能把所有的伺服器都搞挂)】。

2:有重試連接配接機制(2分鐘試1次)。

3:當主機恢複時,從備份機裡恢複資料,并清空備份機的資料(未實作)

由于可能同時挂掉n台,是以備份機可能存檔多台主機的資訊。

于是算法的思路有3個:

至此,cyq.data已經支援上redis了,而且在分布式算法上,借了memcache的風,以及改進的算法,顯的更為實用!

當然,細節仍需打磨,代碼還可以改的更簡潔優美。

在分布式已經泛濫的今天,能正确的判斷并用好分布式架構是一種能力的展現。

剛剛群裡有人發了這條消息:

CYQ.Data V5 分布式緩存Redis應用開發及實作算法原理介紹前言:最初的想法:發現輕量級:Bettle.RedisRedis API 掃盲:重新定位的思路:折騰的經過:Redis使用方式:對于存儲類型的改進:對于分布式算法的改進:總結:

其實前面的問題都可以無視,因為最後解決方案他隻是把redis部署從windows轉移到linux就好了。

qps最大時聽說7萬多(兩台web分來就是3萬多,大部分是刷票造成的請求)

redis在windows上的表現并不如linux的好,這個可以了解。

但是如果在架構設計方案上稍為調整,其實也毫無壓力了。

最後我發現問題的根源不在于技術,在于人:.net缺少有足夠知識和思維的架構師。

不要遇到點問題就力不從心,在.net的陣營上堅持吧,少年!

本文原創發表于部落格園,作者為路過秋天,原文連結:http://www.cnblogs.com/cyq1162/p/6013140.html

繼續閱讀