Springboot整合Redis主從實(shí)踐
前言
SpringBoot版本:2.3.2.RELEASE
原配置
原yml配置內(nèi)容:
spring: # Redis服務(wù)器配置 redis: host: 127.0.0.1 # Redis服務(wù)器連接端口 port: 6379 # Redis服務(wù)器連接密碼 password: redis@123 #連接超時(shí)時(shí)間(毫秒) timeout: 30000ms jedis: # Redis服務(wù)器連接池 pool: # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制) maxIdle: 400 #連接池中的最小空閑連接 minIdle: 100 #連接池中的最大空閑連接 maxActive: 400 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) maxWait: -1ms lettuce: pool: max-idle: 400 min-idle: 100 max-active: 400 max-wait: -1ms
原RedisConfig配置類(lèi):
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableCaching @AutoConfigureAfter(RedisAutoConfiguration.class) public class RedisConfig { @Bean @ConditionalOnMissingBean(value = StringRedisTemplate.class, name = "stringRedisTemplate") public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(factory); return template; } }
現(xiàn)配置
現(xiàn)yml配置內(nèi)容:
spring: redis: # 主節(jié)點(diǎn) master: host: 127.0.0.1 port: 6379 password: redis@123 # 副本節(jié)點(diǎn) replicas: - host: 127.0.0.1 port: 6380 #連接超時(shí)時(shí)間(毫秒) timeout: 30000ms jedis: # Redis服務(wù)器連接池 pool: # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制) maxIdle: 400 #連接池中的最小空閑連接 minIdle: 100 #連接池中的最大空閑連接 maxActive: 400 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) maxWait: -1ms lettuce: pool: max-idle: 400 min-idle: 100 max-active: 400 max-wait: -1ms
現(xiàn)RedisConfig配置類(lèi):
import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer; import com.juxiao.xchat.manager.cache.properties.RedisMasterReplicaProperties; import io.lettuce.core.ClientOptions; import io.lettuce.core.ReadFrom; import org.apache.commons.lang3.StringUtils; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.springframework.boot.autoconfigure.AutoConfigureAfter; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; import org.springframework.boot.autoconfigure.data.redis.RedisProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisPassword; import org.springframework.data.redis.connection.RedisStaticMasterReplicaConfiguration; 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.core.StringRedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; @Configuration @EnableCaching @AutoConfigureAfter(RedisAutoConfiguration.class) @EnableConfigurationProperties({RedisMasterReplicaProperties.class, RedisProperties.class}) public class RedisConfig { private final RedisMasterReplicaProperties properties; private final RedisProperties redisProperties; public RedisConfig(RedisMasterReplicaProperties redisMasterReplicaProperties, RedisProperties redisProperties) { this.properties = redisMasterReplicaProperties; this.redisProperties = redisProperties; } public LettuceConnectionFactory redisConnectionFactory(boolean readFromMaster) { RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration( properties.getMaster().getHost(), properties.getMaster().getPort() ); String password = properties.getMaster().getPassword(); if (StringUtils.isNotBlank(password)) { config.setPassword(RedisPassword.of(password)); } for (RedisMasterReplicaProperties.Node replica : properties.getReplicas()) { config.addNode(replica.getHost(), replica.getPort()); } // 連接池配置 LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder().commandTimeout(redisProperties.getTimeout()); // 使用 application.yml 中的 lettuce.pool 參數(shù) RedisProperties.Pool poolProps = redisProperties.getLettuce().getPool(); if (poolProps != null) { builder.poolConfig(poolConfig(poolProps)); } // 優(yōu)先從副本讀取 builder.readFrom(readFromMaster ? ReadFrom.MASTER : ReadFrom.REPLICA_PREFERRED); // 斷開(kāi)連接時(shí)拒絕命令[而不是再等待連接超時(shí)時(shí)間后再報(bào)錯(cuò)]、啟用自動(dòng)重連 builder.clientOptions(ClientOptions.builder() .disconnectedBehavior(ClientOptions.DisconnectedBehavior.REJECT_COMMANDS) .autoReconnect(true) .build()); LettucePoolingClientConfiguration lettucePoolingClientConfiguration = builder.build(); // 構(gòu)建連接工廠 LettuceConnectionFactory factory = new LettuceConnectionFactory(config, lettucePoolingClientConfiguration); // 禁用共享連接 默認(rèn)是true // factory.setShareNativeConnection(false); // 初始化工廠 否則調(diào)用StringRedisTemplate時(shí)會(huì)空指針 【因?yàn)閞edisConnectionFactory 方法沒(méi)有使用@Bean注解將LettuceConnectionFactory交給Spring工廠管理 所以需要手動(dòng)調(diào)用afterPropertiesSet方法初始化連接工廠】 factory.afterPropertiesSet(); return factory; } // 連接池參數(shù)綁定 private GenericObjectPoolConfig<?> poolConfig(RedisProperties.Pool poolProps) { GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>(); config.setMaxTotal(poolProps.getMaxActive()); config.setMaxIdle(poolProps.getMaxIdle()); config.setMinIdle(poolProps.getMinIdle()); config.setMaxWaitMillis(poolProps.getMaxWait().toMillis()); return config; } @Bean @ConditionalOnMissingBean(name = "redisTemplate") public RedisTemplate<Object, Object> redisTemplate(LettuceConnectionFactory redisConnectionFactory) { redisConnectionFactory.setShareNativeConnection(false); RedisTemplate<Object, Object> template = new RedisTemplate<>(); //使用fastjson序列化 FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class); // value值的序列化采用fastJsonRedisSerializer template.setValueSerializer(serializer); template.setHashValueSerializer(serializer); // key的序列化采用StringRedisSerializer template.setKeySerializer(new StringRedisSerializer()); template.setHashKeySerializer(new StringRedisSerializer()); template.setConnectionFactory(redisConnectionFactory); return template; } @Bean("masterStringRedisTemplate") @ConditionalOnMissingBean(name = "masterStringRedisTemplate") public StringRedisTemplate masterStringRedisTemplate() { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory(true)); return template; } @Bean("replicaStringRedisTemplate") @ConditionalOnMissingBean(name = "replicaStringRedisTemplate") public StringRedisTemplate replicaStringRedisTemplate() { StringRedisTemplate template = new StringRedisTemplate(); template.setConnectionFactory(redisConnectionFactory(false)); return template; } }
新增RedisMasterReplicaProperties配置類(lèi):
import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import java.util.ArrayList; import java.util.List; @Data @ConfigurationProperties(prefix = "spring.redis") public class RedisMasterReplicaProperties { /** * 主節(jié)點(diǎn) */ private Node master; /** * 從節(jié)點(diǎn) */ private List<Node> replicas = new ArrayList<>(); @Data public static class Node { /** * 主機(jī)地址 */ private String host; /** * 端口 */ private int port; /** * 密碼(主從模式master、slave密碼必須設(shè)置一樣的) */ private String password; } }
測(cè)試
@Resource(name = "masterStringRedisTemplate") private StringRedisTemplate masterStringRedisTemplate; @Resource(name = "replicaStringRedisTemplate") private StringRedisTemplate replicaStringRedisTemplate; @GetMapping("/test") public String test() { masterStringRedisTemplate.opsForValue().set("imu:test", "Hello6"); String value = replicaStringRedisTemplate.opsForValue().get("imu:test"); return value; }
LettuceConnectionFactory.setShareNativeConnection 方法的作用
代碼中這一行被注釋?zhuān)3至嗽镜哪J(rèn)配置true
// 禁用共享連接 默認(rèn)是true // factory.setShareNativeConnection(false);
在 Spring Data Redis 中,LettuceConnectionFactory 是一個(gè)用于管理 Redis 連接的工廠類(lèi),而 setShareNativeConnection(boolean shareNativeConnection) 方法用于控制是否 共享底層的 Redis 連接。
true(默認(rèn)):
- 適用于 大多數(shù)應(yīng)用,多個(gè) Redis 操作共享同一個(gè)底層連接,減少資源占用。
- 適用于 Spring Boot + RedisTemplate 場(chǎng)景。
false:
- 適用于 高并發(fā)、多線(xiàn)程環(huán)境,避免多個(gè)線(xiàn)程爭(zhēng)搶同一個(gè) Redis 連接。
- 適用于 WebFlux、Reactive、Pipeline 等場(chǎng)景。
一般來(lái)說(shuō),除非你的 Redis 操作出現(xiàn) 多線(xiàn)程連接爭(zhēng)用問(wèn)題,否則 不用手動(dòng)修改 setShareNativeConnection,保持默認(rèn)值即可!??
而:
- shareNativeConnection = true
- (默認(rèn))時(shí),Spring 只會(huì)創(chuàng)建 一個(gè)共享的 StatefulRedisConnection,那么 連接池的 max-active、max-idle、min-idle 這些配置不會(huì)生效。
- shareNativeConnection = false 時(shí),每次請(qǐng)求都會(huì)新建連接,這時(shí)連接池才會(huì)管理多個(gè)連接,此時(shí) max-active 等參數(shù)才會(huì)起作用。
- 也就是說(shuō)我們?cè)趛ml配置文件中配置的連接池信息都將不起作用
jedis: # Redis服務(wù)器連接池 pool: # 連接池最大連接數(shù)(使用負(fù)值表示沒(méi)有限制) maxIdle: 400 #連接池中的最小空閑連接 minIdle: 100 #連接池中的最大空閑連接 maxActive: 400 # 連接池最大阻塞等待時(shí)間(使用負(fù)值表示沒(méi)有限制) maxWait: -1ms lettuce: pool: max-idle: 400 min-idle: 100 max-active: 400 max-wait: -1ms
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
java實(shí)現(xiàn)計(jì)算地理坐標(biāo)之間的距離
java實(shí)現(xiàn)計(jì)算地理坐標(biāo)之間的距離,主要是通過(guò)計(jì)算兩經(jīng)緯度點(diǎn)之間的距離來(lái)實(shí)現(xiàn),有需要的小伙伴參考下吧2015-03-03java實(shí)現(xiàn)二維數(shù)組轉(zhuǎn)置的方法示例
這篇文章主要介紹了java實(shí)現(xiàn)二維數(shù)組轉(zhuǎn)置的方法,結(jié)合實(shí)例形式詳細(xì)分析了java二維數(shù)組轉(zhuǎn)置的原理、實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-10-10Java 8 動(dòng)態(tài)類(lèi)型語(yǔ)言L(fǎng)ambda表達(dá)式實(shí)現(xiàn)原理解析
Java 8支持動(dòng)態(tài)語(yǔ)言,看到了很酷的Lambda表達(dá)式,對(duì)一直以靜態(tài)類(lèi)型語(yǔ)言自居的Java,讓人看到了Java虛擬機(jī)可以支持動(dòng)態(tài)語(yǔ)言的目標(biāo)。接下來(lái)通過(guò)本文給大家介紹Java 8 動(dòng)態(tài)類(lèi)型語(yǔ)言L(fǎng)ambda表達(dá)式實(shí)現(xiàn)原理分析,需要的朋友可以參考下2017-02-02使用IDEA如何導(dǎo)入SpringBoot項(xiàng)目
這篇文章主要介紹了使用IDEA如何導(dǎo)入SpringBoot項(xiàng)目問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,2023-12-12java如何根據(jù)提供word模板導(dǎo)出word文檔詳解
在日常的開(kāi)發(fā)工作中,我們時(shí)常會(huì)遇到導(dǎo)出Word文檔報(bào)表的需求,比如公司的財(cái)務(wù)報(bào)表、醫(yī)院的患者統(tǒng)計(jì)報(bào)表、電商平臺(tái)的銷(xiāo)售報(bào)表等等,這篇文章主要給大家介紹了關(guān)于java如何根據(jù)提供word模板導(dǎo)出word文檔的相關(guān)資料,需要的朋友可以參考下2023-09-09Java實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池簡(jiǎn)易教程
這篇文章主要為大家介紹了Java實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接池簡(jiǎn)易教程,感興趣的小伙伴們可以參考一下2016-01-01