SpringBoot基于哨兵模式的Redis(7.2)集群实现读写分离

文章目录

  • 一、前提条件
  • 二、SpringBoot访问Redis集群
      • 1. 引入依赖
      • 2. yaml配置
      • 3. 设置读写分离
      • 4. 简单的controller
  • 三、运行
  • 四、测试
      • 1. 写
      • 2. 读
      • 3. 额外测试

环境

  • docker desktop for windows 4.23.0
  • redis 7.2
  • Idea

一、前提条件

先根据以下文章搭建一个Redis集群

  • Docker-Compose部署Redis(v7.2)主从模式
  • Docker-Compose部署Redis(v7.2)哨兵模式

部署完后,redis集群看起来大致如下图
在这里插入图片描述

二、SpringBoot访问Redis集群

1. 引入依赖

需要注意的是lettuce-core版本问题,不能太旧,否则不兼容新版的Redis

		<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.1.4.RELEASE</version> <!-- 或更高版本 --></dependency>

2. yaml配置

application.yml加入以下配置。第一个password是用于sentinel节点验证,第二个password用于数据节点验证。

spring:redis:sentinel:master: mymasternodes:- 172.30.1.11:26379- 172.30.1.12:26379- 172.30.1.13:26379password: 1009password: 1009

这里关于sentinelip问题后面会讲解。

3. 设置读写分离

在任意配置类中写一个Bean,本文简单起见,直接写在SpringBoot启动类了。

    @Beanpublic LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);}

这里的ReadFrom是配置Redis的读取策略,是一个枚举,包括下面选择:

  • MASTER:从主节点读取
  • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica
  • REPLICA:从slave (replica)节点读取
  • REPLICA_PREFERRED:优先从slave (replica)节点读取,所有的slave都不可用才读取master

至于哪些节点支持读,哪些支持写,因为redis 7 默认给从节点设置为只读,所以可以认为只有主节点有读写权限,其余只有读权限。如果情况不一致,就手动给每一个redis-server的配置文件都加上这一行。

replica-read-only yes

4. 简单的controller

写一个简单的controller,等会用于测试。

@RestController
public class HelloController {@Autowiredprivate StringRedisTemplate redisTemplate;@GetMapping("/get/{key}")public String hi(@PathVariable String key) {return redisTemplate.opsForValue().get(key);}@GetMapping("/set/{key}/{value}")public String hi(@PathVariable String key, @PathVariable String value) {redisTemplate.opsForValue().set(key, value);return "success";}
}

三、运行

首先,因为所有redis节点都在一个docker bridge网络中,所以基于Idea编写的项目在宿主机(Windows)中运行spirngboot程序,不好去和redis集群做完整的交互。

虽然说无论是sentinel还是redis-server都暴露了端口到宿主机,我们可以通过映射的端口分别访问它们,但是我们的程序只访问sentinelsentinel管理redis-serversentinel会返回redis-serverip来让我们的程序来访问redis-server,这里的ipdocker bridge网络里的ip,所以即使我们的程序拿到ip也访问不了redis-server

这个时候就需要将我们的项目放到一个docker容器中运行,然后把这个容器放到和redis同一网络下,就像下图。
在这里插入图片描述
具体如何快捷让Idea结合Docker去运行SpringBoot程序,可以参考下面这篇文章。

  • Idea连接Docker在本地(Windows)开发SpringBoot

记得要暴露你的程序端口到宿主机,这样才方便测试。

四、测试

1. 写

浏览器访问localhost:8080/set/num/7799
在这里插入图片描述
查看SpringBoot容器日志,可以看到向主节点172.30.1.2:6379发送写请求。

01-06 07:23:59:848 DEBUG 1 --- [nio-8080-exec-6] io.lettuce.core.RedisChannelHandler      : dispatching command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:23:59:848 DEBUG 1 --- [nio-8080-exec-6] i.l.c.m.MasterReplicaConnectionProvider  : getConnectionAsync(WRITE)
01-06 07:23:59:848 DEBUG 1 --- [nio-8080-exec-6] io.lettuce.core.RedisChannelHandler      : dispatching command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:23:59:848 DEBUG 1 --- [nio-8080-exec-6] i.lettuce.core.protocol.DefaultEndpoint  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf] write() writeAndFlush command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:23:59:848 DEBUG 1 --- [nio-8080-exec-6] i.lettuce.core.protocol.DefaultEndpoint  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf] write() done
01-06 07:23:59:848 DEBUG 1 --- [oEventLoop-4-10] io.lettuce.core.protocol.CommandHandler  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf, chid=0x16] write(ctx, AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise)
01-06 07:23:59:849 DEBUG 1 --- [oEventLoop-4-10] io.lettuce.core.protocol.CommandEncoder  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379] writing command AsyncCommand [type=SET, output=StatusOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:23:59:851 DEBUG 1 --- [oEventLoop-4-10] io.lettuce.core.protocol.CommandHandler  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf, chid=0x16] Received: 5 bytes, 1 commands in the stack
01-06 07:23:59:851 DEBUG 1 --- [oEventLoop-4-10] io.lettuce.core.protocol.CommandHandler  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf, chid=0x16] Stack contains: 1 commands
01-06 07:23:59:851 DEBUG 1 --- [oEventLoop-4-10] i.l.core.protocol.RedisStateMachine      : Decode done, empty stack: true
01-06 07:23:59:852 DEBUG 1 --- [oEventLoop-4-10] io.lettuce.core.protocol.CommandHandler  : [channel=0x9b4ebc85, /172.30.1.5:46700 -> /172.30.1.2:6379, epid=0xf, chid=0x16] Completing command AsyncCommand [type=SET, output=StatusOutput [output=OK, error='null'], commandType=io.lettuce.core.protocol.Command]

2. 读

浏览器访问localhost:8080/get/num
在这里插入图片描述
查看SpringBoot容器日志,会向两个从节点之一发送读请求。

01-06 07:25:45:342 DEBUG 1 --- [io-8080-exec-10] io.lettuce.core.RedisChannelHandler      : dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:25:45:342 DEBUG 1 --- [io-8080-exec-10] i.l.c.m.MasterReplicaConnectionProvider  : getConnectionAsync(READ)
01-06 07:25:45:342 DEBUG 1 --- [io-8080-exec-10] io.lettuce.core.RedisChannelHandler      : dispatching command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:25:45:342 DEBUG 1 --- [io-8080-exec-10] i.lettuce.core.protocol.DefaultEndpoint  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c] write() writeAndFlush command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:25:45:342 DEBUG 1 --- [io-8080-exec-10] i.lettuce.core.protocol.DefaultEndpoint  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c] write() done
01-06 07:25:45:342 DEBUG 1 --- [oEventLoop-4-11] io.lettuce.core.protocol.CommandHandler  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c, chid=0x23] write(ctx, AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command], promise)
01-06 07:25:45:343 DEBUG 1 --- [oEventLoop-4-11] io.lettuce.core.protocol.CommandEncoder  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379] writing command AsyncCommand [type=GET, output=ValueOutput [output=null, error='null'], commandType=io.lettuce.core.protocol.Command]
01-06 07:25:45:346 DEBUG 1 --- [oEventLoop-4-11] io.lettuce.core.protocol.CommandHandler  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c, chid=0x23] Received: 10 bytes, 1 commands in the stack
01-06 07:25:45:346 DEBUG 1 --- [oEventLoop-4-11] io.lettuce.core.protocol.CommandHandler  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c, chid=0x23] Stack contains: 1 commands
01-06 07:25:45:346 DEBUG 1 --- [oEventLoop-4-11] i.l.core.protocol.RedisStateMachine      : Decode done, empty stack: true
01-06 07:25:45:346 DEBUG 1 --- [oEventLoop-4-11] io.lettuce.core.protocol.CommandHandler  : [channel=0x96ae68cf, /172.30.1.5:38102 -> /172.30.1.4:6379, epid=0x1c, chid=0x23] Completing command AsyncCommand [type=GET, output=ValueOutput [output=[B@7427ef47, error='null'], commandType=io.lettuce.core.protocol.Command]

3. 额外测试

以及还有一些额外的测试,可以自行去尝试,检验,这里列举一些,但具体不再赘述。

  1. 关闭两个从节点容器,等待sentinel完成维护和通知后,测试读数据和写数据会请求谁?
  2. 再次开启两个从节点,等待sentinel完成操作后,再关闭主节点,等待sentinel完成操作后,测试读数据和写数据会请求谁?
  3. 再次开启主节点,等待sentinel完成操作后,测试读数据和写数据会请求谁?

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/327314.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

竞赛练一练 第24期:NOC大赛每日一练,scratch题目刷题第3天,包含答案解析

023年NOC大赛创客智慧编程赛项图形化复赛模拟题一,包含答案解析 本次题目来源:NOC 大赛创客智慧编程赛项图形化复赛模拟题(一) 第一题: 制作一个生日贺卡小程序. 1.点击绿旗后蛋糕出现在 (0,-80) 的位置,大小为 100,造型为 cake-b2.当碰到鼠标指针时,将造型切换为 cak…

odoo16 连接postgresql错误

odoo16 连接postgresql错误 odoo16 用odoo15的环境出错&#xff0c;看到是psycopg2.OperationalError分析是postgresql版本问题&#xff0c;安装了13版本&#xff0c;还是出错&#xff0c;多版本共存问题如下&#xff1a; Traceback (most recent call last):File "D:\o…

MyBatis-Plus框架学习笔记

先赞后看&#xff0c;养成习惯&#xff01;&#xff01;&#xff01;❤️ ❤️ ❤️ 文章码字不易&#xff0c;如果喜欢可以关注我哦&#xff01; ​如果本篇内容对你有所启发&#xff0c;欢迎访问我的个人博客了解更多内容&#xff1a;链接地址 MyBatisPlus &#xff08;简称…

时序分解 | Matlab实现CPO-VMD基于冠豪猪优化算法(CPO)优化VMD变分模态分解时间序列信号分解

时序分解 | Matlab实现CPO-VMD基于冠豪猪优化算法(CPO)优化VMD变分模态分解时间序列信号分解 目录 时序分解 | Matlab实现CPO-VMD基于冠豪猪优化算法(CPO)优化VMD变分模态分解时间序列信号分解效果一览基本介绍程序设计参考资料 效果一览 基本介绍 【原创】CPO-VMD【24年新算法…

晶振老化和晶振引脚氧化的原因与影响

相信大部分的客户都会遇到晶振老化和晶振引脚氧化&#xff0c;而很多新手也难民啊会混淆晶振老化和晶振引脚样话这两个概念&#xff0c;也不理解。那么接下来&#xff0c;晶发给大家详细讲解&#xff0c;这两种情况怎么发生以及如何避免此类情况发生&#xff0c;保护我们的晶振…

【Java集合篇】为什么HashMap的Cap是2^n,如何保证?

为什么HashMap的Cap是2^n&#xff0c;如何保证&#xff1f; ✔️目录✔️ 为什么是2 ^ n ?✔️为什么 X %2^n X & (2^n - 1) ? ✔️如何保证✔️初始化时期保证✔️扩容时期保证 ✔️目录 ✔️ 为什么是2 ^ n ? HashMap是通过 (table.length - 1) & (key.hashCode …

【AIGC-图片生成视频系列-7】MoonShot:实现多模态条件下的可控视频生成和编辑

目录 一. 贡献概述 二. 方法详解​编辑 三. Zero-Shot主题定制视频生成 四. 文本到视频生成 五. 直接使用图像ControlNet 六. 图像动画比较 七. 视频编辑 八. 针对视频生成中多模态 Cross-Attn的消融实验 九. 对视频生成中多模态 Cross-Attn的消融实验 十. 论文 十一…

java数据结构与算法刷题-----LeetCode62. 不同路径

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 很多人觉得动态规划很难&#xff0c;但它就是固定套路而已。其实动态规划只…

强化学习1——多臂老虎机(上)

在强化学习中&#xff0c;关注智能体在与环境的交互中学习&#xff0c;成为试错型学习。多臂老虎机不存在状态信息&#xff0c;只有动作和奖励&#xff0c;是最简单的“和环境交互中学习“。 什么是多臂老虎机 老虎机_百度百科 (baidu.com) 多臂老虎机即有多个摇杆的老虎机&a…

[python]gym安装报错ERROR: Failed building wheel for box2d-py

报错截图&#xff1a; box2d是一个游戏领域的2D图形C引擎&#xff0c;用来模拟2D刚体物体运动和碰撞。 swig是一个将c/c代码封装为Python库的工具&#xff08;是Python调用c/c库的一种常见手段&#xff09;&#xff0c;所以在运行时box2d会依赖到swig。而swig并不是一个python库…

vivado xsim 终端 模拟

只模拟的话直接终端运行会快很多 计数器举例 mkdir srccounter.v module counter(input wire clk,input wire rst_n,output reg[31:0] cnt ); always (posedge clk or negedge rst_n)if(!rst_n)cnt < 31h0;elsecnt < cnt1;endmodule tb.v module tb; wire[31:0] out…

Guarded Suspension模式--适合等待事件处理

Guarded是被守护、被保卫、被保护的意思&#xff0c; Suspension则是暂停的意思。 如果执行现在的处理会造成问题&#xff0c; 就让执行处理的线程进行等待--- 这就是Guarded Suspension模式。 模式通过让线程等待来保证实例的安全性。 一个线程ClientThread会将请求 Request的…