Redis-主从与哨兵架构

Jedis使用

Jedis连接代码示例:

1、引入依赖

<dependency><groupId>redis.clients</groupId><artifactId>jedis</artifactId><version>2.9.0</version>
</dependency>

2、访问代码

public class JedisSingleTest
{public static void main(String[] args){JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();jedisPoolConfig.setMaxTotal(20);jedisPoolConfig.setMaxIdle(10);jedisPoolConfig.setMinIdle(5);// timeout,这里既是连接超时又是读写超时,从Jedis 2.8开始有区分connectionTimeout和soTimeout的构造函数JedisPool jedisPool = new JedisPool(jedisPoolConfig, "192.168.0.60", 6379, 3000, null);Jedis jedis = jedisPool.getResource();System.out.println(jedis.set("single", "gc"));  // OKSystem.out.println(jedis.get("single"));		// gc}
}

注意:如果是购买的云服务器,需要在安全组添加端口规则

管道(Pipeline)

客户端可以一次性发送多个请求而不用等待服务器的响应,待所有命令都发送完后再一次性读取服务的响应,这样可以极大的降低多条命令执行的网络传输开销,管道执行多条命令的网络开销实际上只相当于一次命令执行的网络开销。需要注意到是用pipeline方式打包命令发送,redis必须在处理完所有命令前先缓存起所有命令的处理结果。打包的命令越多,缓存消耗内存也越多。所以并不是打包的命令越多越好。

pipeline中发送的每个command都会被server立即执行,如果执行失败,将会在此后的响应中得到信息;也就是pipeline并不是表达“所有command都一起成功”的语义,管道中前面命令失败,后面命令不会有影响,继续执行

Pipeline pl = jedis.pipelined();
for (int i = 0; i < 10; i++)
{pl.incr("pipelineKey");pl.set("gc" + i, "gc");// 模拟管道报错// pl.setbit("gc", -1, true);
}
List<Object> results = pl.syncAndReturnAll();
System.out.println(results);输出:[1, OK, 2, OK, 3, OK, 4, OK, 5, OK, 6, OK, 7, OK, 8, OK, 9, OK, 10, OK]
报错输出:[11, OK, redis.clients.jedis.exceptions.JedisDataException: ERR bit offset is not an integer or out of range, 12, OK, 省略...]

Redis Lua脚本

Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行。使用脚本的好处:

1、减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成。使用脚本,减少了网络往返时延。这点跟管道类似

2、原子操作:Redis会将整个脚本作为一个整体执行,中间不会被其他命令插入。管道不是原子的,不过redis的批量操作命令(类似mset)是原子的。

3、替代redis的事务功能:redis自带的事务功能很鸡肋,而redis的lua脚本几乎实现了常规的事务功能,官方推荐如果要使用redis的事务功能可以用redis lua替代。

官网文档描述:

A Redis script is transactional by definition, so everything you can do with a Redis transaction, you can also do with a script, and usually the script will be both simpler and faster.

从Redis2.6.0版本开始,通过内置的Lua解释器,可以使用EVAL命令对Lua脚本进行求值。语法:

EVAL script numkeys key [key ...] arg [arg ...]

script参数是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数

numkeys参数用于指定键名参数的个数。

键名参数 key [key ...],可以在Lua中通过全局变量KEYS数组访问:KEYS[1],KEYS[2],以此类推。

附加参数 arg [arg ...],可以在Lua中通过全局变量ARGV数组访问,ARGV[1] 、 ARGV[2] ,以此类推

127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
1) "key1"
2) "key2"
3) "first"
4) "second"

在 Lua 脚本中,可以使用redis.call()函数来执行Redis命令

// lua脚本模拟一个商品减库存的原子操作
// lua脚本命令执行方式:redis-cli --eval /tmp/test.lua , 10
jedis.set("product_count_10016", "10");  //初始化商品10016的库存
String script = " local count = redis.call('get', KEYS[1]) " +" local a = tonumber(count) " +" local b = tonumber(ARGV[1]) " +" if a >= b then " +"   redis.call('set', KEYS[1], a-b) " +"   return 1 " +" end " +" return 0 ";
Object obj = jedis.eval(script, Arrays.asList("product_count_10016"), Arrays.asList("12");
System.out.println(obj); 

注意,不要在Lua脚本中出现死循环和耗时的运算,否则redis会阻塞,将不接受其他的命令, 所以使用时要注意不能出现死循环、耗时的运算。redis是单进程、单线程执行脚本。管道不会阻塞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.conf2、将相关配置修改为如下值:
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.conf4、查看sentinel的info信息
src/redis-cli -p 26379
127.0.0.1:26379>info
可以看到Sentinel的info里已经识别出了redis的主从5、可以自己再配置两个sentinel,端口26380和26381,注意上述配置文件里的对应数字都要修改

云服务器的配置项<master-redis-ip>修改为公网IP后测试成功

sentinel集群都启动完毕后,会将哨兵集群的元数据信息写入所有sentinel的配置文件里去(追加在文件的最下面),我们查看下如下配置文件sentinel-26379.conf,如下所示:

sentinel known-replica mymaster 192.168.0.60 6381	#代表主节点的从节点信息
sentinel known-replica mymaster 192.168.0.60 6380	#代表主节点的从节点信息
sentinel known-sentinel mymaster 192.168.0.60 26381 569cb30eefa70d33927d8dfd79360bd11b58b9de #代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.0.60 26380 a38226cc8e7f3f71427eaa1bb2b50de07eadd21d #代表感知到的其它哨兵节点

如果哨兵节点之间互相感应不到,检查配置文件中,用于标识哨兵唯一性的myid是不是重复的

注意:如果哨兵节点已经启动了,然后再复制一份修改为其他的哨兵节点,这种情况如果没有删除已经生成的myid,就会导致哨兵节点之间不想感应不到,因为它们的myid是相同的

当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 26381 569cb30eefa70d33927d8dfd79360bd11b58b9de #代表感知到的其它哨兵节点
sentinel known-sentinel mymaster 192.168.0.60 26380 a38226cc8e7f3f71427eaa1bb2b50de07eadd21d #代表感知到的其它哨兵节点

同时还会修改sentinel文件里之前配置的mymaster对应的6379端口,改为6380

sentinel monitor mymaster 192.168.0.60 6380 2

当6379的redis实例再次启动时,哨兵集群根据集群元数据信息就可以将6379端口的redis节点作为从节点加入集群

测试代码:

public class JedisSingleTest2
{public static void main(String[] args){JedisPoolConfig config = new JedisPoolConfig();config.setMaxTotal(20);config.setMaxIdle(10);config.setMinIdle(5);String masterName = "mymaster";Set<String> sentinels = new HashSet<>();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", "gc"));System.out.println(jedis.get("sentinel"));}catch (Exception e){e.printStackTrace();}finally{// 注意这里不是关闭连接,在JedisPool模式下,Jedis会被归还给资源池。if (jedis != null)jedis.close();}}
}

哨兵的Spring Boot整合Redis连接

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>

2、springboot项目核心配置:

server:port: 8080spring:redis:database: 0timeout: 3000sentinel:    #哨兵模式master: mymaster #主服务器所在集群名称nodes: 192.168.0.60:26379,192.168.0.60:26380,192.168.0.60:26381lettuce:pool:max-idle: 50min-idle: 10max-active: 100max-wait: 1000

访问代码:

@RestController
public class IndexController {private static final Logger logger = LoggerFactory.getLogger(IndexController.class);@Autowiredprivate StringRedisTemplate stringRedisTemplate;/*** 测试节点挂了哨兵重新选举新的master节点,客户端是否能动态感知到* 新的master选举出来后,哨兵会把消息发布出去,客户端实际上是实现了一个消息监听机制,* 当哨兵把新master的消息发布出去,客户端会立马感知到新master的信息,从而动态切换访问的masterip** @throws InterruptedException*/@RequestMapping("/test_sentinel")public void testSentinel() throws InterruptedException {int i = 1;while (true){try {stringRedisTemplate.opsForValue().set("gc"+i, i+"");System.out.println("设置key:"+ "gc" + i);i++;Thread.sleep(1000);}catch (Exception e){logger.error("错误:", e);}}}
}

StringRedisTemplate与RedisTemplate详解

spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api。在RedisTemplate中提供了几个常用的接口方法的使用,分别是:

private ValueOperations<K, V> valueOps;
private HashOperations<K, V> hashOps;
private ListOperations<K, V> listOps;
private SetOperations<K, V> setOps;
private ZSetOperations<K, V> zSetOps;

RedisTemplate中定义了对5种数据结构操作:

redisTemplate.opsForValue();	//操作字符串
redisTemplate.opsForHash();		//操作hash
redisTemplate.opsForList();		//操作list
redisTemplate.opsForSet();		//操作set
redisTemplate.opsForZSet();		//操作有序set

StringRedisTemplate继承自RedisTemplate,也一样拥有上面这些操作。

StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存的。

RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存的。

Redis客户端命令对应的RedisTemplate中的方法列表:

String类型结构
RedisRedisTemplate rt
set key valuert.opsForValue().set("key","value")
get keyrt.opsForValue().get("key")
del keyrt.delete("key")
strlen keyrt.opsForValue().size("key")
getset key valuert.opsForValue().getAndSet("key","value")
getrange key start endrt.opsForValue().get("key",start,end)
append key valuert.opsForValue().append("key","value")
Hash结构
hmset key field1 value1 field2 value2...rt.opsForHash().putAll("key",map) //map是一个集合对象
hset key field valuert.opsForHash().put("key","field","value")
hexists key fieldrt.opsForHash().hasKey("key","field")
hgetall keyrt.opsForHash().entries("key") //返回Map对象
hvals keyrt.opsForHash().values("key") //返回List对象
hkeys keyrt.opsForHash().keys("key") //返回List对象
hmget key field1 field2...rt.opsForHash().multiGet("key",keyList)
hsetnx key field valuert.opsForHash().putIfAbsent("key","field","value"
hdel key field1 field2rt.opsForHash().delete("key","field1","field2")
hget key fieldrt.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 indexrt.opsForList().index("list", index)
llen keyrt.opsForList().size("key")
lpop keyrt.opsForList().leftPop("key")
rpop keyrt.opsForList().rightPop("key")
lpushx list nodert.opsForList().leftPushIfPresent("list","node")
rpushx list nodert.opsForList().rightPushIfPresent("list","node")
lrange list start endrt.opsForList().range("list",start,end)
lrem list count valuert.opsForList().remove("list",count,"value")
lset key index valuert.opsForList().set("list",index,"value")
Set结构
sadd key member1 member2...rt.boundSetOps("key").add("member1","member2",...)
rt.opsForSet().add("key", set) //set是一个集合对象
scard keyrt.opsForSet().size("key")
sidff key1 key2rt.opsForSet().difference("key1","key2") //返回一个集合对象
sinter key1 key2rt.opsForSet().intersect("key1","key2")//同上
sunion key1 key2rt.opsForSet().union("key1","key2")//同上
sdiffstore des key1 key2rt.opsForSet().differenceAndStore("key1","key2","des")
sinter des key1 key2rt.opsForSet().intersectAndStore("key1","key2","des")
sunionstore des key1 key2rt.opsForSet().unionAndStore("key1","key2","des")
sismember key memberrt.opsForSet().isMember("key","member")
smembers keyrt.opsForSet().members("key")
spop keyrt.opsForSet().pop("key")
srandmember key countrt.opsForSet().randomMember("key",count)
srem key member1 member2...rt.opsForSet().remove("key","member1","member2",...)

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

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

相关文章

App 设计工具

目录 说明 打开 App 设计工具 示例 创建 App 创建自定义 UI 组件 打开现有 App 文件 打包和共享 App 本文主要讲述以交互方式创建 App。 说明 App 设计工具是一个交互式开发环境&#xff0c;用于设计 App 布局并对其行为进行编程。 可以使用 App 设计工具&#xff1a…

【黑马甄选离线数仓day05_核销主题域开发】

1. 指标分类 ​ 通过沟通调研&#xff0c;把需求进行分析、抽象和总结&#xff0c;整理成指标列表。指标有原子指标、派生指标、 衍生指标三种类型。 ​ 原子指标基于某一业务过程的度量值&#xff0c;是业务定义中不可再拆解的指标&#xff0c;原子指标的核心功能就是对指标…

Python武器库开发-前端篇之CSS元素(三十二)

前端篇之CSS元素(三十二) CSS 元素是一个网页中的 HTML 元素&#xff0c;包括标签、类和 ID。它们可以通过 CSS 选择器选中并设置样式属性&#xff0c;以使网页呈现具有吸引力和良好的可读性。常见的 HTML 元素包括 div、p、h1、h2、span 等&#xff0c;它们可以使用 CSS 设置…

值得看的书--《全宋词》节选

(https://img-blog.csdnimg.cn/5d5fe2844f6646b5b7b415f0a9e80f6c.jpg)

什么是自动化测试po模式,po分层如何实现?

一、什么是PO模式 全称&#xff1a;page object model 简称&#xff1a;POM/PO PO模式最核心的思想是分层&#xff0c;实现松耦合&#xff01;实现脚本重复使用&#xff0c;实现脚本易维护性&#xff01; 主要分三层&#xff1a; 1.基础层BasePage&#xff1a;封装一些最基…

自监督LIGHTLY SSL教程

Lightly SSL 是一个用于自监督学习的计算机视觉框架。 github链接&#xff1a;GitHub - lightly-ai/lightly: A python library for self-supervised learning on images. Documentation&#xff1a;Documentation — lightly 1.4.20 documentation 以下内容主要来自Documen…

作为Java初学者,如何快速学好Java?

作为Java初学者&#xff0c;如何快速学好Java&#xff1f; 开始的一些话 对于初学者来说&#xff0c;编程的学习曲线可能相对陡峭。这是正常现象&#xff0c;不要感到沮丧。逐步学习&#xff0c;循序渐进。 编程是一门实践性的技能&#xff0c;多写代码是提高的唯一途径。尽量…

C++初阶(十二)string的模拟实现

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、string类的模拟实现1、构造、拷贝构造、赋值运算符重载以及析构函数2、迭代器类3、增删查…

【linux】基本指令(中篇)

echo指令 将引号内容打印到显示屏上 输出的重定向 追加的重定向 输出的重定向 我们学习c语言的时候当以写的方式创建一个文件&#xff0c;就会覆盖掉该文件之前的内容 当我们以追加的方式打开文件的时候&#xff0c;原文件内容不会被覆盖而是追加 more指令 10.more指令…

车载电子电器架构 ——电子电气架构设计方案概述

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 注:本文1万多字,认证码字,认真看!!! 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证…

基于Pytest+Requests+Allure实现接口自动化测试

一、整体结构 框架组成&#xff1a;pytestrequestsallure 设计模式&#xff1a; 关键字驱动 项目结构&#xff1a; 工具层&#xff1a;api_keyword/ 参数层&#xff1a;params/ 用例层&#xff1a;case/ 数据驱动&#xff1a;data_driver/ 数据层&#xff1a;data/ 逻…

基于51单片机的人体追踪可控的电风扇系统

**单片机设计介绍&#xff0c; 基于51单片机超声波测距汽车避障系统 文章目录 一 概要概述硬件组成工作原理优势应用场景总结 二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 # 基于51单片机的人体追踪可控的电风扇系统介绍 概述 该系统是基于51…

AI 视频 | Stable Video Diffusion 来了!(附体验地址)

1. 介绍 11 月 21 日&#xff0c;Stability AI 推出了 Stable Video Diffusion&#xff0c;这是 Stability AI 的第一个基于图像模型 Stable Diffusion 的生成式视频基础模型。 目前 Stability AI 已经在 GitHub 上开源了 Stable Video Diffusion 的代码&#xff0c;在 Huggin…

c语言刷题12周(1~5)

输入年月日&#xff0c;显示这一天是这一年的第几天&#xff0c;保证输入日期合法。 题干输入年月日&#xff0c;显示这一天是这一年的第几天&#xff0c;保证输入日期合法。输入样例2022 1 1 2022 12 31 2024 12 31 2022 4 5输出样例2022-1 2022-365 2024-366 2022-9…

【数据结构实验】图(二)将邻接矩阵存储转换为邻接表存储

文章目录 1. 引言2. 邻接表表示图的原理2.1 有向权图2.2 无向权图2.3 无向非权图2.1 有向非权图 3. 实验内容3.1 实验题目&#xff08;一&#xff09;数据结构要求&#xff08;二&#xff09;输入要求&#xff08;三&#xff09;输出要求 3.2 算法实现 4. 实验结果 1. 引言 图是…

node.js解决输出中文乱码问题

个人简介 &#x1f468;&#x1f3fb;‍&#x1f4bb;个人主页&#xff1a;九黎aj &#x1f3c3;&#x1f3fb;‍♂️幸福源自奋斗,平凡造就不凡 &#x1f31f;如果文章对你有用&#xff0c;麻烦关注点赞收藏走一波&#xff0c;感谢支持&#xff01; &#x1f331;欢迎订阅我的…

shell脚本循环语句

目录 一. 循环语句 1. 循环条件 2. 循环次数 3. 循环命令区别 4. for 循环 ①. 第一种语法 ②. 第二种语法 5. while 循环 6. until 循环 二. 跳出循环 1. break 结束循环 2. continue 结束循环 3. exit 结束循环 三. 补充 1. 偶数的表示 2. 奇数的表示 一. 循环…

【测试开发工程师】TestNG测试框架零基础入门(上)

哈喽大家好&#xff0c;我是小浪。那么今天是一期基于JavaTestNG测试框架的入门教学的博客&#xff0c;从只会手工测试提升到自动化测试&#xff0c;这将对你的测试技术提升是非常大的&#xff0c;有助于我们以后在找工作、面试的时候具备更大的竞争力~ 文章目录 一、什么是T…

【数据结构实验】图(一)Warshall算法(求解有向图的可达矩阵)

文章目录 1. 引言2. Warshall算法原理2.1 初始化可及矩阵2.2 迭代更新可及矩阵 3. 实验内容3.1 实验题目&#xff08;一&#xff09;输入要求&#xff08;二&#xff09;输出要求 3.2 算法实现 4. 实验结果 1. 引言 Warshall算法是一种用于求解有向图的可达矩阵的经典算法。该算…

用Python进行数据分析:探索性数据分析的实践与技巧(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…