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"));  // OK
        System.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.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,注意上述配置文件里的对应数字都要修改

云服务器的配置项<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: 8080

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

访问代码:

@RestController
public class IndexController {

    private static final Logger logger = LoggerFactory.getLogger(IndexController.class);

    @Autowired
    private 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",...)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

超详细的Python+requests+unittest+excel接口自动化测试框架教程

一、框架结构 工程目录 在这我也准备了一份软件测试视频教程&#xff08;含接口、自动化、性能等&#xff09;&#xff0c;需要的可以直接在下方观看&#xff0c;或者直接关注VX公众号&#xff1a;互联网杂货铺&#xff0c;免费领取 软件测试视频教程观看处&#xff1a; 软件测…

ArcGis如何用点连线?

这里指的是根据已有坐标点手动连线&#xff0c;类似于mapgis中的“用点连线”&#xff0c;线的每个拐点是可以自动捕捉到坐标点的&#xff0c;比直接画精确。 我也相信这么强大的软件一定可以实现类似于比我的软件上坐标时自动生成的线&#xff0c;但是目前我还没接触到那里&a…

lv11 嵌入式开发 GPIO实验 9

目录 1 简介 1.1 GPIO 2 LED实验步骤 2.1 通过电路原理图分析LED的控制逻辑 2.2 通过电路原理图查找LED与Exynos4412的连接关系 2.3 通过数据手册分析GPIO中哪些寄存器可以控制LED 2.4 通过程序去操控对应的寄存器完成对LED的控制 2.4.1 使用寄存器写入…

SpringBoot:邮件发送

官网文档&#xff1a;39. Sending Email (spring.io)。 Sending Email Spring框架提供了JavaMailSender实例&#xff0c;用于发送邮件。 如果SpringBoot项目中包含了相关的启动器&#xff0c;那么就会自动装配一个Bean实例到项目中。 在SpringBoot项目中引入如下Email启动器&a…

十大排序之计数排序、桶排序、基数排序(详解)

文章目录 &#x1f412;个人主页&#x1f3c5;算法思维框架&#x1f4d6;前言&#xff1a; &#x1f380;计数排序 时间复杂度O(nk)&#x1f387;1. 算法步骤思想&#x1f387;2.动画实现&#x1f387; 3.代码实现 &#x1f380;桶排序&#x1f387;1. 算法步骤思想&#x1f38…

activiti流程变量操作api

文章目录 runtimeServicetaskServicedelegateTask测试绘制流程图启动流程runtimeService&taskService查询变量runtimeService&taskService设置变量 runtimeService // ## runtimeService操作的都是executionId runtimeService.startProcessInstanceByKey(processDefin…

ACL权限

ACL权限 目录&#xff1a; 1. 什么是ACL 2. 操作步骤 1. 什么是ACL ACL是Access Control List的缩写&#xff0c;即访问控制列表 每个项目成员在有一个自己的项目目录&#xff0c;对自己的目录有完全权限 项目组中的成员对项目目录也有完全权限 其他人对项目目录没有…

互联网时代的身份标识有哪些?

在互联网时代&#xff0c;我们的在线活动几乎都与IP地址相关。无论是浏览网页、观看视频&#xff0c;还是进行在线交易和沟通交流&#xff0c;我们的设备都会分配到一个独特的IP地址。然而&#xff0c;你可能并未意识到的是&#xff0c;IP地址不仅标识了我们在网络中的身份&…

MySQL 索引相关问题,建议搭建好环境,真实操作一下索引应用到的各种场景

文章目录 什么是 B-tree 和 Btree &#xff1f;B-Tree 和 BTree的区别&#xff1f;MySQL 联合唯一索引是BTree&#xff0c;会带来什么原则&#xff1f;主键索引和单字段唯一索引有什么区别吗什么是 聚簇索引和非聚簇索引 &#xff1f;创建一个三百万数据量的表格&#xff0c;方…

HCIP-七、IS-IS 综合实验

七、IS-IS 综合实验 实验拓扑实验需求及解法1.如图所示&#xff0c;配置所有路由器的接口IP地址。2.运行IS-IS&#xff0c;进程号13.IS-IS优化4.路径优化 实验拓扑 实验需求及解法 本实验模拟IS-IS综合网络&#xff0c;完成以下需求&#xff1a; 1.如图所示&#xff0c;配置所…

Acrel-2000电力监控系统在上海大世界保护修缮工程项目中的应用

摘要&#xff1a;安科瑞生产厂家1876150/-6237黄安南 介绍上海大世界电力监控系统&#xff0c;采用智能电力仪表采集配电现场的各种电参量和开关信号。系统采用现场就地组网的方式&#xff0c;组网后通过现场总线通讯并远传至后台&#xff0c;通过Acrel-2000型电力监控系统实现…

Matplotlib图形配置与样式表_Python数据分析与可视化

Matplotlib图形配置与样式表 配置图形修改默认配置rcParams样式表 Matplotlib的默认图形设置经常被用户诟病。虽然2.0版本已经有了很大改善&#xff0c;但是掌握自定义配置的方法可以让我们打造自己的艺术风格。 配置图形 我们可以通过修个单个图形配置&#xff0c;使得最终图…

搜索引擎语法

演示自定的Google hacking语法&#xff0c;解释含意以及在渗透过程中的作用 Google hacking site&#xff1a;限制搜索范围为某一网站&#xff0c;例如&#xff1a;site:baidu.com &#xff0c;可以搜索baidu.com 的一些子域名。 inurl&#xff1a;限制关键字出现在网址的某…

【数据分享】我国12.5米分辨率的坡度数据(免费获取)

地形数据&#xff0c;也叫DEM数据&#xff0c;是我们在各项研究中最常使用的数据之一。之前我们分享过源于NASA地球科学数据网站发布的12.5米分辨率DEM地形数据&#xff08;可查看之前的文章获悉详情&#xff09;&#xff0c;这个DEM数据的优点是精度高。 基于DEM地形数据&…

【OpenGauss源码学习 —— 执行算子(Merge Join 算子)】

执行算子&#xff08;Merge Join 算子&#xff09; 连接算子Merge Join 算子ExecInitMergeJoin 函数MergeJoin 结构体 ExecMergeJoin 函数MergeJoinState 结构体 ExecEndMergeJoin 函数 总结 声明&#xff1a;本文的部分内容参考了他人的文章。在编写过程中&#xff0c;我们尊重…

阿里云windwos 安装oracle数据库,外部用工具连接不上,只能在服务器本机通过127.0.0.1 连接

1. 首先检查阿里云服务器安全组端口是否开放 oracle 数据库端口 2. 其次找到oracle 安装的目录&#xff0c;打开这俩个文件&#xff0c;将localhost 修改为 服务器本机名称 3.重启oracle 监听服务&#xff0c;就可以连接了

新手如何买卖基金,基金投资基础入门

一、教程描述 本套基金教程&#xff0c;大小2.50G&#xff0c;共有13个文件。 二、教程目录 第01课&#xff1a;基金入门&#xff0c;学会投资其实不难.mp4 第02课&#xff1a;基金分类&#xff0c;琳琅满目清清楚楚.mp4 第03课&#xff1a;以稳取胜&#xff0c;稳健基金稳…

程序的编译与链接(详解)

程序的编译与链接 本章内容如下&#xff1a; 1:程序的翻译环境与执行环境的介绍 2:详解程序的翻译环境(编译链接) 2.1预处理阶段干了啥2.2编译阶段干了啥2.3汇编阶段干了啥2.4链接阶段干了啥 3:预处理详解 预定义符号的介绍#define 的介绍(宏与标识符号)#与##的介绍宏与函数…

【MinIO】几个有用的方法

在windows总安装Minio 这是一篇不错的安装指南 进入网址 在Windows安装时&#xff0c;选择相应的exe文件下载&#xff0c;下载到本地后&#xff0c;使用如下的命令即可在前台启动&#xff1a; minio.exe server D:\your_path 或者将该路径写进环境变量的path中&#xff0c;…

怎么当代课老师教学生

老师朋友们&#xff0c;有没有帮忙当过代课老师呢&#xff1f;或者&#xff0c;没当过的老师是不是对这种职业充满了好奇&#xff1f;让我来分享一下&#xff0c;当代课老师的日常是什么样的吧&#xff01; 备课 说起备课&#xff0c;那可是个大工程&#xff01;不过&#xff…