redis部署、主从、故障转移、集群
Redis安装
tar -xvf GCC 4.2.bz2
mkdir /usr/gcc4
configure –prefix=/usr/gcc4 进入解压后目录执行
make install
redis http://www.cnblogs.com/codersay/p/4301677.html
1、下载源码,解压缩后编译源码。
$ wget http://download.redis.io/releases/redis-2.8.3.tar.gz
$ tar xzf redis-2.8.3.tar.gz
$ cd redis-2.8.3
$ make
2、 编译完成后,在Src目录下,有四个可执行文件redis-server、redis-benchmark、redis-cli和redis.conf。然后拷贝到一个目录下。
mkdir /usr/redis
cp redis-server /usr/redis
cp redis-benchmark /usr/redis
cp redis-cli /usr/redis
cp redis.conf /usr/redis redis.conf 在外层
cd /usr/redis
3、 启动Redis服务。
$ redis-server redis.conf
4、 然后用客户端测试一下是否启动成功。
$ redis-cli
redis> set foo bar
OK
redis> get foo
5、 摘要: redis作为一种NoSql数据库,其提供了一种高效的缓存方案,本文则主要对其单例,主从模式,sentinel以及集群的配置方式进行说明,对比其优缺点,阐述redis作为一种缓存框架的高可用性。
redis作为一种高效的缓存框架,使用是非常广泛的,在数据存储上,在运行时其将数据存储在内存中,以实现数据的高效读写,并且根据定制的持久化规则不同,其会不定期的将数据持久化到硬盘中。
另外相较于其他的NoSql数据库,redis提供了非常丰富的数据结构,如dict,sds,linkedlist,ziplist,set,quicklist,geometry。
在这些存储结构的基础上,redis为用户提供了非常丰富的操作选择,如通过zskiplist来达到对某种类型的数据的排序目的,而排序在数据库中是一个非常耗时的操作。
http://www.redis.cn/topics/cluster-tutorial.html
1,主从配置,读写分离
2,哨兵主节点故障转移,sentinel架构的主要作用是解决主从模式下主节点的故障转移工作的
3,集群,当遇到单机内存,并发和流量瓶颈等问题时,可采用Cluster方案达到负载均衡的目的
然而如果在集群创建的时候(或者过一段时间)我们为每个节点添加一个从节点A1,B1,C1,那么整个集群便有三个master节点和三个slave节点组成,这样在节点B失败后,集群便会选举B1为新的主节点继续服务,整个集群便不会因为槽找不到而不可用了
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽.集群的每个节点负责一部分hash槽,举个例子,比如当前集群有3个节点,那么:
节点 A 包含 0 到 5500号哈希槽.
节点 B 包含5501 到 11000 号哈希槽.
节点 C 包含11001 到 16384号哈希槽.
这种结构很容易添加或者删除节点. 比如如果我想新添加个节点D, 我需要从节点 A, B, C中得部分槽到D上. 如果我想移除节点A,需要将A中的槽移到B和C节点上,然后将没有任何槽的A节点从集群中移除即可. 由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态.
1,Redis 主从模式的配置
主从模式指的是使用一个redis实例作为主机,其余的实例作为备份机。
主机和从机的数据完全一致,主机支持数据的写入和读取等各项操作,而从机则只支持与主机数据的同步和读取,也就是说,客户端可以将数据写入到主机,由主机自动将数据的写入操作同步到从机。
主从模式很好的解决了数据备份问题,并且由于主从服务数据几乎是一致的,因而可以将写入数据的命令发送给主机执行,而读取数据的命令发送给不同的从机执行,从而达到读写分离的目的。
如下所示主机redis-A分别有redis-B、redis-C、redis-D、redis-E四个从机
前面第1点中我们已经介绍了redis单例的配置方式,而上面我们也介绍了主从模式其实也是多个redis实例组成的,因而redis主从模式的配置可以理解为多个不同的redis实例通过一定的配置告知其相互之间的主从关系。
而前面已经介绍,每个redis实例都会占用一个本机的端口号,主从模式的配置主要的配置点有两个:当前实例端口号和当前实例是主机还是从机,是从机的话其主机的ip和端口是什么。
一般的redis目录下的redis.conf保存的是默认配置,尽量不要对其进行修改,这里我们复制三份redis.conf文件,分别命名为6379.conf,6380.conf和6381.conf,如下是端口为6379的主机的主要配置:
bind 127.0.0.1
port 6379
logfile "6379.log" --不建议修改
dbfilename "dump-6379.rdb"
如下是端口为6380和6381的从机的配置:
bind 127.0.0.1
port 6380
logfile "6380.log"
dbfilename "dump-6380.rdb"
slaveof 127.0.0.1 6379
bind 127.0.0.1
port 6381
logfile "6381.log"
dbfilename "dump-6381.rdb"
slaveof 127.0.0.1 6379
可以看到,端口为6380和6381的实例被配置为端口为6379的实例的从机。配置完成后使用redis-server分别执行如下命令启动三个实例:
./src/redis-server 6379.conf
./src/redis-server 6380.conf
./src/redis-server 6381.conf
启动之后分别开启开启三个命令行工具分别执行以下命令连接redis实例:
./src/redis-cli -p 6379
./src/redis-cli -p 6380
./src/redis-cli -p 6381
分别在三个命令行工具中执行一个get命令,获取键名为msg的数据,如下所示:
127.0.0.1:6379> get msg
(nil)
127.0.0.1:6380> get msg
(nil)
127.0.0.1:6381> get msg
(nil)
可以看到,在三个redis实例中都不存在键为msg的数据,现在我们在主机6379上设置一个键为msg的数据,如下所示:
127.0.0.1:6379> set msg "hello"
OK
可以看到设置成功了,此时我们在6380和6381的实例上执行get msg的命令,如下所示:
127.0.0.1:6380> get msg
"hello"
127.0.0.1:6381> get msg
"hello"
可以看到,虽然我们只是在6379的实例上设置了msg这条数据,但是在6380和6381的实例上也存有了相应的数据,说明我们成功配置了redis的主从模式。
另外,如果不在配置文件中指定主从节点的关系,也可以在启动相关redis实例之后使用slaveof命令来指定当前节点称为某个节点的从节点,如:
127.0.0.1:6380> slaveof 127.0.0.1 6379
2,sentinel(哨兵),主节点故障转移
redis主从模式解决了数据备份和单例可能存在的性能问题,但是其也引入了新的问题。
由于主从模式配置了三个redis实例,并且每个实例都使用不同的ip(如果在不同的机器上)和端口号。
根据前面所述,主从模式下可以将读写操作分配给不同的实例进行从而达到提高系统吞吐量的目的,但也正是因为这种方式造成了使用上的不便,因为每个客户端连接redis实例的时候都是指定了ip和端口号的,如果所连接的redis实例因为故障下线了,而主从模式也没有提供一定的手段通知客户端另外可连接的客户端地址,因而需要手动更改客户端配置重新连接。
另外,主从模式下,如果主节点由于故障下线了,那么从节点因为没有主节点而同步中断,因而需要人工进行故障转移工作。
为了解决这两个问题,在2.8版本之后redis正式提供了sentinel(哨兵)架构。关于sentinel,这里需要说明几个概念:
每个sentinel节点其实就是一个redis实例,与主从节点不同的是sentinel节点作用是用于监控redis数据节点的,而sentinel节点集合则表示监控一组主从redis实例多个sentinel监控节点的集合,比如有主节点master和从节点slave-1、slave-2。
为了监控这三个主从节点,这里配置N个sentinel节点sentinel-1,sentinel-2,...,sentinel-N。
如下图是sentinel监控主从节点的示例图:
从图中可以看出,对于一组主从节点,sentinel只是在其外部额外添加的一组用于监控作用的redis实例。
在主从节点和sentinel节点集合配置好之后,sentinel节点之间会相互发送消息,以检测其余sentinel节点是否正常工作,并且sentinel节点也会向主从节点发送消息,以检测监控的主从节点是否正常工作。
前面讲到,sentinel架构的主要作用是解决主从模式下主节点的故障转移工作的。
这里如果主节点因为故障下线,那么某个sentinel节点发送检测消息给主节点时;
如果在指定时间内收不到回复,那么该sentinel就会主观的判断该主节点已经下线,那么其会发送消息给其余的sentinel节点,询问其是否“认为”该主节点已下线,其余的sentinel收到消息后也会发送检测消息给主节点;
如果其认为该主节点已经下线,那么其会回复向其询问的sentinel节点,告知其也认为主节点已经下线,当该sentinel节点最先收到超过指定数目(配置文件中配置的数目和当前sentinel节点集合数的一半,这里两个数目的较大值)的sentinel节点回复说当前主节点已下线,那么其就会对主节点进行故障转移工作。
故障转移的基本思路是在从节点中选取某个从节点向其发送slaveof no one(假设选取的从节点为127.0.0.1:6380),使其称为独立的节点(也就是新的主节点),然后sentinel向其余的从节点发送slaveof 127.0.0.1 6380命令使它们重新成为新的主节点的从节点。
重新分配之后sentinel节点集合还会继续监控已经下线的主节点(假设为127.0.0.1:6379),如果其重新上线,那么sentinel会向其发送slaveof命令,使其成为新的主机点的从节点,如此故障转移工作完成。
上面我们讲到了,每个sentinel节点在本质上还是一个redis实例,只不过和redis数据节点不同的是,其主要作用是监控redis数据节点。
在redis安装目录下有个默认的sentinel配置文件sentinel.conf,和配置主从节点类似,这里复制三个配置文件:sentinel-26379.conf,sentinel-26380.conf和sentinel-26381.conf。分别按照如下示例编辑这三个配置文件:
port 26379
daemonize yes
logfile "26379.log"
dir /opt/soft/redis/data
sentinel monitor mymaster 127.0.0.1 6379 2
sentinel down-after-milliseconds mymaster 30000
sentinel parallel-syncs mymaster 1
sentinel failover-timeout mymaster 180000
sentinel myid mm55d2d712b1f3f312b637f9b546f00cdcedc787
对于端口为26380和26381的sentinel,其配置和上述类似,只需要把相应的端口号修改为对应的端口号即可。
这里注意两点:
①每个sentinel的myid参数也要进行修改,因为sentinel之间是通过该属性来唯一区分其他sentinel节点的;
②参数中sentinel monitor mymaster
127.0.0.1 6379 2这里的端口号6379是不用更改的,因为sentinel是通过检测主节点的状态来得知当前主节点的从节点有哪些的,因而设置为主节点的端口号即可。
配置完成后我们首先启动三个主从节点,然后分别使用三个配置文件使用如下命令启用sentinel:
./src/redis-sentinel sentinel-26379.conf
./src/redis-sentinel sentinel-26380.conf
./src/redis-sentinel sentinel-26381.conf
由于sentinel节点也是一个redis实例,因而我们可以通过如下命令使用redis-cli连接sentinel节点:
./src/redis-cli -p 26379
连上sentinel节点之后我们可以通过如下命令查看sentinel状态:
127.0.0.1:26379> info sentinel
结果如下:
# Sentinel
sentinel_masters:1
sentinel_tilt:0
sentinel_running_scripts:0
sentinel_scripts_queue_length:0
sentinel_simulate_failure_flags:0
master0:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=2,sentinels=3
可以看到,sentinel检测到主从节点总共有三个,其中一个主节点,两个从节点,并且sentinel节点总共也有三个。
启动完成之后,我们可以通过主动下线主节点来模拟sentinel的故障转移过程。首先我们连接上端口为6379的主节点,使用如下命令查看主从节点状态:
127.0.0.1:6379> info replication
结果如下:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6380,state=online,offset=45616,lag=1
slave1:ip=127.0.0.1,port=6381,state=online,offset=45616,lag=1
master_repl_offset:45616
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:45615
可以看到,当前主节点有两个从节点,端口分别为6380和6381。然后我们对主节点执行如下命令:
127.0.0.1:6379> shutdown save
然后我们连接上端口号为6380的从节点,并执行如下命令:
127.0.0.1:6380> info replication
结果如下:
# Replication
role:master
connected_slaves:1
slave0:ip=127.0.0.1,port=6381,state=online,offset=12344,lag=0
master_repl_offset:12477
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:12476
可以看到,当端口为6379的实例下线之后,端口为6380的实例被重新竞选为新的主节点,并且端口为6381的实例被设置为6380的实例的从节点。
如果我们此时重新启用端口为6379的节点,然后再查看主从状态,结果如下:
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6381,state=online,offset=59918,lag=0
slave1:ip=127.0.0.1,port=6379,state=online,offset=59918,lag=1
master_repl_offset:60051
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:2
repl_backlog_histlen:60050
可以看到,端口为6379的redis实例重新连接后,sentinel节点检测到其重新连接,那么对其发送命令,使其成为新的主节点的从节点。
3,cluster集群
redis集群是在redis 3.0版本推出的一个功能,其有效的解决了redis在分布式方面的需求。当遇到单机内存,并发和流量瓶颈等问题时,可采用Cluster方案达到负载均衡的目的。
并且从另一方面讲,redis中sentinel有效的解决了故障转移的问题,也解决了主节点下线客户端无法识别新的可用节点的问题,但是如果是从节点下线了,sentinel是不会对其进行故障转移的,并且连接从节点的客户端也无法获取到新的可用从节点,而这些问题在Cluster中都得到了有效的解决。
redis集群中数据是和槽(slot)挂钩的,其总共定义了16384个槽,所有的数据根据一致哈希算法会被映射到这16384个槽中的某个槽中。
另一方面,这16384个槽是按照设置被分配到不同的redis节点上的,比如启动了三个redis实例:cluster-A,cluster-B和cluster-C,这里将0-5460号槽分配给cluster-A,将5461-10922号槽分配给cluster-B,将10923-16383号槽分配给cluster-C(总共有16384个槽,但是其标号类似数组下标,是从0到16383)。
也就是说数据的存储只和槽有关,并且槽的数量是一定的,由于一致hash算法是一定的,因而将这16384个槽分配给无论多少个redis实例,对于确认的数据其都将被分配到确定的槽位上。redis集群通过这种方式来达到redis的高效和高可用性目的。
这里需要进行说明的一点是,一致哈希算法根据数据的key值计算映射位置时和所使用的节点数量有非常大的关系。
一致哈希分区的实现思路是为系统中每个节点分配一个token,范围一般在0~2^32,这些token构成一个哈希环,数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该hash值的token节点,需要操作的数据就保存在该节点上。
通过分析可以发现,一致哈希分区存在如下问题:
加减节点会造成哈希环中部分数据无法命中,需要手动处理或忽略这部分数据;
当使用少量节点时,节点变化将大范围影响环中数据映射,因此这种方式不适合少量节点的分布式方案;
普通的一致性哈希分区在增减节点时需要增加一倍或减去一半节点才能保证数据和负载的平衡。
正是由于一致哈希分区的这些问题,redis使用了虚拟槽来处理分区时节点变化的问题,也即将所有的数据映射到16384个虚拟槽位上,当redis节点变化时数据映射的槽位将不会变化,并且这也是redis进行节点扩张的基础。
对于redis集群的配置,首先将redis安装目录下的redis.conf文件复制六份,分别取名为:cluster-6379.conf、cluster-6380.conf、cluster-6381.conf、cluster-6382.conf、cluster-6383.conf、cluster-6384.conf。
对于一个高可用的集群方案,集群每个节点都将为其分配一个从节点,以防止数据节点因为故障下线,这里使用六份配置文件定义六个redis实例,其中三个作为主节点,剩余三个分别作为其从节点。对于这六份配置文件,以其中一份为例,以下是其需要修改的参数:
port 6379
cluster-enabled yes
cluster-node-timeout 15000
cluster-config-file "nodes-6379.conf"
pidfile /var/run/redis_6379.pid
logfile "cluster-6379.log"
dbfilename dump-cluster-6379.rdb
appendfilename "appendonly-cluster-6379.aof"
对于其余的配置文件,只需要将其中对应项的端口号和带有端口号的文件名修改为当前要指定的端口号和端口号的文件名即可。
配置文件配置好之后使用如下命令启动集群中的每个实例:
./src/redis-server cluster-6379.conf
./src/redis-server cluster-6380.conf
./src/redis-server cluster-6381.conf
./src/redis-server cluster-6382.conf
./src/redis-server cluster-6383.conf
./src/redis-server cluster-6384.conf
仔细阅读上述配置文件可发现,当前配置和启动过程中并没有指定这六个实例的主从关系,也没有对16384个槽位进行分配。
因而我们还需要进行进一步的配置,槽位的分配和主从关系的设定有两种方式进行,一种是使用redis-cli连接到集群节点上后使用cluster meet命令连接其他的节点,如我们首先执行如下命令连接到6379端口的节点:
./src/redis-cli -p 6379
连接上后使用cluster meet命令分别连接其余节点:
127.0.0.1:6379>cluster meet 127.0.0.1 6380
127.0.0.1:6379>cluster meet 127.0.0.1 6381
127.0.0.1:6379>cluster meet 127.0.0.1 6382
127.0.0.1:6379>cluster meet 127.0.0.1 6383
127.0.0.1:6379>cluster meet 127.0.0.1 6384
连接好后可以使用cluster nodes命令查看当前集群状态:
127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 master - 0
1468073975551 5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0
connected
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 master - 0
1468073978579 4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 master - 0
1468073980598 3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0
1468073974541 1 connected
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0
1468073979589 2 connected
可以看到配置的六个节点都已经加入到了集群中,但是其现在还不能使用,因为还没有将16384个槽分配到集群节点中。虚拟槽的分配可以使用redis-cli分别连接到6379,6380和6381端口的节点中,然后分别执行如下命令:
127.0.0.1:6379>cluster addslots {0...5461}
127.0.0.1:6380>cluster addslots {5462...10922}
127.0.0.1:6381>cluster addslots {10923...16383}
需批量分配曹#!/bin/bash 执行以下shell脚本
n=0
for ((i=n;i<=8191;i++))
do
/home/xmh/soft/redis/redis6379/my/redis-cli -h 127.0.0.1 -p 6379
-a dxy CLUSTER ADDSLOTS $i
done
#!/bin/bash
n=8192
for ((i=n;i<=16383;i++))
do
/home/xmh/soft/redis/redis6380/my/redis-cli -h 127.0.0.1 -p 6380
-a dxy CLUSTER ADDSLOTS $i
done
添加完槽位后可使用cluster info命令查看当前集群状态:
127.0.0.1:6379> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:4874
cluster_stats_messages_received:4726
这里我们将16384个虚拟槽位分配给了三个节点,而剩余的三个节点我们通过如下命令将其配置为这三个节点的从节点,从而达到高可用的目的:
127.0.0.1:6382>cluster replicate cfb28ef1deee4e0fa78da86abe5d24566744411e
OK
127.0.0.1:6383>cluster replicate 8e41673d59c9568aa9d29fb174ce733345b3e8f1
OK
127.0.0.1:6384>cluster replicate 40b8d09d44294d2e23c7c768efc8fcd153446746
OK
如此,所有的集群节点都配置完毕,并且处于可用状态。这里可以使用cluster
nodes命令查看当前节点的状态:
127.0.0.1:6379> cluster nodes
4fa7eac4080f0b667ffeab9b87841da49b84a6e4 127.0.0.1:6384 slave
40b8d09d44294d2e23c7c768efc8fcd153446746 0 1468076865939 5 connected
cfb28ef1deee4e0fa78da86abe5d24566744411e 127.0.0.1:6379 myself,master - 0 0 0
connected 0-5461
be9485a6a729fc98c5151374bc30277e89a461d8 127.0.0.1:6383 slave
8e41673d59c9568aa9d29fb174ce733345b3e8f1 0 1468076868966 4 connected
40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 slave
cfb28ef1deee4e0fa78da86abe5d24566744411e 0 1468076869976 3 connected
8e41673d59c9568aa9d29fb174ce733345b3e8f1 127.0.0.1:6380 master - 0
1468076870987 1 connected 5462-10922
40b8d09d44294d2e23c7c768efc8fcd153446746 127.0.0.1:6381 master - 0
1468076867957 2 connected 10923-16383
我们使用redis-cli使用如下命令连接集群:
./src/redis-cli -c -p 6380
注意连接集群模式的redis实例时需要加上参数-c,表示连接的是集群模式的实例。连接上后执行get命令:
127.0.0.1:6380> get hello-> Redirected to slot [866] located at
127.0.0.1:6379(nil)
可以看到,在6380端口的实例上执行get命令时,其首先会为当前的键通过一致哈希算法计算其所在的槽位,并且判断该槽位不在当前redis实例中,因而重定向到目标实例上执行该命令,最后发现没有该键对应的值,因而返回了一个(nil)。
注意事项
试验中一个redis对应一个redis.conf
redis主从:将make好的redis复制一份,更改端口、主从依赖关系
redis哨兵:在主redis中,复制多个哨兵文件分配端口启动,文件中主redis配置不动
哨兵重新分配主节点后,配置文件被改动
集群:集群配置时只修改配置文件中cluster属性,获取命令不加-c时只能从主节点获取数据,
从节点有主节点数据但获取不了
4,主从、哨兵配置
1,sentinel架构的主要作用是解决主从模式下主节点的故障转移工作的
2,如果其重新上线,那么sentinel会向其发送slaveof命令,使其成为新的主机点的从节点,如此故障转移工作完成。
3,启动一组主从redis、一组sentinel
info replication 查看主从节点状态
info sentinel 查看sentinel节点状态
主从配置注意事项:
1,设置redis从机时只需修改 port 6380 slaveof 127.0.0.1 6379 如修改logfile导致从机启动失败
#bind localhost 注释掉 (代码连接需要)
#protected-mode yes 注释掉 (代码连接需要)
主机配置 requirepass 123 (代码连接需要)
从机配置主机认证连接密码masterauth 123 (代码连接需要)
2,指定端口启动客户端
redis-cli -p 6380
redis-cli -h 127.0.0.1 -p 6379 -a 123 如果服务有密码需用密码连接
info replication 查看主从配置
哨兵测试注意事项:
port 26379 哨兵端口
sentinel monitor mymaster 127.0.0.1 6379 2 监听的主redis最后一个数字表示,当有几台哨兵监控到主机出错后执行主从切换
sentinel myid xxxxxxx 需修改哨兵唯一标示,也可将其删除
sentinel auth-pass mymaster 123 如果服务有密码需用密码连接(需配置在监听配置的后面)
protected-mode no 该配置放开(如果不放开,在把127.0.0.1替换为真实IP会有问题)
哨兵重新分配主节点后,配置文件被改动 slaveof 127.0.0.1 6379
连接哨兵 redis-cli -p 26380
哨兵个数最好与redis个数一致
info sentinel 查看哨兵配置

6381下线后选举 6380 为主服务

6381 重新上线后,哨兵将其作为当前主服务6380的从服务

此图为6380redis服务日志,6381下线后该服务升级为主,并使6379作为其从服务
5,代码连接redis
<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
private static Jedis jedis;
static{
//jedis = new Jedis("192.168.89.131", 6379);
//权限认证
//jedis.auth("123");
//System.out.println("连接服务成功");
}
public static void main(String[] args) {
Set<String> sen = new HashSet<String>();
sen.add("192.168.89.131:26379");
sen.add("192.168.89.131:26380");
sen.add("192.168.89.131:26381");
JedisPoolConfig config = new JedisPoolConfig();
// // 设置最大连接数
config.setMaxTotal(10);
// // 设置最大阻塞时间,记住是毫秒数milliseconds
config.setMaxWaitMillis(10*1000);
// // 设置空间连接
config.setMaxIdle(0);
//在获取连接的时候检查有效性, 默认false
config.setTestOnBorrow(false);
//JedisSentinelPool jsp = new JedisSentinelPool("mymaster",sen,"123");
JedisSentinelPool jsp = new JedisSentinelPool("resque",
sen, config,
33*1000, "123", 0);
Jedis je = null;
try{
je = jsp.getResource();
je.set("my", "mywww");
String s = je.get("my");
System.out.println(s);
System.out.println(je.get("xmh"));
}catch(Exception e){
e.printStackTrace();
}
jsp.close();
System.out.println("OK");
//jedis.set("name", "chx11"); //key为name放入value值为chx
//System.out.println("拼接前:" + jedis.get("name"));//读取key为name的值
//向key为name的值后面加上数据 ---拼接
//jedis.append("name", " is my name;");
//System.out.println("拼接后:" + jedis.get("name"));
}
1,关闭firewall
service iptables stop
service iptables status
2,把Redis的配置文件redis.conf里
#bind localhost 注释掉
#protected-mode yes 注释掉
3,客户端命令执行:config set requirepass 123
程序中权限认证方式连接redis,认证密码为123
redis客户端也需要用密码 连接 redis-cli -h 127.0.0.1 -p 6379 -a 123 密码登录
4,连接sentinel哨兵
需要把redis及哨兵中127.0.0.1替换为真实IP,其中哨兵配置中protected-mode no 需如此配置
6,术语
(1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽.
(2)节点的fail是通过集群中超过半数的master节点检测失效时才生效.
(3)客户端与redis节点直连,不需要中间proxy层.客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
(4)redis-cluster把所有的物理节点映射到[0-16383]slot上,cluster 负责维护node<->slot<->key
(1)领着选举过程是集群中所有master参与,如果半数以上master节点与故障节点通信超过(cluster-node-timeout),认为该节点故障,自动触发故障转移操作.
(2):什么时候整个集群不可用(cluster_state:fail)?
a:如果集群任意master挂掉,且当前master没有slave.集群进入fail状态,也可以理解成集群的slot映射[0-16383]不完成时进入fail状态. ps : redis-3.0.0.rc1加入cluster-require-full-coverage参数,默认关闭,打开集群兼容部分失败.
b:如果集群超过半数以上master挂掉,无论是否有slave集群进入fail状态.
ps:当集群不可用时,所有对集群的操作做都不可用,收到((error) CLUSTERDOWN The cluster is down)错误
1):节点自动发现
2):slave->master 选举,集群容错
3):Hot resharding:在线分片
4):集群管理:cluster xxx
5):基于配置(nodes-port.conf)的集群管理
6):ASK 转向/MOVED 转向机制.
sentinel failover-timeout mymaster 900000
和当主从切换多久后认为主从切换失败
sentinel down-after-milliseconds mymaster 15000
这个配置设置的是当哨兵每秒对mymaster主机执行Ping操作时,超过15000毫秒后认为主机宕机

实现思想
获取锁的时候,使用setnx加锁,并使用expire命令为锁添加一个超时时间,超过该时间则自动释放锁,锁的value值为一个随机生成的UUID,通过此在释放锁的时候进行判断。
获取锁的时候还设置一个获取的超时时间,若超过这个时间则放弃获取锁。
释放锁的时候,通过UUID判断是不是该锁,若是该锁,则执行delete进行锁释放。
分布式锁的核心代码如下:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.exceptions.JedisException;
import java.util.List;
import java.util.UUID;
/**
* Created by liuyang on 2017/4/20.
*/
public class DistributedLock {
private final JedisPool jedisPool;
public DistributedLock(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 加锁
* @param locaName 锁的key
* @param acquireTimeout 获取超时时间
* @param timeout 锁的超时时间
* @return 锁标识
*/
public String lockWithTimeout(String locaName,
long acquireTimeout, long timeout) {
Jedis conn = null;
String retIdentifier = null;
try {
// 获取连接
conn = jedisPool.getResource();
// 随机生成一个value
String identifier = UUID.randomUUID().toString();
// 锁名,即key值
String lockKey = "lock:" + locaName;
// 超时时间,上锁后超过此时间则自动释放锁
int lockExpire = (int)(timeout / 1000);
// 获取锁的超时时间,超过这个时间则放弃获取锁
long end = System.currentTimeMillis() + acquireTimeout;
while (System.currentTimeMillis() < end) {
if (conn.setnx(lockKey, identifier) == 1) {
conn.expire(lockKey, lockExpire);
// 返回value值,用于释放锁时间确认
retIdentifier = identifier;
return retIdentifier;
}
// 返回-1代表key没有设置超时时间,为key设置一个超时时间
if (conn.ttl(lockKey) == -1) {
conn.expire(lockKey, lockExpire);
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
} catch (JedisException e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.close();
}
}
return retIdentifier;
}
/**
* 释放锁
* @param lockName 锁的key
* @param identifier 释放锁的标识
* @return
*/
public boolean releaseLock(String lockName, String identifier) {
Jedis conn = null;
String lockKey = "lock:" + lockName;
boolean retFlag = false;
try {
conn = jedisPool.getResource();
while (true) {
// 监视lock,准备开始事务
conn.watch(lockKey);
// 通过前面返回的value值判断是不是该锁,若是该锁,则删除,释放锁
if (identifier.equals(conn.get(lockKey))) {
Transaction transaction = conn.multi();
transaction.del(lockKey);
List<Object> results = transaction.exec();
if (results == null) {
continue;
}
retFlag = true;
}
conn.unwatch();
break;
}
} catch (JedisException e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.close();
}
}
return retFlag;
}
}

浙公网安备 33010602011771号