首页 > 数据库 >【Redis】主从配置和读写分离实现

【Redis】主从配置和读写分离实现

时间:2024-07-14 22:56:42浏览次数:9  
标签:读写 Redis 192.168 prop repl master new import 主从

主从配置

Master配置修改

IP:192.168.0.100,端口:6378

bind 0.0.0.0
port 6378
requirepass 123456
# 关闭持久化
appendonly nosave ""
# 允许远程连接
protected-mode no

SlaveA配置修改

IP:192.168.0.100,端口:6377

bind 0.0.0.0
port 6377
requirepass 123456
# 主密码
masterauth 123456
# 关闭持久化
appendonly nosave ""
# 允许远程连接
protected-mode no
# 主服务器的地址
replicaof 192.168.0.100 6378
# 从机只读模式默认是开启的
replica-read-only yes

SlaveB配置修改

IP:192.168.0.100,端口:6376

bind 0.0.0.0
port 6376
requirepass 123456
# 主密码
masterauth 123456
# 关闭持久化
appendonly nosave ""
# 允许远程连接
protected-mode no
# 主服务器的地址
replicaof 192.168.0.100 6378
# 从机只读模式默认是开启的
replica-read-only yes

查看主redis服务

Master-192.168.1.100:0>info replication
"# Replication
role:master
connected_slaves:2
slave0:ip=192.168.0.100,port=6377,state=online,offset=1063,lag=0
slave1:ip=192.168.0.100,port=6376,state=online,offset=1063,lag=1
master_replid:5f7d94600462d4861fafef71fd824cdeca59dc25
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1063
master_repl_meaningful_offset:377
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1063"

查看从redis服务

SlaveA-192.168.1.100:0>info replication
"# Replication
role:slave
master_host:192.168.0.100
master_port:6378
master_link_status:up
master_last_io_seconds_ago:6
master_sync_in_progress:0
slave_repl_offset:1077
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:5f7d94600462d4861fafef71fd824cdeca59dc25
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1077
master_repl_meaningful_offset:377
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1077
"

读写分离实现

通过自定义LettuceConnectionFactory实现

配置YAML

spring:
  redis:
    database: 5
    password: 123456
    host: 192.168.0.100
    port: 6378
    # 主从复制读写分类配置
    replicaNodeList:
      - host: 192.168.0.100
        port: 6377
      - host: 192.168.0.100
        port: 6376
    timeout: 60000ms
    lettuce:
      pool:
        max-active: 100
        max-idle: 20
        max-wait: 3000ms
@Bean
@ConditionalOnList(name = "spring.redis.replicaNodeList", prop = {"host", "port"})
public LettuceConnectionFactory redisConnectionFactory(RedisConfigurationInformation redisConfigurationInformation) {
    RedisStaticMasterReplicaConfiguration redisConfig = new RedisStaticMasterReplicaConfiguration(redisConfigurationInformation.getHost(), redisConfigurationInformation.getPort());
    redisConfig.setDatabase(redisConfigurationInformation.getDatabase());
    redisConfig.setPassword(redisConfigurationInformation.getPassword());
    for (ReplicaNode node : redisConfigurationInformation.getReplicaNodeList()) {
        redisConfig.addNode(node.getHost(), node.getPort());
    }
    ReadFromCustomized readFrom = new ReadFromCustomized();
    LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder().readFrom(readFrom).build();
    return new LettuceConnectionFactory(redisConfig, clientConfig);
}
import cn.hutool.core.collection.CollectionUtil;
import io.lettuce.core.ReadFrom;
import io.lettuce.core.RedisURI;
import io.lettuce.core.internal.LettuceLists;
import io.lettuce.core.models.role.RedisInstance;
import io.lettuce.core.models.role.RedisNodeDescription;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

/**
 * redis 读信息
 *
 * @author ycx
 */
public class ReadFromCustomized extends ReadFrom {
    @Override
    public List<RedisNodeDescription> select(Nodes nodes) {
        List<RedisNodeDescription> masterList = new ArrayList<>();
        List<RedisNodeDescription> slaveList = new ArrayList<>();
        for (RedisNodeDescription node : nodes) {
            if (node.getRole() == RedisInstance.Role.MASTER) {
                masterList.add(node);
            } else if (node.getRole() == RedisInstance.Role.SLAVE) {
                slaveList.add(node);
            }
        }
        if (CollectionUtil.isNotEmpty(slaveList)) {
            Random random = new Random();
            int index = random.nextInt(slaveList.size());
            return LettuceLists.newList(slaveList.get(index));
        }
        if (CollectionUtil.isNotEmpty(masterList)) {
            return LettuceLists.newList(masterList.get(0));
        }
        return Collections.emptyList();
    }
}
import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

/**
 * 列表条件注解
 *
 * @author ycx
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
@Conditional(OnListCondition.class)
public @interface ConditionalOnList {
    String name() default "";
    String[] prop() default {};
}
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionOutcome;
import org.springframework.boot.autoconfigure.condition.SpringBootCondition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;

import java.util.*;

/**
 * 列表条件实现
 *
 * @author ycx
 */
@Slf4j
@Order(Ordered.HIGHEST_PRECEDENCE + 40)
public class OnListCondition extends SpringBootCondition {
    @Override
    public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
        List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(metadata.getAllAnnotationAttributes(ConditionalOnList.class.getName()));
        String name = null;
        String[] prop = null;
        for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
            name = annotationAttributes.getString("name");
            prop = (String[]) annotationAttributes.get("prop");
        }
        if (Objects.isNull(name) || "".equals(name.trim())) {
            throw new IllegalStateException("The name attribute of @ConditionalOnList must be specified");
        }
        if (Objects.isNull(prop) || prop.length == 0) {
            throw new IllegalStateException("The prop attribute of @ConditionalOnList must be specified");
        }
        List<String> keys = new ArrayList<>(prop.length);
        for (String item : prop) {
            if (Objects.isNull(item) || "".equals(item.trim())) {
                throw new IllegalStateException("The prop attribute of @ConditionalOnList must be nonempty element");
            }
            keys.add(name + "[0]." + item);
        }
        Environment environment = context.getEnvironment();
        boolean hasValue = false;
        for (String key : keys) {
            String value = environment.getProperty(key);
            if (Objects.nonNull(value)) {
                hasValue = true;
                break;
            }
        }
        if (hasValue) {
            for (String key : keys) {
                String value = environment.getProperty(key);
                if (StrUtil.isEmpty(value)) {
                    throw new IllegalStateException("The value of prop(" + key + ") attribute of @ConditionalOnList must be specified");
                }
            }
            log.info("【Redis】启动主从复制读写分离");
            System.out.println("【Redis】启动主从复制读写分离");
            return new ConditionOutcome(true, "启动主从复制读写分离 ");
        }
        log.info("【Redis】启动单节点");
        System.out.println("【Redis】启动单节点");
        return new ConditionOutcome(false, "启动单节点");
    }

    private List<AnnotationAttributes> annotationAttributesFromMultiValueMap(
            MultiValueMap<String, Object> multiValueMap) {
        List<Map<String, Object>> maps = new ArrayList<>();
        multiValueMap.forEach((key, value) -> {
            for (int i = 0; i < value.size(); i++) {
                Map<String, Object> map;
                if (i < maps.size()) {
                    map = maps.get(i);
                }
                else {
                    map = new HashMap<>();
                    maps.add(map);
                }
                map.put(key, value.get(i));
            }
        });
        List<AnnotationAttributes> annotationAttributes = new ArrayList<>(maps.size());
        for (Map<String, Object> map : maps) {
            annotationAttributes.add(AnnotationAttributes.fromMap(map));
        }
        return annotationAttributes;
    }
}

 

标签:读写,Redis,192.168,prop,repl,master,new,import,主从
From: https://www.cnblogs.com/grimm/p/18302166

相关文章

  • SSRF结合Redis未授权的打法
    目录SSRF+Redis未授权案例怎么构造redis数据包?ReferenceSSRF不难理解,服务器端请求伪造(英语:Server-sideRequestForgery,简称SSRF)是攻击者滥用服务器功能来访问或操作无法被直接访问的信息的方式之一。服务器端请求伪造攻击将域中的不安全服务器作为代理使用,这与利用网页......
  • Factory method 'redissonClient' threw exception; nested exception is java.lang.I
    你遇到的这个错误是在Spring框架中常见的,它表示在创建Bean的过程中,有一个依赖关系未能得到满足。在这个特定的情况下,错误发生在创建voucherOrderController和voucherOrderServiceImpl这两个Bean时,其根本原因是无法实例化redissonClient,而redissonClient的创建失败是因为提供的Redi......
  • Redis集群模式
    一、Redis集群方式主从模式、哨兵模式、集群模式(cluster)主从复制:解决数据备份,读写分离。但是无法实现自动化故障转移,无法对master进行扩容。哨兵模式:实现自动化故障恢复。在读写分离下,单节点导致服务不可用。集群模式:解决负载均衡以及存储问题。模式版本优点缺点......
  • 记录些Redis题集(2)
    Redis的多路IO复用多路I/O复用是一种同时监听多个文件描述符(如Socket)的状态变化,并能在某个文件描述符就绪时执行相应操作的技术。在Redis中,多路I/O复用技术主要用于处理客户端的连接请求和读写操作,以实现高并发、高性能的服务。Redis支持多种多路I/O复用机制,包括select、poll......
  • 记录些Redis题集(4)
    Redis通讯协议(RESP)Redis通讯协议(RedisSerializationProtocol,RESP)是Redis服务端与客户端之间进行通信的协议。它是一种二进制安全的文本协议,设计简洁且易于实现。RESP主要用于支持客户端和服务器之间的请求响应交互。RESP的主要特点:简单性:协议的设计非常简单,易于理......
  • 记录些Redis题集(1)
    Redis内存淘汰触发条件的相关配置如下:Redis通过配置项maxmemory来设定其允许使用的最大内存容量。当Redis实际占用的内存达到这一阈值时,将触发内存淘汰机制,开始删除部分数据以释放内存空间,防止服务因内存溢出而异常。Redis内存淘汰策略可在配置文件redis.conf中通过maxmemory......
  • Redis:高性能的开源缓存数据库
    简介:Redis(RemoteDictionaryServer)是一个基于内存的开源缓存数据库,常用于缓存、消息队列、分布式锁等场景。它被设计成快速、可靠且易于使用的数据库系统,具有高性能、高可用、可扩展性等特点。本篇博客将介绍Redis的基本原理、常见应用场景以及优势。Redis的基本原理Redis......
  • Redis存储原理与数据模型
    Redis存储结构存储转换redis-value编码stringint:字符串长度小于等于20切能转成整数raw:字符串长度大于44embstr:字符串长度小于等于44listquicklist(双向链表)ziplist(压缩链表)hashdict(字典):节点数量大于512或者字符串长度大于64ziplist(压缩链表):节点数......
  • [Redis]字符串详解
    Redis中的字符串是可以修改的字符串,在内存中它是以字节数组的形式存在的。我们知道C语言里面的字符串标准形式是以NULL(即0x\0)作为结束符,但是在Redis里面,字符串不是这么表示的。因为要获取以NULL结尾的字符串的长度使用的是strlen标准库函数,这个函数的算法复杂度是0(n......
  • 【java登录锁定功能】redis实现登录失败锁定账号
    登录失败(账号密码<5次时不提示),>=5次时,锁定时间5min,最高密码错误次数为10,第十次密码输入错误后,提醒,“账号已停用,请联系管理员开通”,次日0时,重新计算错误次数代码实现publicstaticStringLOGIN_FAIL_LOCK="login:error:count:";publicstaticStringLOGIN......