个人博客


  • 腾讯云:CentOS7.6 1台
  • Redis版本:5.0.8
  • 1机6节点、3主3从

1、准备工作

  1. 安装Redis5.0.8版本至/usr/local/redis/目录下。(参考:Redis安装
  2. 给每个实例创建1个目录,以端口号为区分,用来存放持久化文件和日志等文件。
1
mkdir /usr/local/redis/900{1,2,3,4,5,6}
  1. /usr/local/redis/config/目录下给每个实例拷贝1个redis配置文件,以每个实例的端口号进行区分。
1
2
3
4
5
6
redis9001.conf
redis9002.conf
redis9003.conf
redis9004.conf
redis9005.conf
redis9006.conf

每个配置文件在原来的redis.conf的配置基础上更改以下配置,这里以9001为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
protected-mode no
port 9001
daemonize yes
pidfile /usr/local/redis/9001/redis_9001.pid
logfile "/usr/local/redis/9001/redis_9001.log"
dbfilename dump_9001.rdb
dir /usr/local/redis/9001
appendonly yes
appendfilename "appendonly_9001.aof"
cluster-enabled yes
cluster-config-file /usr/local/redis/9001/nodes-9001.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
cluster-migration-barrier 1
cluster-require-full-coverage no

  1. 启动redis。
1
2
3
4
5
6
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9001.conf
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9002.conf
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9003.conf
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9004.conf
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9005.conf
/usr/local/redis/bin/redis-server /usr/local/redis/config/redis9006.conf

2、集群搭建

  1. 为了能让外网访问,需要云服务器开放端口,这里不仅需要开放9001-9006的端口,还需要开放19001-19006的端口,否则集群搭建不了。
1
2
3
firewall-cmd --zone=public --add-port=900{1,2,3,4,5,6}/tcp --permanent
firewall-cmd --zone=public --add-port=1900{1,2,3,4,5,6}/tcp --permanent
firewall-cmd --reload
  1. 创建集群,每个节点以外网ip进行注册。
1
/usr/local/redis/bin/redis-cli --cluster create 148.70.153.63:9001 148.70.153.63:9002 148.70.153.63:9003 148.70.153.63:9004 148.70.153.63:9005 148.70.153.63:9006 --cluster-replicas 1
  1. 验证集群可用性,用如下命令进入客户端,进行get、set操作看是否可以。
1
./redis-cli -c -p 9001

3、Java应用

  1. 本人在搭完后用SpringBoot2.x版本的RedisTemplate操作Redis发现一个很奇怪的问题:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2020-05-12 22:15:31,218 [WARN] [lettuce-nioEventLoop-4-7] [io.lettuce.core.cluster.topology.ClusterTopologyRefresh:243] [] Unable to connect to 172.27.0.10:9006
java.util.concurrent.CompletionException: io.netty.channel.ConnectTimeoutException: connection timed out: /172.27.0.10:9006
at java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:292)
at java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:308)
at java.util.concurrent.CompletableFuture.uniApply(CompletableFuture.java:593)
at java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:577)
at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:474)
at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:1977)
at io.lettuce.core.AbstractRedisClient.lambda$initializeChannelAsync0$4(AbstractRedisClient.java:330281199210082216)
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:502)
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:495)
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:474)
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:415)
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:540)
at io.netty.util.concurrent.DefaultPromise.setFailure0(DefaultPromise.java:533)
at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:114)
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:269)
at io.netty.util.concurrent.PromiseTask$RunnableAdapter.call(PromiseTask.java:38)
at io.netty.util.concurrent.ScheduledFutureTask.run(ScheduledFutureTask.java:127)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:495)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
Caused by: io.netty.channel.ConnectTimeoutException: connection timed out: /172.27.0.10:9006
at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe$1.run(AbstractNioChannel.java:267)
... 8 common frames omitted
  • 第一次执行操作总是特别慢,有时候执行成功但会提示上述的报错,有时候干脆执行也失败。
  • 报错提示居然是连接不了我云服务器的内网ip地址,而且我在自己的虚拟机上从来没有过这个问题。
  • 因为SpringBoot从2.x版本开始默认使用lettuce作为操作redis的实现,我把这个依赖排除然后自行引入jedis包,再次操作发现没有问题。
  1. 原因排查。
    查了相关资料,发现整合SpringBoot以后,客户端在连接redis某个节点时,会通过cluster slots命令去获取集群中的槽点信息。通过这个命令返回的每个节点除了自身节点是内网ip外其他节点均为公网ip,而这些节点信息源自于我们之前在配置文件中配的cluster-config-file /usr/local/redis/9001/nodes-9001.conf这个文件。这个文件是由集群创建时生成的,在生成自身节点时读取的ip是网卡ip,而云服务器的网卡ip即为内网ip地址。知道了问题源头就好解决了。

  2. 故障排除。
    这里以9001为例,修改/usr/local/redis/9001/nodes-9001.conf这个文件,将内网ip改为外网ip。
    -w701
    按同样方法依次修改每个节点的node文件,重启redis,这时候再试发现一切都正常了。

4、设置密码

  1. 如果在云服务器搭建的redis,最好修改默认端口并设置强度较高的密码。这里需要设置2个密码:
    requirepass:对登录权限做限制即校验客户端的连接,主要是在主节点配置。
    masterauth:主要是针对master对应的slave节点设置的,在slave节点数据同步的时候用到,主要是在从节点配置。
    实际上为了运维方便以及考虑到主从切换,一般会为每个节点同时配置这2个参数且密码相同。

  2. 挨个登录每个节点的redis客户端设置密码。

1
2
3
4
config set requirepass 123456
config set masterauth 123456
auth 123456
config rewrite

查看配置文件末尾就会发现多了2行配置,就是我们设置的密码。
如果直接在redis.conf配置文件上修改则需要重启redis后才会生效。
再次登录客户端则需要使用密码。

1
./redis-cli -c -p 9001 -a 123456

参考链接