redis读写分离之lettuce
问题
redis使用过程中,很多情况都是读多写少,而不管是主从、哨兵、集群,从节点都只是用来备份,为了最大化节约用户成本,我们需要利用从节点来进行读,分担主节点压力,这里我们继续上一章的jedis的读写分离,由于springboot现在redis集群默认用的是lettuce,所以介绍下lettuce读写分离
读写分离
主从读写分离
这里先建一个主从集群,1主3从,一般情况下只需要进行相关配置如下:
spring:redis:host: redisMastHostport: 6379lettuce:pool:max-active: 512max-idle: 256min-idle: 256max-wait: -1
这样就可以直接注入redisTemplate,读写数据了,但是这个默认只能读写主,如果需要设置readfrom,则需要自定义factory,下面给出两种方案
方案一(适用于非aws)
只需要配置主节点,从节点会信息会自动从主节点获取
@Configuration
class WriteToMasterReadFromReplicaConfiguration {@Beanpublic LettuceConnectionFactory redisConnectionFactory() {LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder().readFrom(ReadFrom.SLAVE_PREFERRED).build();RedisStandaloneConfiguration serverConfig = new RedisStandaloneConfiguration("server", 6379);return new LettuceConnectionFactory(serverConfig, clientConfig);}
}
方案二(云上redis,比如aws)
下面给个demo
import io.lettuce.core.ReadFrom;
import io.lettuce.core.models.role.RedisNodeDescription;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;@Configuration
public class RedisConfig {@Value("${spring.redis1.master}")private String master;@Value("${spring.redis1.slaves:}")private String slaves;@Value("${spring.redis1.port}")private int port;@Value("${spring.redis1.timeout:200}")private long timeout;@Value("${spring.redis1.lettuce.pool.max-idle:256}")private int maxIdle;@Value("${spring.redis1.lettuce.pool.min-idle:256}")private int minIdle;@Value("${spring.redis1.lettuce.pool.max-active:512}")private int maxActive;@Value("${spring.redis1.lettuce.pool.max-wait:-1}")private long maxWait;private static Logger logger = LoggerFactory.getLogger(RedisConfig.class);private final AtomicInteger index = new AtomicInteger(-1);@Bean(value = "lettuceConnectionFactory1")LettuceConnectionFactory lettuceConnectionFactory1(GenericObjectPoolConfig genericObjectPoolConfig) {RedisStaticMasterReplicaConfiguration configuration = new RedisStaticMasterReplicaConfiguration(this.master, this.port);if(StringUtils.isNotBlank(slaves)){String[] slaveHosts=slaves.split(",");for (int i=0;i<slaveHosts.length;i++){configuration.addNode(slaveHosts[i], this.port);}}LettuceClientConfiguration clientConfig =LettucePoolingClientConfiguration.builder().readFrom(ReadFrom.SLAVE).commandTimeout(Duration.ofMillis(timeout)).poolConfig(genericObjectPoolConfig).build();return new LettuceConnectionFactory(configuration, clientConfig);}/*** GenericObjectPoolConfig 连接池配置* @return*/@Beanpublic GenericObjectPoolConfig genericObjectPoolConfig() {GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();genericObjectPoolConfig.setMaxIdle(maxIdle);genericObjectPoolConfig.setMinIdle(minIdle);genericObjectPoolConfig.setMaxTotal(maxActive);genericObjectPoolConfig.setMaxWaitMillis(maxWait);return genericObjectPoolConfig;}@Bean(name = "redisTemplate1")public RedisTemplate redisTemplate(@Qualifier("lettuceConnectionFactory1") LettuceConnectionFactory connectionFactory) {RedisTemplate<String,String> template = new RedisTemplate<String,String>();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new StringRedisSerializer());logger.info("redis 连接成功");return template;}}
这里的核心代码在readfrom的设置,lettuce提供了5中选项,分别是
- MASTER
- MASTER_PREFERRED
- SLAVE_PREFERRED
- SLAVE
- NEAREST
最新的版本SLAVE改成了ReadFrom.REPLICA
这里设置为SlAVE,那么读请求都会走从节点,但是这里有个bug,每次都会读取最后一个从节点,其他从节点都不会有请求过去,跟踪源代码发现节点顺序是一定的,但是每次getConnection时每次都会获取最后一个,下面是缓存命令情况
解决方案就是自定义一个readFrom,如下
LettuceClientConfiguration clientConfig =LettucePoolingClientConfiguration.builder().readFrom(new ReadFrom() {@Overridepublic List<RedisNodeDescription> select(Nodes nodes) {List<RedisNodeDescription> allNodes = nodes.getNodes();int ind = Math.abs(index.incrementAndGet() % allNodes.size());RedisNodeDescription selected = allNodes.get(ind);logger.info("Selected random node {} with uri {}", ind, selected.getUri());List<RedisNodeDescription> remaining = IntStream.range(0, allNodes.size()).filter(i -> i != ind).mapToObj(allNodes::get).collect(Collectors.toList());return Stream.concat(Stream.of(selected),remaining.stream()).collect(Collectors.toList());}}).commandTimeout(Duration.ofMillis(timeout)).poolConfig(genericObjectPoolConfig).build();return new LettuceConnectionFactory(configuration, clientConfig);
手动实现顺序读各个从节点,修改后调用情况如下,由于还有其他应用连接该redis,所以监控图中非绝对均衡
哨兵模式
这个我就提供一个简单demo
@Configuration
@ComponentScan("com.redis")
public class RedisConfig {@Beanpublic LettuceConnectionFactory redisConnectionFactory() {
// return new LettuceConnectionFactory(new RedisStandaloneConfiguration("192.168.80.130", 6379));RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration().master("mymaster")// 哨兵地址.sentinel("192.168.80.130", 26379).sentinel("192.168.80.130", 26380).sentinel("192.168.80.130", 26381);LettuceClientConfiguration clientConfig = LettuceClientConfiguration.builder().readFrom(ReadFrom.SLAVE_PREFERRED).build();return new LettuceConnectionFactory(sentinelConfig, clientConfig);}@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory);// 可以配置对象的转换规则,比如使用json格式对object进行存储。// Object --> 序列化 --> 二进制流 --> redis-server存储redisTemplate.setKeySerializer(new StringRedisSerializer());redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());return redisTemplate;}}
集群模式
集群模式就比较简单了,直接套用下面demo
import io.lettuce.core.ReadFrom;
import io.lettuce.core.resource.ClientResources;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;import java.time.Duration;
import java.util.HashSet;
import java.util.Set;@Slf4j
@Configuration
public class Redis2Config {@Value("${spring.redis2.cluster.nodes: com:9736}")public String REDIS_HOST;@Value("${spring.redis2.cluster.port:9736}")public int REDIS_PORT;@Value("${spring.redis2.cluster.type:}")public String REDIS_TYPE;@Value("${spring.redis2.cluster.read-from:master}")public String READ_FROM;@Value("${spring.redis2.cluster.max-redirects:1}")public int REDIS_MAX_REDIRECTS;@Value("${spring.redis2.cluster.share-native-connection:true}")public boolean REDIS_SHARE_NATIVE_CONNECTION;@Value("${spring.redis2.cluster.validate-connection:false}")public boolean VALIDATE_CONNECTION;@Value("${spring.redis2.cluster.shutdown-timeout:100}")public long SHUTDOWN_TIMEOUT;@Bean(value = "myRedisConnectionFactory")public RedisConnectionFactory connectionFactory(ClientResources clientResources) {RedisClusterConfiguration clusterConfiguration = new RedisClusterConfiguration();if (StringUtils.isNotEmpty(REDIS_HOST)) {String[] serverArray = REDIS_HOST.split(",");Set<RedisNode> nodes = new HashSet<RedisNode>();for (String ipPort : serverArray) {String[] ipAndPort = ipPort.split(":");nodes.add(new RedisNode(ipAndPort[0].trim(), Integer.valueOf(ipAndPort[1])));}clusterConfiguration.setClusterNodes(nodes);}if (REDIS_MAX_REDIRECTS > 0) {clusterConfiguration.setMaxRedirects(REDIS_MAX_REDIRECTS);}LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder clientConfigurationBuilder = LettucePoolingClientConfiguration.builder().clientResources(clientResources).shutdownTimeout(Duration.ofMillis(SHUTDOWN_TIMEOUT));if (READ_FROM.equals("slave")) {clientConfigurationBuilder.readFrom(ReadFrom.SLAVE_PREFERRED);} else if (READ_FROM.equals("nearest")) {clientConfigurationBuilder.readFrom(ReadFrom.NEAREST);} else if (READ_FROM.equals("master")) {clientConfigurationBuilder.readFrom(ReadFrom.MASTER_PREFERRED);}LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(clusterConfiguration, clientConfigurationBuilder.build());lettuceConnectionFactory.afterPropertiesSet();return lettuceConnectionFactory;}@Bean(name = "myRedisTemplate")public RedisTemplate myRedisTemplate(@Qualifier("myRedisConnectionFactory") RedisConnectionFactory connectionFactory) {RedisTemplate template = new RedisTemplate();template.setConnectionFactory(connectionFactory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer());return template;}}
不过这里集群模式不推荐读取从节点,因为在生产中有可能导致某一分片挂掉以至于整个集群都不可用,可以考虑从节点整多个,然后配置读写分离。
redis读写分离之lettuce相关推荐
- 缓存成神路:Redis读写分离难以理解?一文解析Redis读写分离技术
背景 云数据库Redis版不管主从版还是集群规格,replica作为备库不对外提供服务,只有在发生HA的时候,replica提升为master后才承担读写流量.这种架构读写请求都在master上完成, ...
- Redis 读写分离技术架构解析
以下文章来源方志朋的博客,回复"666"获面试宝典 Redis 不管主从版还是集群规格,replica作为备库不对外提供服务,只有在发生HA的时候,replica提升为master ...
- Redis读写分离技术架构解析
作者:小热爱 来源:juejin.cn/post/6955355686108659726 背景 Redis 不管主从版还是集群规格,replica作为备库不对外提供服务,只有在发生HA的时候,repl ...
- 单机mysql能支撑起10w的qps_高并发redis - 读写分离支撑qps10w+
之前讲到单机redis的为了保证数据安全,必须做好数据备份等基础工作.但是如果流量越来越大, redis的读写请求压力越来越大,到了一个极限值,性能依旧不够用我们应该如何处理? 这边文章就分为以下几步 ...
- Redis读写分离技术解析
背景 云数据库Redis版不管主从版还是集群规格,replica作为备库不对外提供服务,只有在发生HA的时候,replica提升为master后才承担读写流量.这种架构读写请求都在master上完成, ...
- redis读写分离 java_Redis主从实现读写分离
前言 大家在工作中可能会遇到这样的需求,即Redis读写分离,目的是为了压力分散化.下面我将为大家介绍借助AWS的ELB实现读写分离,以写主读从为例. 实现 引用库文件 redis.clients j ...
- 阿里云Redis读写分离典型场景:如何轻松搭建电商秒杀系统
秒杀活动是绝大部分电商选择的低价促销,推广品牌的方式.不仅可以给平台带来用户量,还可以提高平台知名度.一个好的秒杀系统,可以提高平台系统的稳定性和公平性,获得更好的用户体验,提升平台的口碑,从而提升秒 ...
- redis读写分离 java_spring-data-redis读写分离
在对Redis进行性能优化时,一直想对Redis进行读写分离.但由于项目底层采用spring-data-redis对redis进行操作,参考spring官网却发现spring-data-redis目前 ...
- Redis 读写分离
1.什么是主从复制 主机数据更新后根据配置和策略,自动同步到备机的Master/Slaver,Master 以写为主,Slave以读为主 读写分离的优点 1.读写分离,性能提高 2.容灾快速恢复 查看 ...
- php redis 读写分离类,yii实现redis读写分离
/** * FileName:RedisCluster * 配置说明 * 配置为1主多从 或者 1个独立的服务器 * 写往主的里面写 * 读是从从的里面读 * 'class'=>'RedisCa ...
最新文章
- 物理学走到尽头了吗?
- 深入浅出mfc学习笔记——六大关键技术之仿真_运行时和动态创建
- MS Reporting Services 报表开发
- byte 8位有符号 与 char 16位无符号
- java操作ssdb:set、map、list..
- 东师2016年秋季计算机基础,东师2016年秋季《计算机基础》期末考核答案(1).doc
- 亟需为个人信息安全“保驾护航”
- java8 stream map 求最大值、最小值、平均数、求和
- 入门嵌入式HTML/CSS/脚本引擎 sciter
- 浅谈 Mousewheel 事件
- 期货开户公司想恶意滑点是做不到的
- 戴尔服务器系统缓存怎么清理,戴尔笔记本怎样清理磁盘空间
- HI3519 开发交流QQ群
- cnavas手绘图形库 : rough.js
- 推荐1位AI产品经理求职信息(企业方可要简历),新增3个JD(共计59个、AI PM可内推)...
- 联想服务器linux系统raid驱动,ThinkSystem服务器RAID 530/930系列阵列卡驱动及安装RHEL7.3要点说明...
- 基于微信小程序的校园食堂窗口自助点餐系统#毕业设计
- 如何去做词频统计和关键词共现分析
- 和过去的自己写的告别信
- echarts人体含水量(象形柱图)更改SVG(性别占比或其他占比百分比)
热门文章
- kali安装软件源软件
- C语言并集编程,c语言求并集和交集的关键代码,谢谢
- One or more PGP signatures could not be verified
- Citespace安装【Version Check】Your version‘s status cannot be verified due to a network connection issue
- 期货市场间竞争的比较优势分析
- C语言typedef和define、字节对齐的问题
- 曙光服务器面板显示感叹号,磁盘阵列和磁带库面板感叹号灯橙色
- Hadoop3.3.2+hbase2.4.10org.apache.hadoop.hbase.ipc.ServerNotRunningYetException: Server is not runn
- python:网络爬虫之遍历单个域名获取电影名称及年份
- 双边功率谱密度和单边功率谱密度_以高斯信号为例,计算幅度谱、相位谱、双边功率谱、双边功率谱密度、单边功率谱、单边功率谱密度。...