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