17010三、Redis官方集羣方案 Redis Cluster

前面咱們談了Redis Sharding多服務器集羣技術,Redis Sharding是客戶端Sharding技術,對於服務端來講,各個Redis服務器彼此是相互獨立的,這對於服務端根據須要靈活部署Redis很是輕便,Redis Sharding具備很好的靈活性、可伸縮性,是一種輕量級集羣技術。node

 

本篇,介紹另一種多Redis服務器集羣技術,即Redis Cluster。Redis Cluster是一種服務器Sharding技術,3.0版本開始正式提供。redis

 

Redis Cluster中,Sharding採用slot(槽)的概念,一共分紅16384個槽,這有點兒相似前面講的pre sharding思路。對於每一個進入Redis的鍵值對,根據key進行散列,分配到這16384個slot中的某一箇中。使用的hash算法也比較簡單,就是CRC16後16384取模。算法

 

Redis集羣中的每一個node(節點)負責分攤這16384個slot中的一部分,也就是說,每一個slot都對應一個node負責處理。當動態添加或減小node節點時,須要將16384個槽作個再分配,槽中的鍵值也要遷移。固然,這一過程,在目前實現中,還處於半自動狀態,須要人工介入。spring

 

Redis集羣,要保證16384個槽對應的node都正常工做,若是某個node發生故障,那它負責的slots也就失效,整個集羣將不能工做。瀏覽器

 

爲了增長集羣的可訪問性,官方推薦的方案是將node配置成主從結構,即一個master主節點,掛n個slave從節點。這時,若是主節點失效,Redis Cluster會根據選舉算法從slave節點中選擇一個上升爲主節點,整個集羣繼續對外提供服務。這很是相似前篇文章提到的Redis Sharding場景下服務器節點經過Sentinel監控架構成主從結構,只是Redis Cluster自己提供了故障轉移容錯的能力。ruby

 

Redis Cluster的新節點識別能力、故障判斷及故障轉移能力是經過集羣中的每一個node都在和其它nodes進行通訊,這被稱爲集羣總線(cluster bus)。它們使用特殊的端口號,即對外服務端口號加10000。例如若是某個node的端口號是6379,那麼它與其它nodes通訊的端口號是16379。nodes之間的通訊採用特殊的二進制協議。服務器

 

對客戶端來講,整個cluster被看作是一個總體,客戶端能夠鏈接任意一個node進行操做,就像操做單一Redis實例同樣,當客戶端操做的key沒有分配到該node上時,Redis會返回轉向指令,指向正確的node,這有點兒像瀏覽器頁面的302 redirect跳轉。架構

 

Redis Cluster是Redis 3.0之後才正式推出,時間較晚,目前能證實在大規模生產環境下成功的案例還不是不少,須要時間檢驗。app

 

 

集羣搭建R工具

 

下面,咱們就實際操做創建一個Redis Cluster。咱們將創建3個node,每一個node架構成一主一從,故總共有6個redis實例,端口號從7000-7005。

 

Cluster下的Redis實例,和普通Redis實例同樣,只是處於cluster模式方式下運行。

 

實操步驟以下:

 

1. 以3.0.5爲例,創建cluster目錄,其下再建6個子目錄表明6實例環境

 

mkdir cluster

mkdir 7000 7001 7002 7003 7004 7005

 

2. 將redis.conf模板分別copy到上面6子目錄中,並作以下修改,以7000爲例:

 

修改以下信息

daemonize yes

pidfile /var/run/redis-7000.pid

port 7000

logfile "/var/log/redis-7000.log"

 

註釋掉以下信息, 不須要RDB持久化

#save 900 1

#save 300 10

#save 60 10000

 

修改以下信息

appendonly yes

appendfilename "appendonly-7000.aof"

 

取消以下注釋,讓Redis在集羣模式下運行

cluster-enabled yes 啓動cluster模式

cluster-config-file nodes-7000.conf 集羣信息文件名,由redis本身維護

cluster-node-timeout 15000 15秒中聯繫不到對方node,即認爲對方有故障可能

 

3. 在各個目錄下執行 redis-server redis.conf 啓動redis實例

 

這時,這幾個實例都是各自是一個集羣狀態在運行,並無造成一個總體集羣態,咱們須要Redis提供的基於ruby開發的工具進行人工設置。

 

4.安裝ruby環境

 

yum install ruby ruby-devel rubygems

 

5.安裝Redis的ruby依賴接口

 

gem install redis

 

6.利用腳本工具創建集羣

 

./redis-trib.rb create --replicas 1 192.168.1.142:7000 192.168.1.142:7001 192.168.1.142:7002 192.168.1.142:7003 192.168.1.142:7004 192.168.1.142:7005

 

該腳本自動執行節點分配方案,將前3個redis實例做爲主節點,後三個做爲從節點,如圖:

 

 

 

按提示敲入"yes",執行方案,將6個節點組成集羣,3主3從。

 

執行redis-cli -p 7000 info Replication 命令,觀察7000這個節點,發現其複製配置信息已配置成主節點,並有一個從節點7003

 

 

再執行redis-cli -p 7003 info Replication發現,該節點已設置成從節點。這些主從設置,都是在建立集羣時自動完成的。

 

 

至此,3主3從的Redis集羣創建起來了。接下來咱們作個故障轉移試驗,將主節點7001 shutdown掉,看看發生什麼?

 

 

咱們看到,從節點7004會上升爲主節點繼續提供集羣服務。那又從新啓動7001節點呢?

 

 

咱們發現7001節點已經成爲從節點,不會成爲取代7004成爲主節點。那若是將主節點7001和從節點7004都shutdown掉呢?

 

 

這時,整個cluster是拒絕提供服務的。由於原來7001分配的slot如今無節點接管,須要人工介入從新分配slots。

 

 

增刪集羣節點R

 

下面咱們操做下,若是修改集羣節點架構:

 

刪除一個從節點。注意,若是刪除主節點,其負責的slots必須爲空。

 

./redis-trib.rb del-node 192.168.1.142:7000 ee2fc0ea6e630f54e3b811caedf8896b26a99cba

 

將7001節點的slot都轉移到7000

 

./redis-trib.rb reshard 192.168.1.142:7000

 

按提示操做便可。

 

加一個節點。注意新添加的節點尚未分配slot,用reshard給它分配必定比例的slots

 

./redis-trib.rb add-node 192.168.1.142:7001 192.168.1.142:7000

 

給指定一個主節點添加一個從節點。

 

./redis-trib.rb add-node --slave --master-id f4d17d56a9dda1a102da7cd799192beff7cba69e 192.168.1.142:7004 192.168.1.142:7000

 

注意若是此redis實例參與過集羣,需先cluster reset 清除重置一下。

 

Jedis客戶端訪問R

 

上面介紹了服務端Redis Cluster搭建過程。下面來看看客戶端如何使用?

 

Java語言的客戶端驅動Jedis是支持Redis Cluster的。咱們具體實操下:

 

1. pom.xml中配置jedis jar包

 

<dependency>

<groupId>redis.clients</groupId>

<artifactId>jedis</artifactId>

<version>2.8.0</version> <!-- 最近升級了 -->

</dependency>

 

2. spring配置文件中配置JedisCluster

 

<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">

<property name="maxTotal" value="4096"/>

<property name="maxIdle" value="200"/>

<property name="maxWaitMillis" value="3000"/>

<property name="testOnBorrow" value="true" />

<property name="testOnReturn" value="true" />

</bean>

 

<bean id = "jedisCluster" class = "redis.clients.jedis.JedisCluster">

<constructor-arg index="0">

<set>

<bean class="redis.clients.jedis.HostAndPort">

<constructor-arg index="0" value="192.168.1.142" />

<constructor-arg index="1" value="7002" />

</bean>

</set>

</constructor-arg>

<constructor-arg index="1" value="2000" type="int"/>

<constructor-arg index="2" value="2" type="int"/>

<constructor-arg index="3" ref="poolConfig"/>

</bean>

 

3.編寫測試代碼

 

@Test

public void basicOpTestForCluster(){

 

long begin = System.currentTimeMillis();

for(int i=0;i<10000; i++){

jedis.set("person." + i + ".name", "frank");

jedis.set("person." + i + ".city", "beijing");

 

String name = jedis.get("person." + i + ".name");

String city = jedis.get("person." + i + ".city");

 

assertEquals("frank",name);

assertEquals("beijing",city);

 

jedis.del("person." + i + ".name");

Boolean result = jedis.exists("person." + i + ".name");

assertEquals(false,result);

 

result = jedis.exists("person." + i + ".city");

assertEquals(true,result);

}

long end = System.currentTimeMillis(); 

System.out.println("total time: " + (end-begin)/1000);

}

相關文章
相關標籤/搜索