1. 程式人生 > >redis叢集環境搭建以及java中jedis客戶端叢集程式碼實現

redis叢集環境搭建以及java中jedis客戶端叢集程式碼實現

    最近專案中用到了redis,所以就找了相關的資料學習了,然後搭建了redis叢集,以及客戶端的java程式碼實現。由於對linux不太熟悉,所以在Linux上搭建redis叢集的時候碰到了很多問題,下面就大概總結下。

redis相關網站:

作業系統:centos 6.3

redis版本:3.0.6

java客戶端版本: jedis 2.7.2

redis客戶端圖形使用者介面:RedisDesktopManager

1.redis服務端叢集搭建步驟:

  1.下載redis安裝包,進行解壓安裝

  2.安裝ruby、rubygems install ruby ,安裝ruby的原因是,在進行叢集的時候,使用的是ruby語言工具實現的,所以在叢集之前首先需要搭建ruby的環境

  3.在上述步驟完成之後,便可以搭建叢集環境,redis提供了兩種叢集搭建方法,執行指令碼方法(安裝包下面的util包中)和手動搭建。

  注意:

  1.在叢集的時候,如果是遠端客戶端訪問redis服務端,那麼在分片的時候,需要使用Ip進行分片,下面會詳細說

  2.在建立每個節點的時候,不要只用redis-server ,使用絕對路徑下的redis-server xxx

  附件:在安裝ruby的時候,需要gemredis,下載地址在下面。

2.客戶端(java):

注意:

1.本文的客戶端使用的是java,官網中對於java客戶端也提供了不少的client,但是本文使用的是官方推薦的jedis。

2.在專案開發中,一般情況下都會用到spring來管理應用,本文也是如此,spring 本身也提供了對redis的整合支援,具體的網址:http://projects.spring.io/spring-data-redis,

 但是好像目前spring-data-redis不提供叢集的功能,所以本文沒有使用它,而是使用了原裝的jedis來進行開發,如果在專案中沒有用到叢集的功能,則可以使用spirng-data-redis。

下面是具體的程式碼實現

1.maven依賴

Java程式碼  收藏程式碼
  1. <dependency>  
  2.     <groupId>redis.clients</groupId>  
  3.     <artifactId>jedis</artifactId>  
  4.     <version>2.7.2</version>  
  5. </dependency>  

2.applicationContext.xml中的配置

Java程式碼  收藏程式碼
  1.    <!-- jedis cluster config -->  
  2.    <bean name="genericObjectPoolConfig" class="org.apache.commons.pool2.impl.GenericObjectPoolConfig" >  
  3.         <property name="maxWaitMillis" value="-1" />  
  4.         <property name="maxTotal" value="1000" />  
  5.         <property name="minIdle" value="8" />  
  6.         <property name="maxIdle" value="100" />  
  7. </bean>  
  8. <bean id="jedisCluster" class="com.besttone.subscribe.util.JedisClusterFactory">  
  9.     <property name="addressConfig">  
  10.         <value>classpath:redis-config.properties</value>  
  11.     </property>  
  12.     <property name="addressKeyPrefix" value="address" />    
  13.     <property name="timeout" value="300000" />  
  14.     <property name="maxRedirections" value="6" />  
  15.     <property name="genericObjectPoolConfig" ref="genericObjectPoolConfig" />  
  16. </bean>  

3.JedisClusterFactory實現類

Java程式碼  收藏程式碼
  1. public class JedisClusterFactory implements FactoryBean<JedisCluster>, InitializingBean {  
  2.     private Resource addressConfig;  
  3.     private String addressKeyPrefix ;  
  4.     private JedisCluster jedisCluster;  
  5.     private Integer timeout;  
  6.     private Integer maxRedirections;  
  7.     private GenericObjectPoolConfig genericObjectPoolConfig;  
  8.     private Pattern p = Pattern.compile("^.+[:]\\d{1,5}\\s*$");  
  9.     @Override  
  10.     public JedisCluster getObject() throws Exception {  
  11.         return jedisCluster;  
  12.     }  
  13.     @Override  
  14.     public Class<? extends JedisCluster> getObjectType() {  
  15.         return (this.jedisCluster != null ? this.jedisCluster.getClass() : JedisCluster.class);  
  16.     }  
  17.     @Override  
  18.     public boolean isSingleton() {  
  19.         return true;  
  20.     }  
  21.     private Set<HostAndPort> parseHostAndPort() throws Exception {  
  22.         try {  
  23.             Properties prop = new Properties();  
  24.             prop.load(this.addressConfig.getInputStream());  
  25.             Set<HostAndPort> haps = new HashSet<HostAndPort>();  
  26.             for (Object key : prop.keySet()) {  
  27.                 if (!((String) key).startsWith(addressKeyPrefix)) {  
  28.                     continue;  
  29.                 }  
  30.                 String val = (String) prop.get(key);  
  31.                 boolean isIpPort = p.matcher(val).matches();  
  32.                 if (!isIpPort) {  
  33.                     throw new IllegalArgumentException("ip 或 port 不合法");  
  34.                 }  
  35.                 String[] ipAndPort = val.split(":");  
  36.                 HostAndPort hap = new HostAndPort(ipAndPort[0], Integer.parseInt(ipAndPort[1]));  
  37.                 haps.add(hap);  
  38.             }  
  39.             return haps;  
  40.         } catch (IllegalArgumentException ex) {  
  41.             throw ex;  
  42.         } catch (Exception ex) {  
  43.             throw new Exception("解析 jedis 配置檔案失敗", ex);  
  44.         }  
  45.     }  
  46.     @Override  
  47.     public void afterPropertiesSet() throws Exception {  
  48.         Set<HostAndPort> haps = this.parseHostAndPort();  
  49.         jedisCluster = new JedisCluster(haps, timeout, maxRedirections,genericObjectPoolConfig);  
  50.     }  
  51.     public void setAddressConfig(Resource addressConfig) {  
  52.         this.addressConfig = addressConfig;  
  53.     }  
  54.     public void setTimeout(int timeout) {  
  55.         this.timeout = timeout;  
  56.     }  
  57.     public void setMaxRedirections(int maxRedirections) {  
  58.         this.maxRedirections = maxRedirections;  
  59.     }  
  60.     public void setAddressKeyPrefix(String addressKeyPrefix) {  
  61.         this.addressKeyPrefix = addressKeyPrefix;  
  62.     }  
  63.     public void setGenericObjectPoolConfig(GenericObjectPoolConfig genericObjectPoolConfig) {  
  64.         this.genericObjectPoolConfig = genericObjectPoolConfig;  
  65.     }  
  66. }  

4.redis-config.properties檔案

   這是一個叢集環境,六個節點(不同埠),三個master ,三個slaver

Java程式碼  收藏程式碼
  1. address1=192.168.30.139:7000  
  2. address2=192.168.30.139:7001  
  3. address3=192.168.30.139:7002  
  4. address4=192.168.30.139:7003  
  5. address5=192.168.30.139:7004  
  6. address6=192.168.30.139:7005  

5.專案目錄圖

 

 

6.程式碼中使用(此程式碼為從redis中獲取相關資訊)

 

 

ok,執行之後,會發現redis會根據不同的key,把它們放入到不同的節點中,如下圖

7.三個master節點中的資料

 

 

 8.三個slave節點中的資料

 

 

實踐過程中碰到的問題:

1.在一切準備好了之後,在操作redis的時候,卻報錯誤:Too many Cluster redirections

由於,我是windows開發環境,在本機開了一個虛擬機器,然後在虛擬機器中搭建的linux叢集環境,本機的ip和虛擬機器中的ip不相同,所以報這個錯誤,

解決方法:在redis叢集搭建過程中,在為每個節點分hash槽的時候,執行如下程式碼(其中,xxx為叢集環境中的ip):

Java程式碼  收藏程式碼
  1. ./redis-trib.rb create --replicas 1 xxx.xxx.xxx.xxx:7000  xxx.xxx.xxx.xxx:7001 xxx.xxx.xxx.xxx:7002 xxx.xxx.xxx.xxx:7003 xxx.xxx.xxx.xxx:7004 xxx.xxx.xxx.xxx:7005./redis-trib.rb create --replicas 1 127.0.0.1:7000  127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005  

2.在一切搭建好後,我們使用redis-cli登陸,當命令:set msg XXX時,報錯:

Java程式碼  收藏程式碼
  1. (error) MOVED 6257 192.168.30.141:7001  

 解決:在使用客戶端登陸時:加上-c引數,即:

Java程式碼  收藏程式碼
  1. redis-cli -c -h 192.168.30.141 -p 7000  

 ok,以上滿足使用,結束!!

-----------------------------------------------------------------------下面是我前一段時間看redis資料,總結的一些東東,記錄下來-------------------------------------------------------------------------------------------

1.redis是什麼?

redis(remote dictionary server):是一個以key-value形式儲存於記憶體中的資料庫.提供了 String / List / Set / Sort Set /Hash 五種資料結構。

伺服器在斷電之後,仍然可以恢復到斷電之前的狀態。

資料: 官網 : http://redis.io   中文網:     http://www.redis.cn/         相關文件: http://redisdoc.com/

2.redis特點?

執行緒模型:單執行緒-多路複用io模型

效能高:支援讀 11萬/秒  ,  寫 8萬/秒

儲存: 記憶體 ;  RDB檔案(二進位制安全的真實資料) ;  AOF檔案(客戶端的命令集合)

事務: 支援事務(每個客戶端序列執行命令,其他客戶端處於阻塞狀態)

釋出/訂閱模式:  功能? 什麼場景使用??

3.redis資料型別

String:動態字串(每個key都是一個String)

編碼方式:int / raw() /embstr

應用場景:普通的string場景

List:列表結構,有序可重複的結構。它擁有佇列的特性。 

編碼方式:ziplist / linkedlist (如果資料量較小,且是數字或者字串,則內部結構為 ziplist)

應用場景:普通的集合資料

Set:集合結構,不重複的集合結構。

編碼方式:intset(整數集合) / hashtable

應用場景:普通的非重複集合資料;支援取交集、取並集等操作

Sort Set:有序集合結構,和Set比較起來,它是有序的。

編碼方式:ziplist / skiplist

應用場景:有序不重複的集合資料

Hash:雜湊結構,儲存多個key:value的結構,此種結構可以儲存物件 ;  如  HMSET  user(key)  username value1 password value2

編碼方式:ziplist / hashtable

應用場景: 從關係型資料庫去出一條資料,就可以讓入到此種結構中

4.記憶體優化

redis提供記憶體回收策略,根據使用的情況可以選擇適當的回收策略

redis提供記憶體共享策略,伺服器啟動時,會自動建立0-9999的數字物件,其他地方使用,可以直接引用。

本質:對記憶體的操作,其實是在每一個redis物件結構內都有一個count的屬性,該屬性記錄了這個物件被引用的次數,如果為0,那麼在記憶體回收時將回收該空間。

save引數調整:當滿足條件時,觸發SAVE命令,持久化到RDB檔案

appendonly引數: 預設no ,若yes,則開啟AOF檔案持久化; BGREWRITEAOF  命令 持久化。其中appendsync引數調整具體的持久化策略,預設為每秒

記憶體回收策略:

5.釋出訂閱模式

6.資料過期設定

可以根據業務需求,將某些資料進行日期設定

7.事務

單執行緒處理所有客戶端發來的請求,所以當有一個客戶端在執行,其他客戶端只能處於阻塞態。只有當前客戶端請求完畢,其他客戶端才能請求

8.資料儲存

RDB檔案模式(快照):該模式儲存的是真實資料,SAVE /BGSAVE 命令 可以將記憶體中的資料儲存到磁碟檔案中。SAVE和BGSAVE區別在於,SAVE是同步命令,

            即當執行該命令,其他客戶端處於阻塞狀態;而BGSAVE 命令則是開啟一個子程序處理,不會影響主程序操作。

AOF檔案模式:該模式儲存的是伺服器執行的命令集合,BGREWRITEAOF 命令。 該模式是appendonly引數控制,若開啟,則會將資料同步到aof檔案中

特點:該模式下,會在伺服器端開闢一段緩衝記憶體來儲存最近時間單位的命令,所以該點要注意。同樣,它也是子程序進行執行

注意:AOF模式的更新頻率比RDB高,若開啟AOF模式的情況下,優先載入AOF檔案內容

9.資料恢復策略

若RDB模式開啟:重啟伺服器只加載rdb檔案內容

若AOF模式開啟:重啟伺服器只加載aof檔案內容

若兩者都開啟:只加載aof檔案內容

10主從複製

功能:資料備份,讀寫分離(測試環境,主伺服器寫,從伺服器讀)

步驟:在從服務端器執行: slaveof <masterip> <masterport> 即可維持關係;配置檔案中也可以

特點:

1.master可以有多個slave

2.除了多個slave連到相同的master外,slave也可以連線其他slave形成圖狀結構

3.主從複製不會阻塞master。也就是說當一個或多個slave與master進行初次同步資料時,master可以繼續處理client發來的請求。相反slave在初次同步資料時則會阻塞不能處理client的請求。

4.主從複製可以用來提高系統的可伸縮性,我們可以用多個slave 專門用於client的讀請求,比如sort操作可以使用slave來處理。也可以用來做簡單的資料冗餘

5.可以在master禁用資料持久化,只需要註釋掉master 配置檔案中的所有save配置,然後只在slave上配置資料持久化。

  6.主伺服器可以關閉持久化功能(註釋掉save引數)

11.sentinel(監測系統)

本質:是一個執行在特殊模式下的redis伺服器。

功能:監控執行在多機上的主redis伺服器,若有某一臺主伺服器出現故障,將自動把其他正常的從伺服器切換為主伺服器,代替出現故障主伺服器的工作。

特點:

1.不發揮資料庫的功能(所有對key以及資料型別操作的命令不能使用)

2.將會給監控的主伺服器以及主伺服器所屬的從伺服器傳送命令,確認是否下線

3.會和監控同一個主伺服器的其他sentinel伺服器通訊,作用是在共同判斷所監控的主伺服器的狀態

4.根據多個sentinel判斷的主伺服器狀態,來決定是否要進行主從切換,故障轉移等

轉移:sentinel監控的主伺服器配置引數要在 sentinel.conf 檔案中配置,啟動時載入

具體配置安裝步驟:

1.http://blog.csdn.net/pi9nc/article/details/17735653

2.http://blog.csdn.net/luyee2010/article/details/9385155

12.叢集

功能:將眾多的key-value集合存在多個節點上,當某一個節點出現障礙,不影響整個叢集的功能。

涉及到的關鍵詞:

節點:一個埠的redis服務便是一個節點

槽指派(叢集將整個系統分為16384個hash槽):這16384個槽位要全部分佈在叢集中的主節點上。

重新分片:若某個主節點故障了,將該主節點的槽位分配到其他可以用的主節點上。

上線/下線狀態: 是否全部的槽位都分佈在節點上。

特點:

1.如果某個節點要叢集,必須要設定cluster-enabled yes

2.每個節點都有這16384個槽位所屬的節點資訊,如果值沒有正確進入槽位,那麼該節點會提示系統將資訊放入正確槽位。重定向的過程會出現一個面向客戶端隱藏的MOVED錯誤

3.叢集線上狀態也可以進行重新分片

4.叢集中的主節點使用者處理客戶端命令,從節點用於複製主節點的資料,主節點下線時,從節點代替主節點的工作

//注意:目前官方提供的叢集功能仍處於內測版本。

13.redis基準

redis自帶的redis-benchmark 工具,支援各種引數進行效能測試

特點:

1.可以模擬多個客戶端處理任意個請求

2.可以測試僅僅少數使用的命令等

注意:測試發現,linux環境下部署的redis伺服器效能遠高於windows下部署的redis伺服器效能, 不在一個層級上面

14.關係資料庫模型的轉換

關係型資料庫表結構:user表 (uid username password birthday )

在redis中可以這樣存在:

1.主鍵: SET user:uid 1  、  GET user:1

2.其他欄位:SET user:uid:username  GET user:5:username ( 5 是通過引數值傳進來的)

3.表資料也可以存在hash結構中:      HMSET user:uid username value1 password value2 birthday value3

15.排序

16.管道

功能:客戶端一次可以傳送多個命令到伺服器,減少往返時延。大大提高效能。

17.優化

redis提供一些簡單的記憶體優化策略,如過期資料清除,記憶體資料共享,

18.持久化