1.redis-cluster設計 Redis集群搭建的方式有多種,例如使用zookeeper,但從redis 3.0之後版本支持redis-cluster集群,redis-cluster採用無中心結構,每個節點保存數據和整個集群狀態,每個節點都和其他所有節點連接。其redis-cluster架構圖 ...
1.redis-cluster設計
Redis集群搭建的方式有多種,例如使用zookeeper,但從redis 3.0之後版本支持redis-cluster集群,redis-cluster採用無中心結構,每個節點保存數據和整個集群狀態,每個節點都和其他所有節點連接。其redis-cluster架構圖如下:
其結構特點
- 所有的redis節點彼此互聯(PING-PONG機制),內部使用二進位協議優化傳輸速度和帶寬。
- 節點的fail是通過集群中超過半數的節點檢測失效時才生效。
- 客戶端與redis節點直連,不需要中間proxy層.客戶端不需要連接集群所有節點,連接集群中任何一個可用節點即可。
- redis-cluster把所有的物理節點映射到[0-16383]slot上(不一定是平均分配),cluster 負責維護node<->slot<->value。
- Redis集群預分好16384個桶,當需要在 Redis 集群中放置一個 key-value 時,根據 CRC16(key) mod 16384的值,決定將一個key放到哪個桶中。
redis cluster節點分配
例如分配三個主節點分別是:7000, 7001, 7002。三個從節點分別是7003,7004,7005。它們可以是一臺機器上的六個埠,也可以是六台不同的伺服器。採用哈希槽 (hash slot)的方式來分配16384個slot 的話,六個節點分別承擔的slot 區間如同所示:
獲取數據: 如果存入一個值,按照redis cluster哈希槽的演算法: CRC16('key')%16384 = 6782。 就會把這個key 的存儲分配到7001 上了。同樣,當連接(7000,7001,7002)任何一個節點想獲取'key'這個key時,也會這樣的演算法,然後內部跳轉到7001節點上獲取數據 。
2.redis-cluster主從模式
redis cluster為了保證數據的高可用性,加入了主從模式,一個主節點對應一個或多個從節點,主節點提供數據存取,從節點則是從主節點拉取數據備份,當這個主節點掛掉後,就會有這個從節點選取一個來充當主節點,從而保證集群不會掛掉。主從模式具有如下特點:
- 集群有7000,7001,7002三個主節點, 如果這3個節點都沒有加入從節點,如果7001掛掉了,就無法訪問整個集群,7000和7002的slot也無法訪問。
- 主從節點同時掛掉後,如節點7001和7004同時掛了,Redis集群將無法繼續正確地提供服務。
- 為每個主節點設置從節點, 比如像這樣, 集群包含主節點7000,7001,7002 以及從節點7003,7004,7005, 那麼即使7001掛掉系統也可以繼續正確工作。7004節點替代了7001節點,所以Redis集群將會選擇7004節點作為新的主節點,集群將會繼續正確地提供服務。 當7001重新開啟後,它就會變成7004的從節點。
2.redis-cluster集群搭建
搭建環境
本次測試採用虛擬機模式,在本機上做測試,虛擬機環境為Centos7.0。搭建集群需要如下相關依賴軟體,下載地址為“
- rubygems軟體包下載:https://rubygems.org/pages/download
- ruby軟體包下載:http://www.ruby-lang.org/en/downloads/
- redis-3.2.2.gem依賴包下載:https://rubygems.global.ssl.fastly.net/gems/redis-3.2.2.gem
- openssl軟體包下載: http://www.openssl.org/source/
下載文件包截圖如下:
redis集群與大多數分散式中間件一樣,redis的cluster也是依賴選舉演算法來保證集群的高可用,所以類似zookeeper一樣,一般是奇數個節點(可以允許N/2以下的節點失效),再考慮到每個節點做Master-Slave互為備份,所以一個redis cluster集群最少也得6個節點。
步驟1:安裝redis
下載最新版redis並安裝在linux系統中。具體操作可參考網址:Redis介紹及Jedis基礎操作
步驟2:新建集群文件夾目錄
新建一個根目錄data/cluster/。併在cluster目錄下麵建立6個子目錄:mkdir
7000 7001 7002 7003 7004 7005。
步驟3:修改redis.conf配置文件
修改redis的配置文件redis.conf,複製原有解壓redis文件中的redis.conf文件到7000目錄中,操作指令如:cp /usr/software/redis-4.0.6/redis.conf /data/cluster/7000。修改redis.conf文件中的配置欄位,修改欄位如下:
daemonize yes #後臺啟動
port 7000 #修改埠號,從7000到7005
cluster-enabled yes #開啟cluster,去掉註釋
cluster-config-file nodes-7000.conf
cluster-node-timeout 15000
appendonly yes
pidfile /var/run/redis_7000.pid
相同操作處理其他五個文件夾,配置文件redis.conf中,將7000替換為對應的值。比如:7001文件下替換為7001。處理完成後,一次運行定義的每個文件夾下麵的redis,查看是否啟動成功。
步驟4:安裝Ruby環境
安裝Ruby環境。網上很多博客都是採用yum模式安裝的,但考慮到FQ等限制條件,本文采用離線模式安裝。
Ruby簡介
Ruby是一種純粹的面向對象編程語言。它由日本的松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)創建於1993年。可以在 www.ruby-lang.org 的 Ruby 郵件列表上找到松本行弘(まつもとゆきひろ/Yukihiro Matsumoto)的名字。在 Ruby 社區,松本也被稱為馬茨(Matz)。Ruby 是"程式員的最佳朋友"。Ruby 的特性與 Smalltalk、Perl 和 Python 類似。Perl、Python 和 Smalltalk 是腳本語言。Smalltalk 是一個真正的面向對象語言。Ruby,與 Smalltalk 一樣,是一個完美的面向對象語言。使用 Ruby 的語法比使用 Smalltalk 的語法要容易得多。
Ruby離線安裝
- Ruby下載地址:http://www.ruby-lang.org/en/downloads/ ,最新的版本為2.5.0。
- Ruby安裝參考網址:Linux 安裝Ruby詳解(線上和離線安裝)
- 在安裝之前,請確保使用賬號具有Root許可權,將下載的Ruby安裝包上傳到伺服器當中,通過xtfp5工具進行文件上傳:解壓上傳文件, tar -zxvf ruby-2.5.0.tar.gz,進入到ruby-2.5.0目錄執行指令make && make install安裝,安裝成功可通過指令:ruby -v查看版本號:
步驟5:安裝RubyGems環境
安裝RubyGems環境。網上很多博客都是採用yum模式安裝的,但考慮到FQ等限制條件,本文采用離線模式安裝。
RubyGems簡介
RubyGems 是 Ruby 的一個包管理器,它提供一個分發 Ruby 程式和庫的標準格式,還提供一個管理程式包安裝的工具。RubyGems 旨在方便地管理 gem 安裝的工具,以及用於分發 gem 的伺服器。這類似於 Ubuntu 下的apt-get, Centos 的 yum,Python 的 pip。RubyGems大約創建於2003年11月,從Ruby 1.9版起成為Ruby標準庫的一部分。
離線安裝RubyGems
如果你的 Ruby 低於 1.9 版本,也可以通過手動安裝:
- RubyGems下載地址:https://rubygems.org/pages/download
- RubyGems安裝參考網址:Linux 離線安裝Rubygems詳解
- 解壓下載文件併進入目錄,解壓指令tar -zxvf rubygems-2.7.4.tgz,執行命令:ruby setup.rb
步驟6:安裝openssl
使用gem install 安裝 ruby redis。直接操作會報如下錯誤,查看原因是因為缺少openssl。
離線安裝openssl
- openssl下載地址:http://www.openssl.org/source/
- openssl安裝參考網址:配置群集時# gem install redis 報錯:Unable to require openssl, install OpenSSL and rebuild ruby
- 解壓下載文件併進入目錄,解壓指令如: tar -xzvf openssl-1.0.2n.tar.gz ,執行以下命令:
tar -xzvf openssl-1.0.2n.tar.gz
cd openssl-1.0.2n
./config -fPIC --prefix=/usr/local/openssl enable-shared
./config -t make && make install
- 執行以上命令安裝openssl,安裝後查看版本號如下:
- 解決ssl.h文件找不到的問題,配置ruby文件,# ruby extconf.rb --with-openssl-include=/usr/local/openssl/include/ --with-openssl-lib=/usr/local/openssl/lib
- 設置軟鏈接:ln -s /usr/local/src/ruby-2.2.3/include /
- 再次編譯安裝,成功後如下圖所示:
步驟7: 安裝redis-trib.rb運行依賴的ruby的包redis-3.2.2.gem
- redis-3.2.2.gem下載地址:https://rubygems.global.ssl.fastly.net/gems/redis-3.2.2.gem
- 下載完成後上傳到伺服器上面,執行安裝命令如:gem install /usr/software/redis-3.2.2.gem
步驟8: 使用redis-trib.rb創建集群
使用create命令 --replicas 1 參數表示為每個主節點創建一個從節點,其他參數是實例的地址集合。可利用命令:.
/redis-trib
.rb help查看使用介紹。
運行集群創建shell腳本,cluster就創建成功了。最終的結果是後面的192.168.210.128:7000~192.168.210.128:7005中,會有3個會指定成master,而其它3個會指定成slave。
註:利用redis-trib創建cluster的操作,只需要一次即可,假設系統關機,把所有6個節點全關閉後,下次重啟後,即自動進入cluster模式,不用再次redis-trib.rb create。
查看redis進程啟動狀態,並開放防火牆中的對應埠。
查看節點分配指令為:./redis-trib.rb check 192.168.210.128:7002 (任意一個集群的ip地址)
3.redis-cluster集群節點選舉,擴容與刪除
集群選舉
現在模擬將7002節點掛掉,按照redis-cluster原理會選舉將 7002的從節點7005選舉為主節點。直接關閉7002的進程,在重新check可發現7005已經被自動選舉為主節點。當啟動7002後,7002將作為7005的從節點。
新增主節點
新增一個節點D,redis cluster的這種做法是從各個節點的前面各拿取一部分slot到新增點上,也可設置從指定部分節點獲取。舉例如:在A,B,C節點上新增節點D。
- 節點A覆蓋1365-5460
- 節點B覆蓋6827-10922
- 節點C覆蓋12288-16383
- 節點D覆蓋0-1364,5461-6826,10923-12287
新增一個節點7006作為主節點,操作步驟如下:
- 修改配置文件,新建一個對應的文件7006,並把複製配置文件redis.conf放入到文件7006下麵,並修改配置文件,把埠修改為7006,其他配置信息也參考前面的案例對應修改。節點配置信息成功後,啟動7006下麵的redis。
- 將7006加入到現有的集群中,輸入指令:./redis-trib.rb add-node 192.168.210.128:7006 192.168.210.128:7002。指令說明:dd-node是加入集群節點,192.168.210.128:7006為要加入的節點,192.168.210.128:7002 表示加入的集群的一個節點,用來辨識是哪個集群,理論上那個集群的節點都可以。
- 目前cluster已經定義7006為主節點,但是Cluster並未給7006分配哈希卡槽(0 slots)。
- redis-cluster在新增節點時並未分配卡槽,需要操作者手動對集群進行重新分片遷移數據,需要重新分片命令:reshard。操作如:redis-trib.rb reshard 192.168.210.128:7002。指令說明:這個命令是用來遷移slot節點的,後面的192.168.210.128:7002是表示是哪個集群,埠填[7000-7006]都可以,執行後:它提示需要遷移多少slot到7006上平分16384個哈希槽給4個節點:16384/4 = 4096,可移動4096個槽點到7006上。填寫7006的id:如ee3efb90e5ac0725f15238a64fc60a18a71205d7。
- redis-trib 會向你詢問重新分片的源節點(source node),即,要從特定的哪個節點中取出 4096 個哈希槽,還是從全部節點提取4096個哈希槽, 並將這些槽移動到7006節點上。如果不打算從特定的節點上取出指定數量的哈希槽,那麼可以向redis-trib輸入 all,這樣的話, 集群中的所有主節點都會成為源節點,redis-trib從各個源節點中各取出一部分哈希槽,湊夠4096個,然後移動到7006節點上。操作命令為:Source node #1:all 。
- 確認之後,redis-trib就開始執行分片操作,將哈希槽一個一個從源主節點移動到7006目標主節點。重新分片結束後可以check以下節點的分配情況。指令為:./redis-trib.rb check 192.168.210.128:7002。可查看擴容主節點是否成功。
新增從節點
- 新增一個節點7007作為從節點修改配置文件,新建一個對應的文件7007,並把複製配置文件redis.conf放入到文件7007下麵,並修改配置文件,把埠修改為7007,其他配置信息也參考前面的案例對應修改。節點配置信息成功後,啟動7007下麵的redis並加入到現有集群中。
- redis-trib增加從節點的命令為:./redis-trib.rb add-node --slave --master-id $[nodeid] 192.168.210.128:7007 192.168.210.128:7000 。操作指令含義:nodeid為要加到master主節點的node id,192.168.210.128:7007為新增的從節點,192.168.210.128:7000為集群的一個節點(集群的任意節點都行),用來辨識是哪個集群;如果沒有給定那個主節點--master-id的話,redis-trib將會將新增的從節點隨機到從節點較少的主節點上。
- 從節點不存在分片操作,與主節點對應的片一致。
移除主節點
-
移除節點使用redis-trib的del-node命令,redis-trib del-node 192.168.210.128:7002 ${node-id} 。操作指令含義: 192.168.210.128:7000為指定集群,node-id為要刪除的主節點。 和添加節點不同,移除節點node-id是必需的。
-
測試刪除7001主節點,redis cluster提示7001已經有數據了,不能夠被刪除,需要將他的數據轉移出去,也就是和新增主節點一樣需重新分片。
- 分區指令: ./redis-trib.rb reshard 192.168.210.128:7002
- 輸入提示的需要移動的分片大小,分配給7001的slots為4096,輸入需要移動的片為4096。
- 輸入這些移除的slots如何分配給其他node,可指定一個具體node的id或者選擇所有。
- 最後確認後,開始移除節點。
移除從節點
- 移除節點使用redis-trib的del-node命令,redis-trib del-node 192.168.210.128:7002 ${node-id} 。操作指令含義: 192.168.210.128:7000為指定集群,node-id為要刪除的節點。 和添加節點不同,移除節點node-id是必需的。
- 從節點不存在分片問題,直接執行命令,確認移除即可。
4.redis-cluster集群與分散式連接池區別
ShardedJedisPool是redis沒有集群功能之前客戶端實現的一個數據分散式方案,redis3.0提供集群之後,客戶端則採用JedisCluster實現連接redis集群環境。 ShardedJedisPool使用的是JedisShardInfo的instance的順序或者name來做的一致性哈希,JedisCluster使用的是CRC16演算法來做的哈希槽。
集群環境各個服務之間的數據是隔離的。無論是ShardedJedisPool的一致性哈希演算法還是JedisCluster的CRC16哈希槽演算法,都是把所有的服務疊加然後進行均勻的分割,分割出來的每一個段或槽都是不重覆的,所以導致存儲的數據彼此之間也是處於隔離狀態的。
jediscluster通過在客戶端調用捕捉異常,可實現集群環境下的高可用。Jedis還提供了對jedis sentinel pool的封裝,所以ShardedJedisPool發生主從切換的時候,web server都不需要重新配置和deploy。高可用性的極佳體現啊。
5.java客戶端調用redis-cluster
更新pom文件中redis-clients的版本,低版本會報錯。<redis-clients.version>2.9.0</redis-clients.version>
java客戶端調用redis-cluster可通過在java代碼中直接填寫地址或通過spring配置文件填寫,具體可參考上傳的代碼。在java中調用集群案例代碼如下:
/** * Description: redis cluster 測試 * Copyright: 2018 CSNT. All rights reserved. * Company:CSNT * * @author wangling * @version 1.0 */ public class RedisClusterTestDemo { @Test public void testRedisCluster() throws Exception { JedisPoolConfig poolConfig = new JedisPoolConfig(); Set<HostAndPort> nodes = new HashSet<HostAndPort>(); HostAndPort hostAndPort = new HostAndPort("192.168.210.128", 7000); HostAndPort hostAndPort1 = new HostAndPort("192.168.210.128", 7001); HostAndPort hostAndPort2 = new HostAndPort("192.168.210.128", 7002); HostAndPort hostAndPort3 = new HostAndPort("192.168.210.128", 7003); HostAndPort hostAndPort4 = new HostAndPort("192.168.210.128", 7004); HostAndPort hostAndPort5 = new HostAndPort("192.168.210.128", 7005); nodes.add(hostAndPort); nodes.add(hostAndPort1); nodes.add(hostAndPort2); nodes.add(hostAndPort3); nodes.add(hostAndPort4); nodes.add(hostAndPort5); JedisCluster jedisCluster = new JedisCluster(nodes,5000,1000); jedisCluster.set("jedisKey","wangling test jedisKey"); //redis內部會創建連接池,從連接池中獲取連接使用,然後再把連接返回給連接池 String string = jedisCluster.get("jedisKey"); System.out.println(string); } }
//基於配置文件調用,完整配置信息參考上傳的源代碼 /** * Description: redis cluster 測試 * Copyright: 2018 CSNT. All rights reserved. * Company:CSNT * * @author wangling * @version 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:redis-cluster-context.xml") public class JedeisClusterTest { @Autowired private JedisCluster jedisCluster; @Test public void testJedisCluster(){ jedisCluster.set("jedisCluster", "wangling test jedisCluster"); String val = jedisCluster.get("jedisCluster"); System.out.println(val); } }
軟體運行截圖如下:
6.參考網址
- 分散式緩存技術redis學習系列(七)——spring整合jediscluster
- Linux 安裝Ruby詳解(線上和離線安裝)
- 解決方法:配置群集時gem install redis 報錯
- redis 學習筆記(6)-cluster集群搭建
- Redis介紹及Jedis基礎操作
7.源碼下載
在Git上面下載:https://github.com/wuya11/redisClusterDemo