Redis學習05:Springboot集成Redis集群cluster(Lettuce版)
目標
Redis的三種模式:主從、哨兵、集群;本隨筆使用集群模式,配置6個redis服務節點,3主3從,並引入Springboot框架
相關概念:
1- Redis 集群使用數據分片(sharding)而非一致性哈希(consistency hashing)來實現: 一個 Redis 集群包含 16384 個哈希槽(hash slot), 數據庫中的每個鍵都屬於這 16384 個哈希槽的其中一個, 集群使用公式 CRC16(key) % 16384 來計算鍵 key 屬於哪個槽, 其中 CRC16(key) 語句用於計算鍵 key 的 CRC16 校驗和 。集群中的每個節點負責處理一部分哈希槽。
第一步:安裝Redis服務。
因為項目有兩台Linux服務器,打算在兩台服務器上都安裝redis服務,並分別開啟6379,6380,6381三個端口,具體如下(以server_ip1:6379為例):
1- 安裝redis並啟動。具體如何安裝,這里不在贅述;如下表示安裝成功
[root@izbp1aum9uyt0x56d3vepwz ~]# redis-cli --version
redis-cli 3.2.9
2-設置redis配置文件(redis.conf),為了方便配置文件管理,將./redis/redis.conf文件復制到/etc/redis/redis_6379.conf, 關鍵配置如下:
#綁定的主機端口(將bind 127.0.0.1注釋掉,否則外網無法訪問) #bind 127.0.0.1 #集群內redis如果要相互可見,需要配置protected-mode=no protected-mode no #端口號 port 6379 #redis后台運行 daemonize yes #進程文件 pidfile /var/run/redis-cluster/redis_6379.pid #數據存放目錄(根據實際目錄來) dir /usr/local/redis/data/redis_6379 #日志文件 logfile /usr/local/redis/log/redis_6379.log #密碼 requirepass xuegao1234 #開啟集群模式,把注釋#去掉 cluster-enabled yes #集群的配置,配置文件首次啟動自動生成 cluster-config-file /usr/local/redis/conf/node_6379.conf
3- 6380和6381配置和上面一樣,將7379替換成6380或6381即可;
4- 配置結束后,依次啟動6台redis;
[root@izbp10ebr6tsvo83iahgzdz redis]# /usr/local/bin/redis-server /etc/redis/redis_6379.conf [root@izbp10ebr6tsvo83iahgzdz redis]# /usr/local/bin/redis-server /etc/redis/redis_6380.conf [root@izbp10ebr6tsvo83iahgzdz redis]# /usr/local/bin/redis-server /etc/redis/redis_6379.conf [root@izbp10ebr6tsvo83iahgzdz redis]# /usr/local/bin/redis-server /etc/redis/redis_6381.conf [root@izbp1aum9uyt0x56d3vepwz redis]# ps -ef | grep redis root 16949 15871 0 10:15 pts/0 00:00:00 grep --color=auto redis root 28373 1 0 Jun18 ? 00:01:04 /usr/local/bin/redis-server *:6379 [cluster] root 28383 1 0 Jun18 ? 00:01:04 /usr/local/bin/redis-server *:6380 [cluster] root 28391 1 0 Jun18 ? 00:01:37 /usr/local/bin/redis-server *:6381 [cluster]
第二步:創建集群
redis設置集群有多種方式,其中一種是使用redis插件 redis-tri.rb;redis-trib.rb是官方提供的Redis Cluster的管理工具,無需額外下載,默認位於源碼包的src目錄下,但因該工具是用ruby開發的,所以需要准備相關的依賴環境。
1- 安裝redis-trib.rb運行環境和依賴
##准備redis-trib.rb的運行環境 wget https://cache.ruby-lang.org/pub/ruby/2.5/ruby-2.5.1.tar.gz yum -y install zlib-devel tar xvf ruby-2.5.1.tar.gz cd ruby-2.5.1/ ./configure -prefix=/usr/local/ruby make make install cd /usr/local/ruby/ cp bin/ruby /usr/local/bin cp bin/gem /usr/local/bin #安裝rubygem redis依賴 wget http://rubygems.org/downloads/redis-3.3.0.gem gem install -l redis-3.3.0.gem
2- 確認運行環境和依賴安裝成功
[root@izbp1aum9uyt0x56d3vepwz redis]# cd /usr/local/redis/src/ [root@izbp1aum9uyt0x56d3vepwz src]# ./redis-trib.rb help Usage: redis-trib <command> <options> <arguments ...> ....... For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.
3- 使用redis-trib.rb創建集群前的幾個細節准備;
3-1- 確認6個redis節點的IP和端口號(6379~6381以及16379~16381)都開放;包括服務器平台(我的事阿里雲ECS)的安全組配置、服務器防火牆(關閉或允許端口開放)、redis.conf的配置文件(#bind 127.0.0.1); 否則創建節點時會報[ERR] Sorry, can't connect to node 47.111.164.6:6380異常
3-2- redis.conf的配置文件 protected-mode no,確保redis節點相互之間可見
3-3- 確認集群密碼和redis密碼一致
[root@izbp1aum9uyt0x56d3vepwz src]# vim /usr/local/ruby/lib/ruby/gems/2.5.0/gems/redis-3.3.0/lib/redis/client.rb DEFAULTS = { :url => lambda { ENV["REDIS_URL"] }, :scheme => "redis", :host => "127.0.0.1", :port => 6379, :path => nil, :timeout => 5.0, :password => "xuegao1234", ##密碼設置和redis密碼一致,同時6台redis密碼保持一致 :db => 0, :driver => nil, :id => nil, :tcp_keepalive => 0, :reconnect_attempts => 1, :inherit_socket => false }
4- 創建集群(集群創建失敗的情況以及如何處理,見文章附錄)
[root@izbp1aum9uyt0x56d3vepwz src]# pwd /usr/local/redis/src [root@izbp1aum9uyt0x56d3vepwz src]# ./redis-trib.rb create --replicas 1 47.111.164.6:6379 47.111.164.6:6380 47.111.164.6:6381 120.26.55.92:6379 120.26.55.92:6380 120.26.55.92:6381 >>> Creating cluster /usr/local/ruby/lib/ruby/gems/2.5.0/gems/redis-3.3.0/lib/redis/client.rb:459: warning: constant ::Fixnum is deprecated >>> Performing hash slots allocation on 6 nodes... Using 3 masters: ...... Waiting for the cluster to join... >>> Performing Cluster Check (using node 47.111.164.6:6379) ..... [OK] All nodes agree about slots configuration. >>> Check for open slots... >>> Check slots coverage... [OK] All 16384 slots covered.
特別注意:如果創建集群失敗,重現創建集群時,需要確認(1)redis中沒有key-value;(2)需要對每一台redis重置集群信息,如下
127.0.0.1:6381> flushall OK 127.0.0.1:6381> cluster reset OK 127.0.0.1:6381> quit
第三步:Springboot集成Redis集群
集成方式比較簡單,使用Lettuce客戶端繼承。其中Jedis和Lettuce的區別:Jedis 是直連模式,在多個線程間共享一個 Jedis 實例時是線程不安全的,每個線程都去拿自己的 Jedis 實例,當連接數量增多時,物理連接成本就較高了。Lettuce的連接是基於Netty的,連接實例可以在多個線程間共享,不用擔心並發線程的數量。通過異步的方式可以讓我們更好地利用系統資源。
1- POM文件(注意commons-pool2.jar)
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
<!--lettuce.pool緩存連接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
2- application.properities
server.port=80 spring.redis.password=xuegao1234 spring.redis.cluster.nodes=47.111.164.6:6379,120.26.55.92:6379,47.111.164.6:6380,120.26.55.92:6380,47.111.164.6:6381,120.26.55.92:6381 spring.redis.cluster.max-redirects=3 spring.redis.lettuce.pool.max-idle=16 spring.redis.lettuce.pool.max-active=16 spring.redis.lettuce.pool.min-idle=16
3- RedisConfiguration.java
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration //自動配置 public class RedisConfiguration { @Bean public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) { // 構建template RedisTemplate template = new RedisTemplate(); template.setConnectionFactory(factory); // 設置key序列化方式為字符串 template.setKeySerializer(new StringRedisSerializer()); // 設置value序列化方式為JSON template.afterPropertiesSet(); return template; } }
4- RedisController.java
@RestController @RequestMapping("/api/redis/") public class RedisController { @Autowired private RedisTemplate redisTemplate; //1- 字符串string @GetMapping("/string/set/{key}/{value}") public Object set(@PathVariable String key,@PathVariable String value){ redisTemplate.opsForValue().set(key,value); return "success"; } @GetMapping("/string/get/{key}") public Object get(@PathVariable String key){ return redisTemplate.opsForValue().get(key); } }
附錄(集成過程中碰到的問題)
1- redis-trib.rb創建集群時,報ERR Slot 0 is already busy 或 ERR Slot 5798 is already busy
問題 ERR Slot 0 is already busy (Redis::CommandError) 解決:錯誤提示是說:slot插槽被占用了、這是因為 搭建集群前時,以前redis的舊數據和配置信息沒有清理干凈。 對每個節點執行如下操作 127.0.0.1:6381> flushall OK 127.0.0.1:6381> cluster reset OK 127.0.0.1:6381> quit
2- redis-trib.rb創建集群時,一直處於等待中Waiting for the cluster to join
##一直等待中 >>> Sending CLUSTER MEET messages to join the cluster Waiting for the cluster to join................................................................................................................ .......................................................................................................................... 解決:看到上圖,這時候就不用再等了,果斷Ctrl+C 關閉集群中的所有實例 於此同時刪除掉每個節點文件下的 demp.rdb和nodes.conf文件 然后開放你redis實例端口號+10000的端口
3- redis-trib.rb創建集群時,無法連接節點 ERR] Sorry, can't connect to node 47.111.164.6:6380
問題 [ERR] Sorry, can't connect to node 47.111.164.6:6380
解決
可能是因為端口沒有開放或者集群密碼錯誤,檢查端口是否開放以及設施集群密碼(見上文)
4- Spring啟動后,使用接口測試Redis,如果報跟Redis相關的錯誤,基本都是創建集群有問題。
END