jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks

          http://www.tuicool.com/articles/2Ujquyy



          豁達(dá)是正確樂觀的面對(duì)失敗的系統(tǒng)。不需要過多的擔(dān)心,需要一種去說那又怎樣的能力。因此架構(gòu)的設(shè)計(jì)是如此的重要。許多優(yōu)秀的系統(tǒng)沒有進(jìn)一步成長的能力,我們應(yīng)該做的是使用其他的系統(tǒng)去共同分擔(dān)工作。

          Redis 是其中一個(gè)吸引我的系統(tǒng),一個(gè)持久性的,鍵值對(duì)存儲(chǔ)內(nèi)存操作高性能的平臺(tái)。它是一個(gè)優(yōu)秀的鍵值對(duì)數(shù)據(jù)庫。我已經(jīng)在使用了。即使AWS最近宣布開始支持ElasticCache的下級(jí)緩存。但是一個(gè)無主的redis集群仍然起著重要的作用。我們需要多系統(tǒng)去完成工作。同時(shí),我們能夠集合多種組件在一個(gè)容錯(cuò)和無主的集群里共同工作么?在這片文章中我將介紹夢幻般的redis。

          一致哈希

          構(gòu)建一個(gè)存儲(chǔ)數(shù)據(jù)集群的關(guān)鍵是有一個(gè)有效的數(shù)據(jù)存儲(chǔ)和復(fù)制機(jī)制。我希望 通過一個(gè)行之有效的方法來說明建造一個(gè)數(shù)據(jù)集群,在這個(gè) 過程中你可以隨意添加或移除一個(gè)Redis節(jié)點(diǎn),同時(shí)保證你的數(shù)據(jù)仍然存在,而不會(huì)消失。這個(gè)方法稱為一致哈希。

          由于它不是一個(gè)很明顯的概念,我將用一點(diǎn)時(shí)間來解釋一下。為了理解一致哈希,你可以想像有一個(gè)函數(shù)f(x),對(duì)于給定的x總是返回一個(gè)1到60(為什么是60?你會(huì)知道的,但現(xiàn)在請(qǐng)等等)之間的結(jié)果,同樣對(duì)于一個(gè)唯一的x,f(x)總是返回相同的結(jié)果。這些1到60的值按順時(shí)針排成一個(gè)環(huán)。

          現(xiàn)在 集群 中的每個(gè)節(jié)點(diǎn)都需要一個(gè)唯一的名字。所以如果你將這個(gè)名字傳遞給f(''),它將返回一個(gè)1到60之間的數(shù)字(包括1和60),這個(gè)數(shù)字就是節(jié)點(diǎn)在環(huán)上的位置。當(dāng)然它只是節(jié)點(diǎn)的邏輯(記錄的)位置。這樣,你獲得一個(gè)節(jié)點(diǎn),將它傳給哈希函數(shù),獲得結(jié)果并將它放到環(huán)上。是不是很簡單?這樣每個(gè)節(jié)點(diǎn)都在環(huán)上有了它自己的位置。假設(shè)這里有5個(gè)Redis節(jié)點(diǎn),名字分別為'a','b','c','d','e'。每個(gè)節(jié)點(diǎn)都傳給哈希函數(shù)f(x)并且放到了環(huán)上。在這里f('a') = 21, f('b') = 49, f('c') = 11, f('d') = 40, f('e') = 57。一定記得這里位置是邏輯位置。

          那么,我們?yōu)槭裁匆獙⒐?jié)點(diǎn)放在一個(gè)環(huán)上呢?將節(jié)點(diǎn)放到環(huán)上的目的是確定擁有哪些哈希空間。圖中的節(jié)點(diǎn)'d'擁有的哈希空間就是f('a')到f('d')(其值為40)之間的部分,包括f('d'),即(21, 40]。也就是說節(jié)點(diǎn)'d'將擁有鍵x,如果f(x)的屬于區(qū)間(21, 40】。比如鍵‘apple’,其值f('apple') = 35,那么鍵'apple'將被存在'd'節(jié)點(diǎn)。類似的,每個(gè)存儲(chǔ)在集群上的鍵都會(huì)通過哈希函數(shù),在環(huán)上按順時(shí)針方向被恰當(dāng)?shù)卮娴阶罱墓?jié)點(diǎn)。

          雖然一致哈希講完了,但應(yīng)知道,在多數(shù)情況下,這種類型的系統(tǒng)是伴隨著高可用性而構(gòu)建。為了滿足數(shù)據(jù)的高可用性,需要根據(jù)一些因子進(jìn)行復(fù)制, 這些因子稱為復(fù)制因子。假設(shè)我們集群的復(fù)制因子為2,那么屬于'd'節(jié)點(diǎn)的數(shù)據(jù)將會(huì)被復(fù)制到按順時(shí)針方向與之相隔最近的'b'和'e'節(jié)點(diǎn)上。這就保證了如果從'd'節(jié)點(diǎn)獲取數(shù)據(jù)失敗,這些數(shù)據(jù)能夠從'b'或'e'節(jié)點(diǎn)獲取。

          不僅僅是鍵使用一致哈希來存儲(chǔ),也很容易覆蓋失敗了的節(jié)點(diǎn),并且復(fù)制因子依然完好有效。比如'd'節(jié)點(diǎn)失敗了,'b'節(jié)點(diǎn)將獲取'd'節(jié)點(diǎn)哈希空間的所有權(quán),同時(shí)'d'節(jié)點(diǎn)的哈希空間能夠很容易地復(fù)制到'c'節(jié)點(diǎn)。

          壞事和好事

          壞事就是目前這些討論過的所有概念,復(fù)制(冗余),失效處理以及集群規(guī)模等,在Redis之外是不可行的。一致哈希僅僅描述了節(jié)點(diǎn)在哈希環(huán)上的映射以及那些哈希數(shù)據(jù)的所有權(quán),盡管這樣,它仍然是構(gòu)建一個(gè)彈性可擴(kuò)展系統(tǒng)的極好的開端。

          好事就是,也有一些分立的其他工具在Redis集群上實(shí)現(xiàn)一致哈希,它們能提醒節(jié)點(diǎn)失效和新節(jié)點(diǎn)的加入。雖然這個(gè)功能不是一個(gè)工具的一部分,我們將看到如何用多個(gè)系統(tǒng)來使一個(gè)理想化的Redis集群運(yùn)轉(zhuǎn)起來。

          Twemproxy aka Nutcracker

          Twemproxy是一個(gè)開源工具,它是一個(gè)基于memcached和Redis協(xié)議的快速、輕量的代理。其本質(zhì)就是,如果你有一些 Redis服務(wù)器 在運(yùn)行,同時(shí)希望用這些服務(wù)器構(gòu)建集群,你只需要將Twemproxy部署在這些服務(wù)器前端,并且讓所有Redis流量都通過它。

          Twemproxy除了能夠代理Redis流量外,在它存儲(chǔ)數(shù)據(jù)在Redis服務(wù)器時(shí)還能進(jìn)行一致哈希。這就保證了數(shù)據(jù)被分布在基于一致哈希的多個(gè)不同Redis節(jié)點(diǎn)上。

          但是Twemproxy并沒有為Redis集群建立高可用性支持。最簡單的辦法是為集群中的每個(gè)節(jié)點(diǎn)都建立一個(gè)從(冗余)服務(wù)器,當(dāng)主服務(wù)器失效時(shí)將從(冗余)服務(wù)器提升為主服務(wù)器。為Redis配置一個(gè)從服務(wù)器是非常簡單的。

          這種模型的缺點(diǎn)是非常明顯的,它需要為Redis集群中的每個(gè)節(jié)點(diǎn)同時(shí)運(yùn)行兩個(gè)服務(wù)器。但是節(jié)點(diǎn)失效也是非常明顯,并且更加危險(xiǎn),所以我們?cè)趺粗肋@些問題并解決。

          Gossip on Serf

          Gossip是一個(gè)標(biāo)準(zhǔn)的機(jī)制,通過這個(gè)機(jī)制集群上的節(jié)點(diǎn)可以很清楚的了解成員的最新情況。這樣子集群中的每個(gè)節(jié)點(diǎn)就很清楚集群中節(jié)點(diǎn)的變化,如節(jié)點(diǎn)的新增和節(jié)目的刪除。

          Serf通過實(shí)現(xiàn)Gossip機(jī)制提供這樣的幫助。Serf是一個(gè)基于代理的機(jī)制,這個(gè)機(jī)制實(shí)現(xiàn)了Gossip的協(xié)議達(dá)到節(jié)點(diǎn)成員信息交換的目的。Serf是不斷運(yùn)行,除此之外,它還可以生成自定義的事件

          現(xiàn)在拿我們的節(jié)點(diǎn)集群為例,如果每個(gè)節(jié)點(diǎn)上也有一個(gè)serf代理正在運(yùn)行,那么節(jié)點(diǎn)與節(jié)點(diǎn)之間可以進(jìn)行細(xì)節(jié)交換.因此,群集中的每個(gè)節(jié)點(diǎn)都能清楚知道其他節(jié)點(diǎn)的存在,也能清楚知道他們的狀態(tài)。

          這還并不夠,為了高可靠性我們還需要讓twemproxy知道何時(shí)一個(gè)節(jié)點(diǎn)已經(jīng)失效,這樣的話它就可以據(jù)此修改它的配置。像前面提到的Serf,就可以做到這一點(diǎn),它是基于一些gossip觸發(fā)的事件,使用自定義動(dòng)作實(shí)現(xiàn)的。因此只要集群中的一個(gè)Redis節(jié)點(diǎn)因?yàn)橐恍┰蝈吹袅耍硪粋€(gè)節(jié)點(diǎn)就可以發(fā)送有成員意外掉線的消息給任何給定的端點(diǎn),這個(gè)端點(diǎn)在我們的案例中也就是twemproxy服務(wù)器。

          這還不是全部

          現(xiàn)在我們有了Redis集群,基于一致性哈希環(huán),相應(yīng)的是用twemproxy存儲(chǔ)數(shù)據(jù)(一致性哈希),還有Serf,它用gossip協(xié)議來檢測Redis集群成員失效,并且向twemproxy發(fā)送失效消息;但是我們還沒有建立起理想化的Redis集群。

          消息偵聽器

          雖然Serf可以給任何端點(diǎn)發(fā)送成員離線或者成員上線消息。然而twemproxy卻沒有偵聽此類事件的機(jī)制。因此我們需要自定義一個(gè)偵聽器,就像Redis-Twenproxy代理,它需要做以下這些事情。

          • 偵聽Serf消息
          • 更新nutcraker.yml 以反映新的拓?fù)?/li>
          • 重啟twemproxy

          這個(gè)消息偵聽器可以是一個(gè)小型的http服務(wù)器;它在收到一批POST數(shù)據(jù)的時(shí)候,為twemproxy做以上列表中的動(dòng)作。需要記住的是,這種消息應(yīng)該是一個(gè)原子操作;因?yàn)楫?dāng)一個(gè)節(jié)點(diǎn)失效(或者意外離線)的時(shí)候,所有能發(fā)消息的活動(dòng)節(jié)點(diǎn)都將發(fā)送這個(gè)失效事件消息給偵聽器;但是偵聽器應(yīng)該只響應(yīng)一次。

          數(shù)據(jù)復(fù)制

          在上面的“一致哈希”中,我提到了Redis集群中的復(fù)制因素。同樣它也不是Twemproxy的固有特性;Twemproxy只關(guān)心使用一致哈希存儲(chǔ)一個(gè)拷貝。所以在我們追求理想化Redis集群的過程中,我們還需要給twemproxy或者redis自己創(chuàng)建這種復(fù)制的能力。

          為了給Twenproxy創(chuàng)建復(fù)制能力,需要將復(fù)制因子作為一個(gè)配置項(xiàng)目,并且將數(shù)據(jù)保存在集群中相鄰的redis節(jié)點(diǎn)(根據(jù)復(fù)制因子)。由于twemproxy知道節(jié)點(diǎn)的位置,所以這將給twemproxy增加一個(gè)很棒的功能。

          由于twemproxy只不過是代理服務(wù)器,它的簡單功能就是它強(qiáng)大的地方,為它創(chuàng)建復(fù)制管理功能將使它臃腫膨脹。

          Redis 主從環(huán)

          在思考這其中的工作機(jī)制的時(shí)候,我忽然想到,為什么不將每個(gè)節(jié)點(diǎn)設(shè)置成另一個(gè)節(jié)點(diǎn)的副本,或者說從節(jié)點(diǎn),并由此而形成一個(gè)主從環(huán)呢?

          這樣的話如果一個(gè)節(jié)點(diǎn)失效了,失效節(jié)點(diǎn)的數(shù)據(jù)在這個(gè)環(huán)上相鄰的節(jié)點(diǎn)上仍然可以獲得。而那個(gè)具有該數(shù)據(jù)副本的節(jié)點(diǎn),將作為該節(jié)點(diǎn)的從節(jié)點(diǎn),并提供保存數(shù)據(jù)副本的服務(wù)。這是一個(gè)既是主節(jié)點(diǎn)也是從節(jié)點(diǎn)的環(huán)。Serf仍像通常一樣作為散布節(jié)點(diǎn)失效消息的代理。但是這一回,我們twenproxy上的的客戶端,即偵聽器,將不僅僅只更新twenproxy上的失效信息,還要調(diào)整redis服務(wù)器群集來適應(yīng)這個(gè)變化。

          在這個(gè)環(huán)中有一個(gè)明顯的,同樣也是技術(shù)方面的缺陷。這個(gè)明顯的缺陷是,這個(gè)環(huán)會(huì)壞掉,因?yàn)閺墓?jié)點(diǎn)的從節(jié)點(diǎn)無法判別哪一個(gè)是它的主節(jié)點(diǎn)的數(shù)據(jù),哪一個(gè)是它的主節(jié)點(diǎn)的主節(jié)點(diǎn)的數(shù)據(jù)。這樣的話就會(huì)循環(huán)的傳送所有的數(shù)據(jù)。

          同樣技術(shù)性的問題是,一旦redis將主節(jié)點(diǎn)的數(shù)據(jù)同步給從節(jié)點(diǎn),它就會(huì)將從節(jié)點(diǎn)的數(shù)據(jù)擦除干凈;這樣就將曾經(jīng)寫到從節(jié)點(diǎn)的所有數(shù)據(jù)刪除了。這種主從環(huán)顯然不能實(shí)際應(yīng)用,除非修改主從環(huán)的復(fù)制機(jī)制以適應(yīng)我們的需求。這樣的話每個(gè)從節(jié)點(diǎn)就不會(huì)將它的主節(jié)點(diǎn)的數(shù)據(jù)同步給它的從節(jié)點(diǎn)。要想實(shí)現(xiàn)這一點(diǎn),必須的條件是每個(gè)節(jié)點(diǎn)都可以區(qū)分出自己的密鑰空間,以及它的主節(jié)點(diǎn)的密鑰空間;這樣的話它就不會(huì)將主節(jié)點(diǎn)的數(shù)據(jù)傳送給它的從節(jié)點(diǎn)。

          那么這樣一來,當(dāng)一個(gè)節(jié)點(diǎn)失效時(shí),就需要執(zhí)行四個(gè)動(dòng)作。一,將失效節(jié)點(diǎn)的從節(jié)點(diǎn)作為它的密鑰空間的所有者。二,將這些密鑰散布給失效節(jié)點(diǎn)從節(jié)點(diǎn)的從節(jié)點(diǎn),以便進(jìn)行復(fù)制。三,將失效節(jié)點(diǎn)的從節(jié)點(diǎn)作為失效節(jié)點(diǎn)的主節(jié)點(diǎn)的從節(jié)點(diǎn)。最后,在新的拓?fù)渖蠌?fù)位twemproxy。

          如何理想化?

          事實(shí)上并沒有這樣的Redis集群,它可以具有一致性的哈希,高可靠性以及分區(qū)容錯(cuò)性。因此最后一幅圖片描繪了一種理想化的Redis集群;但這并不是不可能的。接下來將羅列一下需要哪些條件才能使之成為一個(gè)實(shí)實(shí)在在的產(chǎn)品。

          透明的Twenproxy

          有必要部署一個(gè)Twenproxy,這會(huì)使得Hash散列中Redis各節(jié)點(diǎn)的位置都是透明的。每個(gè)Redis節(jié)點(diǎn)都可以知道自己以及其相鄰節(jié)點(diǎn)的位置,這些信息對(duì)于節(jié)點(diǎn)的主從復(fù)制以及失敗節(jié)點(diǎn)的修復(fù)是有必要的。自從Twenproxy開源之后,節(jié)點(diǎn)的位置信息可以被修改、以及擴(kuò)展。

          Redis的數(shù)據(jù)擁有權(quán)

          這是比較困難的部分的,每個(gè)Redis節(jié)點(diǎn)都應(yīng)該記錄自身擁有的數(shù)據(jù),以及哪些是主節(jié)點(diǎn)的數(shù)據(jù)。當(dāng)前這樣的隔離是不可能的。這也需要修改Redis的基礎(chǔ)代碼,這樣節(jié)點(diǎn)才知道何時(shí)與從節(jié)點(diǎn)同步,什么時(shí)候不需要。

          綜上所述,我們的理想化的Redis集群變成現(xiàn)實(shí)需要修改這樣的兩個(gè)組件。一直以來,它們都是非常大的工業(yè)級(jí)別,使用在生產(chǎn)環(huán)境中。這已經(jīng)值得任何人去實(shí)現(xiàn)這個(gè)集群了。

          posted on 2016-12-15 15:45 jinfeng_wang 閱讀(113) 評(píng)論(0)  編輯  收藏 所屬分類: 2016-REDIS
          主站蜘蛛池模板: 东源县| 牟定县| 乌恰县| 平阳县| 临夏县| 东丰县| 晴隆县| 资中县| 石首市| 湘潭县| 永平县| 台前县| 马鞍山市| 临海市| 甘孜| 沐川县| 闻喜县| 临湘市| 梨树县| 金乡县| 巴彦淖尔市| 瑞丽市| 呼伦贝尔市| 西林县| 新竹县| 博乐市| 呼玛县| 启东市| 罗江县| 安宁市| 黄陵县| 遵义县| 堆龙德庆县| 香河县| 大厂| 砚山县| 隆回县| 濮阳市| 鄢陵县| 长兴县| 北川|