redis哨兵模式
Redis哨兵高可用架构

sentinel哨兵是特殊的redis服务,不提供读写服务,主要用来监控redis实例节点。
哨兵架构下client端第一次从哨兵找出redis的主节点,后续就直接访问redis的主节点,不会每次都通过sentinel代理访问redis的主节点,当redis的主节点发生变化,哨兵会第一时间感知到,并且将新的redis主节点通知给client端(这里面redis的client端一般都实现了订阅功能,订阅sentinel发布的节点变动消息)
redis哨兵架构搭建步骤:
1、复制一份sentinel.conf文件 cp sentinel.conf sentinel-26379.conf 2、将相关配置修改为如下值: port 26379 daemonize yes pidfile "/var/run/redis-sentinel-26379.pid" logfile "26379.log" dir "/usr/local/redis-5.0.3/data" # sentinel monitor <master-redis-name> <master-redis-ip> <master-redis-port> <quorum> # quorum是一个数字,指明当有多少个sentinel认为一个master失效时(值一般为:sentinel总数/2 + 1),master才算真正失效 sentinel monitor mymaster 192.168.0.60 6379 2 # mymaster这个名字随便取,客户端访问时会用到 3、启动sentinel哨兵实例 src/redis-sentinel sentinel-26379.conf 4、查看sentinel的info信息 src/redis-cli -p 26379 127.0.0.1:26379>info 可以看到Sentinel的info里已经识别出了redis的主从 5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改
sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:
sentinel known-replica mymaster 192.168.0.60 6380 #代表redis主节点的从节点信息 sentinel known-replica mymaster 192.168.0.60 6381 #代表redis主节点的从节点信息 sentinel known-sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c56935760f #代表感知到的其它哨兵节点 sentinel known-sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8bd5ca6 #代表感知到的其它哨兵节点
当redis主节点如果挂了,哨兵集群会重新选举出新的redis主节点,同时会修改所有sentinel节点配置文件的集群元数据信息,比如6379的redis如果挂了,假设选举出的新主节点是6380,则sentinel文件里的集群元数据信息会变成如下所示:
sentinel known-replica mymaster 192.168.0.60 6379 #代表主节点的从节点信息 sentinel known-replica mymaster 192.168.0.60 6381 #代表主节点的从节点信息 sentinel known-sentinel mymaster 192.168.0.60 26380 52d0a5d70c1f90475b4fc03b6ce7c3c56935760f #代表感知到的其它哨兵节点 sentinel known-sentinel mymaster 192.168.0.60 26381 e9f530d3882f8043f76ebb8e1686438ba8bd5ca6 #代表感知到的其它哨兵节点
同时还会修改sentinel文件里之前配置的mymaster对应的6379端口,改为6380
sentinel monitor mymaster 192.168.0.60 6380 2
当6379的redis实例再次启动时,哨兵集群根据集群元数据信息就可以将6379端口的redis节点作为从节点加入集群
哨兵的Jedis连接代码:
public class JedisSentinelTest { public static void main(String[] args) throws IOException { JedisPoolConfig config = new JedisPoolConfig(); config.setMaxTotal(20); config.setMaxIdle(10); config.setMinIdle(5); String masterName = "mymaster"; Set<String> sentinels = new HashSet<String>(); sentinels.add(new HostAndPort("192.168.0.60",26379).toString()); sentinels.add(new HostAndPort("192.168.0.60",26380).toString()); sentinels.add(new HostAndPort("192.168.0.60",26381).toString()); //JedisSentinelPool其实本质跟JedisPool类似,都是与redis主节点建立的连接池 //JedisSentinelPool并不是说与sentinel建立的连接池,而是通过sentinel发现redis主节点并与其建立连接 JedisSentinelPool jedisSentinelPool = new JedisSentinelPool(masterName, sentinels, config, 3000, null); Jedis jedis = null; try { jedis = jedisSentinelPool.getResource(); System.out.println(jedis.set("sentinel", "zhuge")); System.out.println(jedis.get("sentinel")); } catch (Exception e) { e.printStackTrace(); } finally { //注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。 if (jedis != null) jedis.close(); } } }
哨兵的Spring Boot整合Redis连接代码见示例项目:redis-sentinel-cluster
1、引入相关依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
springboot项目核心配置:
1 server: 2 port: 8080 3 4 spring: 5 redis: 6 database: 0 7 timeout: 3000 8 sentinel: #哨兵模式 9 master: mymaster #主服务器所在集群名称 10 nodes: 192.168.0.60:26379,192.168.0.60:26380,192.168.0.60:26381 11 lettuce: 12 pool: 13 max-idle: 50 14 min-idle: 10 15 max-active: 100 16 max-wait: 1000 17
访问代码:
1 @RestController 2 public class IndexController { 3 4 private static final Logger logger = LoggerFactory.getLogger(IndexController.class); 5 6 @Autowired 7 private StringRedisTemplate stringRedisTemplate; 8 9 /** 10 * 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到 11 * 新的master选举出来后,哨兵会把消息发布出去,客户端实际上是实现了一个消息监听机制, 12 * 当哨兵把新master的消息发布出去,客户端会立马感知到新master的信息,从而动态切换访问的masterip 13 * 14 * @throws InterruptedException 15 */ 16 @RequestMapping("/test_sentinel") 17 public void testSentinel() throws InterruptedException { 18 int i = 1; 19 while (true){ 20 try { 21 stringRedisTemplate.opsForValue().set("zhuge"+i, i+""); 22 System.out.println("设置key:"+ "zhuge" + i); 23 i++; 24 Thread.sleep(1000); 25 }catch (Exception e){ 26 logger.error("错误:", e); 27 } 28 } 29 } 30 }
StringRedisTemplate与RedisTemplate详解
spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在RedisTemplate中提供了几个常用的接口方法的使用,分别是:
1 private ValueOperations<K, V> valueOps; 2 private HashOperations<K, V> hashOps; 3 private ListOperations<K, V> listOps; 4 private SetOperations<K, V> setOps; 5 private ZSetOperations<K, V> zSetOps;
RedisTemplate中定义了对5种数据结构操作
1 redisTemplate.opsForValue();//操作字符串 2 redisTemplate.opsForHash();//操作hash 3 redisTemplate.opsForList();//操作list 4 redisTemplate.opsForSet();//操作set 5 redisTemplate.opsForZSet();//操作有序set
StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。
StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。
RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。
Redis客户端命令对应的RedisTemplate中的方法列表:
String类型结构
|
|
Redis
|
RedisTemplate rt
|
set key value
|
rt.opsForValue().set("key","value")
|
get key
|
rt.opsForValue().get("key")
|
del key
|
rt.delete("key")
|
strlen key
|
rt.opsForValue().size("key")
|
getset key value
|
rt.opsForValue().getAndSet("key","value")
|
getrange key start end
|
rt.opsForValue().get("key",start,end)
|
append key value
|
rt.opsForValue().append("key","value")
|
Hash结构
|
|
hmset key field1 value1 field2 value2...
|
rt.opsForHash().putAll("key",map) //map是一个集合对象
|
hset key field value
|
rt.opsForHash().put("key","field","value")
|
hexists key field
|
rt.opsForHash().hasKey("key","field")
|
hgetall key
|
rt.opsForHash().entries("key") //返回Map对象
|
hvals key
|
rt.opsForHash().values("key") //返回List对象
|
hkeys key
|
rt.opsForHash().keys("key") //返回List对象
|
hmget key field1 field2...
|
rt.opsForHash().multiGet("key",keyList)
|
hsetnx key field value
|
rt.opsForHash().putIfAbsent("key","field","value"
|
hdel key field1 field2
|
rt.opsForHash().delete("key","field1","field2")
|
hget key field
|
rt.opsForHash().get("key","field")
|
|
|
List结构
|
|
lpush list node1 node2 node3...
|
rt.opsForList().leftPush("list","node")
|
rt.opsForList().leftPushAll("list",list) //list是集合对象
|
|
rpush list node1 node2 node3...
|
rt.opsForList().rightPush("list","node")
|
rt.opsForList().rightPushAll("list",list) //list是集合对象
|
|
lindex key index
|
rt.opsForList().index("list", index)
|
llen key
|
rt.opsForList().size("key")
|
lpop key
|
rt.opsForList().leftPop("key")
|
rpop key
|
rt.opsForList().rightPop("key")
|
lpushx list node
|
rt.opsForList().leftPushIfPresent("list","node")
|
rpushx list node
|
rt.opsForList().rightPushIfPresent("list","node")
|
lrange list start end
|
rt.opsForList().range("list",start,end)
|
lrem list count value
|
rt.opsForList().remove("list",count,"value")
|
lset key index value
|
rt.opsForList().set("list",index,"value")
|
Set结构
|
|
sadd key member1 member2...
|
rt.boundSetOps("key").add("member1","member2",...)
|
rt.opsForSet().add("key", set) //set是一个集合对象
|
|
scard key
|
rt.opsForSet().size("key")
|
sidff key1 key2
|
rt.opsForSet().difference("key1","key2") //返回一个集合对象
|
sinter key1 key2
|
rt.opsForSet().intersect("key1","key2")//同上
|
sunion key1 key2
|
rt.opsForSet().union("key1","key2")//同上
|
sdiffstore des key1 key2
|
rt.opsForSet().differenceAndStore("key1","key2","des")
|
sinter des key1 key2
|
rt.opsForSet().intersectAndStore("key1","key2","des")
|
sunionstore des key1 key2
|
rt.opsForSet().unionAndStore("key1","key2","des")
|
sismember key member
|
rt.opsForSet().isMember("key","member")
|
smembers key
|
rt.opsForSet().members("key")
|
spop key
|
rt.opsForSet().pop("key")
|
srandmember key count
|
rt.opsForSet().randomMember("key",count)
|
srem key member1 member2...
|
rt.opsForSet().remove("key","member1","member2",...)
|
多情自古空余恨
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?