目录
Redis 集群测试环境部署 1
1. 部署地址与redis目录 2
2. 安装 2
第一步 2
第二步 2
第三步 3
第四步 5
第五步 6
3. docker 常用命令 : 7
4. 验证集群: 7
5.集群原理分析 9
地址: 192.168.x.x redis连接密码: ****** , (192.168.x.x 是宿主机的ip不是docker容器的ip)
集群方式 : 一台宿主机3主3从,占用端口8001-8006 版本 5.0.2
/home/docker/redis-cluster/redis01-06
Ii. redis持久化数据目录 :
/home/datas/redis-cluster/redis01-06/data
Iii. redis成功运行之后data文件夹下面自动生产的文件 :
appendonly.aof : 数据持久化文件
nodes-800*.conf : 集群的节点信息
dump.rdb : 快照文件
第一步: docker pull redis5.0.2的镜像,然后在docker/redis-cluster目录中新建6个文件用于存放redis.conf,然后在datas/redis-cluster目录中新建6个文件夹用于存放redis持久化数据文件与节点信息文件。
第二步: 修改redis.conf的配置信息
port 8001(分别对每个机器的端口号进行设置)
#bind 127.0.0.1 (注释掉这部分,这是限制redis只能本地访问)
protected-mode no (关闭保护模式)
appendonly yes (持久化(可选))
requirepass ****** (设置redis访问密码)
masterauth ****** (设置集群节点间访问密码,跟上面一致)
cluster-enabled yes(启动集群模式)
cluster-config-file nodes-8001.conf(集群节点信息文件,这里800x最好和port对应上)
cluster-node-timeout 5000 (节点之间的延迟时间)
cluster-announce-ip 192.168.x.x (宿主机ip)
cluster-announce-port 8001 (节点映射端口)
cluster-announce-bus-port 18001 (节点总线端)
注意 : docker中daemonize 设置为no 表示为前台启动,设置为yes为后台启动,如果设置为后台启动,会导致Docker认为你的服务已结束 所以挂了。所以在docker里面设置为前台启动,还可以在docker里面查看日志。
第三步: 分别用docker启动6个redis应用
redis01:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis01/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis01/data:/data --name redis01 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
redis02:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis02/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis02/data:/data --name redis02 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
redis03:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis03/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis03/data:/data --name redis03 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
redis04:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis04/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis04/data:/data --name redis04 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
redis05:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis05/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis05/data:/data --name redis05 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
redis06:
docker run -d --privileged=true -v /home/docker/redis-cluster/redis06/redis.conf:/etc/redis/redis.conf -v /home/datas/redis-cluster/redis06/data:/data --name redis06 --net host --restart=always redis:5.0.2 redis-server /etc/redis/redis.conf --appendonly yes
(redis01-06,网络模式使用host,docker容器直接用宿主机的ip和端口,因为用默认的桥接模式创建集群,外部用Jrediscluster无法连接,只能用Jredis单机才能连接,可能与桥接模式的网络模式有关,集群导致ip或者端口冲突,然后java连接redis集群代码报出无法从连接池中获取连接、连接超时的错误。--net host 表示使用host网络模式, --restart=always 表示重启docker的时候,自动重启redis。--net host网络模式的时候需要关闭服务器防火墙。Centos7关闭防火墙 : systemctl stop firewalld.service)
第四步: 进入任意主节点redis应用容器中,连接上redis,执行创建集群的命令
进入redis01容器 :
docker exec -it redis01 /bin/bash
连接redis01 :
redis-cli -a ****** -c -h 192.168.x.x -p 8001
创建集群 :
redis-cli -a ****** --cluster create 192.168.x.x:8001 192.168.x.x:8002 192.168.x.x:8003 192.168.x.x:8004 192.168.x.x:8005 192.168.x.x:8006 --cluster-replicas 1
节点信息 :
(主节点对应有一个master标识,从节点有一个slave标识,主节点的最前面是容器id,对应的从节点后面会存储主节点的容器id)
第五步: 容器中,连接上redis进行测试;然后再用java代码连接集群测试
连接进入redis客户端之后集群信息查询命令 :
cluster nodes : 节点信息
cluster info : 集群信息
注意 : 用java代码连接redis集群的时候,
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8001));
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8002));
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8003));
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8004));
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8005));
jedisClusterNode.add(new HostAndPort("192.168.x.x", 8006));
第一个端口为8001的节点,必须要去验证是否能正常接收数据,因为它是第一个加入的节点,用第一个节点去连接失败了,代码不会再去尝试后面的节点了;所以只要8001端口的节点挂掉了,哪怕服务器端的集群是正常的,它也会报连接超时,而后面的节点挂掉了,不会有影响,因为只要前面的节点正常和集群正常就行,因为连接集群只需要一个节点就行了。所以程序连接集群的时候要考虑要用代码处理这个问题。
查看所有下载下来的镜像 : docker images
查看在运行的容器 : docker ps
查看所有的容器 : docker ps -a
查看容器日志 : docker logs redis01
运行容器 : docker start redis01(开启)、docker stop redis01(停止)、docker restart redis01(重启)
删除容器 : docker rm redis01
查看端口 :
docker inspect redis01 redis02 redis03 redis04 redis05 redis06 | grep IPAddress (一起查看redis01-06的端口)
查看网路 : docker network ls
进入测试环境服务器,
进入容器 : docker exec -it redis01 /bin/bash
连接redis : redis-cli -a ****** -c -h 192.168.x.x -p 8001
(注意 : 只要容器没有被停止,容器名可以是集群中任意一个,端口也是同理。)
查看节点信息 : cluster nodes
查集群信息 : cluster info
在服务器上连接上redis客户端之后,通过set key data 插入数据(redis会通过hash算法,算出来这个key的hash值要存在哪个节点上,redis一共有16384个槽,大致平均分配到3个主节点上去,从节点没有槽点,只作数据备份),如果就在当前节点上,成功之后会显示一个OK,如果在其它节点上,连接会自动跳到其它的节点上。插入成功之后先去主节点的appendonly.aof 中查看key值与存储的数据,然后再去对应的从节点上查看。
经过停止对应的节点容器,通过cluster nodes查看,一般情况下,经过选举,从节点成为主节点的概率更高。
4.测试主节点只存在两台主节点时,集群停止工作。
通过docker stop redis0* 主节点之后,从节点会发起选举,一般情况下是对应的从节点变为主节点,当最后如果只存在2个主节点时,集群将不可用,代码报错 : The cluster is down。
5.验证任意从节点停止,集群是否能正常工作。
通过docker stop redis0* 3台从节点,集群任然可用。
Redis Cluster 将所有数据划分为 16384 的 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。当 Redis Cluster 的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将
其缓存在客户端本地。这样当客户端要查找某个 key 时,可以直接定位到目标节点。同时因为槽位的信息可能会存在客户端与服务器不一致的情况,还需要纠正机制来实现槽位信息的校验调整。
槽位定位算法
Cluster 默认会对 key 值使用 crc16 算法进行 hash 得到一个整数值,然后用这个整数值对 16384 进行取模来得到具体槽位。
HASH_SLOT = CRC16(key) mod 16384
跳转重定位
当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射
表。
网络抖动真实世界的机房网络往往并不是风平浪静的,它们经常会发生各种各样的小问题。比如网络抖动就是非常常见的一种现象,突然之间部分连接变得不可访问,然后很快又恢复正常。
为解决这种问题,Redis Cluster 提供了一种选项clusternodetimeout,表示当某个节点持续 timeout 的时间失联时,才可以认定该节点出现故障,需要进行主从切 换。如果没有这个选项,网络抖动会导致主从频繁切换 (数据的重新复制)。
集群搭建参考 :
https://blog.csdn.net/adolph586/article/details/85340764
https://www.cnblogs.com/djlsunshine/p/10635022.html
https://blog.csdn.net/weixin_42456466/article/details/87270959
报错 :
=================================================
redis.clients.jedis.exceptions.JedisConnectionException: Could not get a resource from the pool
at redis.clients.util.Pool.getResource(Pool.java:53)
at redis.clients.jedis.JedisPool.getResource(JedisPool.java:226)
at com.atguigu.bigdata.sparkmall.common.RedisUtil$.getJedisClient(RedisUtil.scala:26)
at com.atguigu.bigdata.sparkmall.realtime.Req7TimeAdvertClickTrendApplication$$anonfun$main$1$$anonfun$apply$2.apply(Req7TimeAdvertClickTrendApplication.scala:54)
at com.atguigu.bigdata.sparkmall.realtime.Req7TimeAdvertClickTrendApplication$$anonfun$main$1$$anonfun$apply$2.apply(Req7TimeAdvertClickTrendApplication.scala:53)
at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:926)
at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$29.apply(RDD.scala:926)
at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1951)
at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1951)
at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:87)
at org.apache.spark.scheduler.Task.run(Task.scala:99)
at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:322)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
Caused by: redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused: connect
at redis.clients.jedis.Connection.connect(Connection.java:207)
at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
at redis.clients.jedis.BinaryJedis.connect(BinaryJedis.java:1767)
at redis.clients.jedis.JedisFactory.makeObject(JedisFactory.java:106)
at org.apache.commons.pool2.impl.GenericObjectPool.create(GenericObjectPool.java:868)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:435)
at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:363)
at redis.clients.util.Pool.getResource(Pool.java:49)
... 14 more
Caused by: java.net.ConnectException: Connection refused: connect
at java.net.DualStackPlainSocketImpl.waitForConnect(Native Method)
at java.net.DualStackPlainSocketImpl.socketConnect(DualStackPlainSocketImpl.java:85)
at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
服务器中,redis集群是正常的,可以使用命令连接,并且可以正常的存值取值、同步数据等。然后用代码连接服务器的redis集群就报以上错。(用单个Jedis连接就是正常的,用JedisCluster就报错)
猜测:
可能是网络模式的原因,我觉得网桥docker0只有一个ip地址,如果用Jrediscluster(集群)去连接,就会去竞争ip地址和端口,或者是ip和端口冲突;就一直不断创建连接,一直达到最大数目,然后返回,不能再从连接池中获取连接了 ,而用Jredis去连接,只有一个ip和端口,就不会冲突了,就可以连接成功。
解决方案 :
采用 --net host 启动容器,然后关闭防火墙。
=================================================自我总结:
老师的Docker部署微服务 这个里面的pdf 的linux命令是乱码 , 需要在linux里面手动照着敲, 否则安装不成功。
docker安装redis :
https://www.jianshu.com/p/ede209c259a9
https://blog.csdn.net/EHOIST/article/details/88262250