个人博客


  • 腾讯云: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}
  3. /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
  4. 启动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
  2. 创建集群,每个节点以外网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
  3. 验证集群可用性,用如下命令进入客户端,进行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包,再次操作发现没有问题。
  2. 原因排查。
    查了相关资料,发现整合SpringBoot以后,客户端在连接redis某个节点时,会通过cluster slots命令去获取集群中的槽点信息。通过这个命令返回的每个节点除了自身节点是内网ip外其他节点均为公网ip,而这些节点信息源自于我们之前在配置文件中配的cluster-config-file /usr/local/redis/9001/nodes-9001.conf这个文件。这个文件是由集群创建时生成的,在生成自身节点时读取的ip是网卡ip,而云服务器的网卡ip即为内网ip地址。知道了问题源头就好解决了。

  3. 故障排除。
    这里以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

参考链接