最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文詳解SpringBoot?Redis多數(shù)據(jù)源配置

 更新時(shí)間:2024年11月17日 09:33:01   作者:Lvan  
Spring?Boot默認(rèn)只允許一種?Redis?連接池配置,且配置受限于?Lettuce?包,不夠靈活,所以本文將為大家介紹如何自定義Redis配置方案實(shí)現(xiàn)多數(shù)據(jù)源支持,需要的可以參考下

問(wèn)題背景

在實(shí)際項(xiàng)目中,我們需要支持兩種 Redis 部署方式(集群和主從),但 Spring Boot 默認(rèn)只允許一種 Redis 連接池配置,且配置受限于 Lettuce 包,不夠靈活。為了解決這個(gè)問(wèn)題,我深入研究了 Spring Boot 的源碼,自定義了 Redis 配置方案,實(shí)現(xiàn)了多數(shù)據(jù)源支持。這一調(diào)整讓我們可以在同一項(xiàng)目中靈活適配不同的 Redis 部署方式,解決了配置的局限性,讓系統(tǒng)更具擴(kuò)展性和靈活性。

源碼分析

LettuceConnectionConfiguration 的核心配置

LettuceConnectionConfiguration 位于 org.springframework.boot.autoconfigure.data.redis 包內(nèi),使其作用域被限制,無(wú)法直接擴(kuò)展。為支持集群和主從 Redis 部署,我們需要通過(guò)自定義配置,繞過(guò)該限制。

LettuceConnectionFactory 是 Redis 連接的核心工廠,依賴于 DefaultClientResources,并通過(guò) LettuceClientConfiguration 設(shè)置諸如連接池、超時(shí)時(shí)間等基礎(chǔ)參數(shù)。配置如下:

class LettuceConnectionConfiguration extends RedisConnectionConfiguration {
	@Bean(destroyMethod = "shutdown")
	@ConditionalOnMissingBean(ClientResources.class)
	DefaultClientResources lettuceClientResources(ObjectProvider<ClientResourcesBuilderCustomizer> customizers) {
		DefaultClientResources.Builder builder = DefaultClientResources.builder();
		customizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
		return builder.build();
	}

	@Bean
	@ConditionalOnMissingBean(RedisConnectionFactory.class)
	LettuceConnectionFactory redisConnectionFactory(
			ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,
			ClientResources clientResources) {
		LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(builderCustomizers, clientResources,
				getProperties().getLettuce().getPool());
		return createLettuceConnectionFactory(clientConfig);
	}
}

lettuceClientResources 方法定義了 ClientResources,作為單例供所有 Redis 連接工廠復(fù)用。因此,自定義 LettuceConnectionFactory 時(shí)可以直接使用這個(gè)共享的 ClientResources

客戶端配置與初始化解析

LettuceClientConfiguration 的獲取getLettuceClientConfiguration 方法用以構(gòu)建 Lettuce 客戶端配置,應(yīng)用基本參數(shù)并支持連接池:

private LettuceClientConfiguration getLettuceClientConfiguration(
       ObjectProvider<LettuceClientConfigurationBuilderCustomizer> builderCustomizers,  
       ClientResources clientResources, Pool pool) {
    LettuceClientConfigurationBuilder builder = createBuilder(pool);
    applyProperties(builder);
    if (StringUtils.hasText(getProperties().getUrl())) {  
       customizeConfigurationFromUrl(builder);  
    }
    builder.clientOptions(createClientOptions());
    builder.clientResources(clientResources);
    builderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));
    return builder.build();
}

創(chuàng)建 LettuceClientConfigurationBuildercreateBuilder 方法生成 LettuceClientConfigurationBuilder,并判斷是否啟用連接池。若啟用,PoolBuilderFactory 會(huì)創(chuàng)建包含連接池的配置,該連接池通過(guò) GenericObjectPoolConfig 構(gòu)建。

private static final boolean COMMONS_POOL2_AVAILABLE = ClassUtils.isPresent("org.apache.commons.pool2.ObjectPool", 
       RedisConnectionConfiguration.class.getClassLoader());
       
private LettuceClientConfigurationBuilder createBuilder(Pool pool) {  
    if (isPoolEnabled(pool)) {  
       return new PoolBuilderFactory().createBuilder(pool);  
    }  
    return LettuceClientConfiguration.builder();  
}

protected boolean isPoolEnabled(Pool pool) {  
    Boolean enabled = pool.getEnabled();  
    return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE;  
}

private static class PoolBuilderFactory {  
  
    LettuceClientConfigurationBuilder createBuilder(Pool properties) {  
       return LettucePoolingClientConfiguration.builder().poolConfig(getPoolConfig(properties));  
    }  
  
    private GenericObjectPoolConfig<?> getPoolConfig(Pool properties) {  
       GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();  
       config.setMaxTotal(properties.getMaxActive());  
       config.setMaxIdle(properties.getMaxIdle());  
       config.setMinIdle(properties.getMinIdle());  
       if (properties.getTimeBetweenEvictionRuns() != null) {  
          config.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());  
       }  
       if (properties.getMaxWait() != null) {  
          config.setMaxWait(properties.getMaxWait());  
       }  
       return config;  
    }   
}

參數(shù)應(yīng)用與超時(shí)配置applyProperties 方法用于配置 Redis 的基礎(chǔ)屬性,如 SSL、超時(shí)時(shí)間等。

private LettuceClientConfigurationBuilder applyProperties(
    LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
    if (getProperties().isSsl()) {
        builder.useSsl();
    }
    if (getProperties().getTimeout() != null) {
        builder.commandTimeout(getProperties().getTimeout());
    }
    if (getProperties().getLettuce() != null) {
        RedisProperties.Lettuce lettuce = getProperties().getLettuce();
        if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {
            builder.shutdownTimeout(getProperties().getLettuce().getShutdownTimeout());
        }
    }
    if (StringUtils.hasText(getProperties().getClientName())) {
        builder.clientName(getProperties().getClientName());
    }
    return builder;
}

Redis 多模式支持

在創(chuàng)建 LettuceConnectionFactory 時(shí),根據(jù)不同配置模式(哨兵、集群或單節(jié)點(diǎn))構(gòu)建連接工廠:

private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
    if (getSentinelConfig() != null) {
        return new LettuceConnectionFactory(getSentinelConfig(), clientConfiguration);
    }
    if (getClusterConfiguration() != null) {
        return new LettuceConnectionFactory(getClusterConfiguration(), clientConfiguration);
    }
    return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
}
  • 哨兵模式getSentinelConfig() 返回哨兵配置實(shí)例,實(shí)現(xiàn)高可用 Redis。
  • 集群模式:若啟用集群,getClusterConfiguration() 返回集群配置以支持分布式 Redis。
  • 單節(jié)點(diǎn)模式:默認(rèn)單節(jié)點(diǎn)配置,返回 StandaloneConfig。

getClusterConfiguration 的方法實(shí)現(xiàn):

protected final RedisClusterConfiguration getClusterConfiguration() {
    if (this.clusterConfiguration != null) {
        return this.clusterConfiguration;
    }
    if (this.properties.getCluster() == null) {
        return null;
    }
    RedisProperties.Cluster clusterProperties = this.properties.getCluster();
    RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());
    if (clusterProperties.getMaxRedirects() != null) {
        config.setMaxRedirects(clusterProperties.getMaxRedirects());
    }
    config.setUsername(this.properties.getUsername());
    if (this.properties.getPassword() != null) {
        config.setPassword(RedisPassword.of(this.properties.getPassword()));
    }
    return config;
}
  • 集群節(jié)點(diǎn)與重定向:配置集群節(jié)點(diǎn)信息及最大重定向次數(shù)。
  • 用戶名與密碼:集群連接的身份驗(yàn)證配置。

Redis 多數(shù)據(jù)源配置思路

通過(guò)以上的源碼分析,我梳理出了 LettuceConnectionFactory 構(gòu)建的流程:

1.LettuceClientConfiguration 初始化:

  • 連接池初始化
  • Redis 基礎(chǔ)屬性配置
  • 設(shè)置 ClientResource

2.RedisConfiguration 初始化,官方支持以下配置:

  • RedisClusterConfiguration
  • RedisSentinelConfiguration
  • RedisStaticMasterReplicaConfiguration
  • RedisStandaloneConfiguration

Redis 多數(shù)據(jù)源配置實(shí)戰(zhàn)

復(fù)用 LettuceClientConfiguration 配置

/**
 * 這里與源碼有個(gè)不同的是返回了builder,因?yàn)楹罄m(xù)實(shí)現(xiàn)的主從模式的lettuce客戶端仍有自定義的配置,返回了builder則相對(duì)靈活一點(diǎn)。
 */
private LettuceClientConfiguration.LettuceClientConfigurationBuilder getLettuceClientConfiguration(ClientResources clientResources, RedisProperties.Pool pool) {  
    LettuceClientConfiguration.LettuceClientConfigurationBuilder builder = createBuilder(pool);  
    applyProperties(builder);  
    builder.clientOptions(createClientOptions());  
    builder.clientResources(clientResources);  
    return builder;  
}  
  
/**  
 * 創(chuàng)建Lettuce客戶端配置構(gòu)建器  
 */  
private LettuceClientConfiguration.LettuceClientConfigurationBuilder createBuilder(RedisProperties.Pool pool) {  
    if (isPoolEnabled(pool)) {  
        return LettucePoolingClientConfiguration.builder().poolConfig(getPoolConfig(pool));  
    }  
    return LettuceClientConfiguration.builder();  
}  
  
/**  
 * 判斷Redis連接池是否啟用  
 */  
private boolean isPoolEnabled(RedisProperties.Pool pool) {  
    Boolean enabled = pool.getEnabled();  
    return (enabled != null) ? enabled : COMMONS_POOL2_AVAILABLE;  
}  
  
/**  
 * 根據(jù)Redis屬性配置創(chuàng)建并返回一個(gè)通用對(duì)象池配置  
 */  
private GenericObjectPoolConfig<?> getPoolConfig(RedisProperties.Pool properties) {  
    GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();  
    config.setMaxTotal(properties.getMaxActive());  
    config.setMaxIdle(properties.getMaxIdle());  
    config.setMinIdle(properties.getMinIdle());  
    if (properties.getTimeBetweenEvictionRuns() != null) {  
        config.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());  
    }  
    if (properties.getMaxWait() != null) {  
        config.setMaxWait(properties.getMaxWait());  
    }  
    return config;  
}  
  
/**  
 * 根據(jù)Redis屬性配置構(gòu)建Lettuce客戶端配置  
 *  
 * @param builder Lettuce客戶端配置的構(gòu)建器  
 * @return 返回配置完畢的Lettuce客戶端配置構(gòu)建器  
 */  
private LettuceClientConfiguration.LettuceClientConfigurationBuilder applyProperties(  
        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {  
    if (redisProperties.isSsl()) {  
        builder.useSsl();  
    }  
    if (redisProperties.getTimeout() != null) {  
        builder.commandTimeout(redisProperties.getTimeout());  
    }  
    if (redisProperties.getLettuce() != null) {  
        RedisProperties.Lettuce lettuce = redisProperties.getLettuce();  
        if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {  
            builder.shutdownTimeout(redisProperties.getLettuce().getShutdownTimeout());  
        }  
    }  
    if (StringUtils.hasText(redisProperties.getClientName())) {  
        builder.clientName(redisProperties.getClientName());  
    }  
    return builder;  
}  
  
/**  
 * 創(chuàng)建客戶端配置選項(xiàng)  
 */  
private ClientOptions createClientOptions() {  
    ClientOptions.Builder builder = initializeClientOptionsBuilder();  
    Duration connectTimeout = redisProperties.getConnectTimeout();  
    if (connectTimeout != null) {  
        builder.socketOptions(SocketOptions.builder().connectTimeout(connectTimeout).build());  
    }  
    return builder.timeoutOptions(TimeoutOptions.enabled()).build();  
}  
  
/**  
 * 初始化ClientOptions構(gòu)建器  
 */  
private ClientOptions.Builder initializeClientOptionsBuilder() {  
    if (redisProperties.getCluster() != null) {  
        ClusterClientOptions.Builder builder = ClusterClientOptions.builder();  
        RedisProperties.Lettuce.Cluster.Refresh refreshProperties = redisProperties.getLettuce().getCluster().getRefresh();  
        ClusterTopologyRefreshOptions.Builder refreshBuilder = ClusterTopologyRefreshOptions.builder()  
                .dynamicRefreshSources(refreshProperties.isDynamicRefreshSources());  
        if (refreshProperties.getPeriod() != null) {  
            refreshBuilder.enablePeriodicRefresh(refreshProperties.getPeriod());  
        }  
        if (refreshProperties.isAdaptive()) {  
            refreshBuilder.enableAllAdaptiveRefreshTriggers();  
        }  
        return builder.topologyRefreshOptions(refreshBuilder.build());  
    }  
    return ClientOptions.builder();  
}

復(fù)用 Redis 集群初始化

/**  
 * 獲取Redis集群配置  
 */  
private RedisClusterConfiguration getClusterConfiguration() {  
    if (redisProperties.getCluster() == null) {  
        return null;  
    }  
    RedisProperties.Cluster clusterProperties = redisProperties.getCluster();  
    RedisClusterConfiguration config = new RedisClusterConfiguration(clusterProperties.getNodes());  
    if (clusterProperties.getMaxRedirects() != null) {  
        config.setMaxRedirects(clusterProperties.getMaxRedirects());  
    }  
    config.setUsername(redisProperties.getUsername());  
    if (redisProperties.getPassword() != null) {  
        config.setPassword(RedisPassword.of(redisProperties.getPassword()));  
    }  
    return config;  
}

自定義 Redis 主從配置

@Getter  
@Setter  
@ConfigurationProperties(prefix = "spring.redis")  
public class RedisPropertiesExtend {  
  
    private MasterReplica masterReplica;  
  
    @Getter  
    @Setter    public static class MasterReplica {  
  
        private String masterNodes;  
        private List<String> replicaNodes;  
    }  
}

/**  
 * 獲取主從配置  
 */  
private RedisStaticMasterReplicaConfiguration getStaticMasterReplicaConfiguration() {  
    if (redisPropertiesExtend.getMasterReplica() == null) {  
        return null;  
    }  
    RedisPropertiesExtend.MasterReplica masterReplica = redisPropertiesExtend.getMasterReplica();  
    List<String> masterNodes = CharSequenceUtil.split(masterReplica.getMasterNodes(), StrPool.COLON);  
    RedisStaticMasterReplicaConfiguration config = new RedisStaticMasterReplicaConfiguration(  
            masterNodes.get(0), Integer.parseInt(masterNodes.get(1)));  
    for (String replicaNode : masterReplica.getReplicaNodes()) {  
        List<String> replicaNodes = CharSequenceUtil.split(replicaNode, StrPool.COLON);  
        config.addNode(replicaNodes.get(0), Integer.parseInt(replicaNodes.get(1)));  
    }  
    config.setUsername(redisProperties.getUsername());  
    if (redisProperties.getPassword() != null) {  
        config.setPassword(RedisPassword.of(redisProperties.getPassword()));  
    }  
    return config;  
}

注冊(cè) LettuceConnectionFactory

@Primary  
@Bean(name = "redisClusterConnectionFactory")  
@ConditionalOnMissingBean(name = "redisClusterConnectionFactory")  
public LettuceConnectionFactory redisClusterConnectionFactory(ClientResources clientResources) {  
    LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources,  
            redisProperties.getLettuce().getPool())  
            .build();  
    return new LettuceConnectionFactory(getClusterConfiguration(), clientConfig);  
}  
  
@Bean(name = "redisMasterReplicaConnectionFactory")  
@ConditionalOnMissingBean(name = "redisMasterReplicaConnectionFactory")  
public LettuceConnectionFactory redisMasterReplicaConnectionFactory(ClientResources clientResources) {  
    LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(clientResources,  
            redisProperties.getLettuce().getPool())  
            .readFrom(ReadFrom.REPLICA_PREFERRED)  
            .build();  
    return new LettuceConnectionFactory(getStaticMasterReplicaConfiguration(), clientConfig);  
}

應(yīng)用

@Bean  
@ConditionalOnMissingBean(name = "redisTemplate")  
public RedisTemplate<Object, Object> redisTemplate(@Qualifier("redisClusterConnectionFactory") RedisConnectionFactory redisConnectionFactory) {  
    RedisTemplate<Object, Object> template = new RedisTemplate<>();  
    template.setConnectionFactory(redisConnectionFactory);  
    return template;  
}

@Bean  
@ConditionalOnMissingBean(name = "masterReplicaRedisTemplate")  
public RedisTemplate<Object, Object> masterReplicaRedisTemplate(@Qualifier("redisMasterReplicaConnectionFactory") RedisConnectionFactory redisConnectionFactory) {  
    RedisTemplate<Object, Object> template = new RedisTemplate<>();  
    template.setConnectionFactory(redisConnectionFactory);  
    return template;  
}

最后

深入理解 Spring Boot Redis 自動(dòng)配置源碼是實(shí)現(xiàn)靈活多數(shù)據(jù)源支持的關(guān)鍵。通過(guò)源碼分析,我們能夠在保持框架兼容性的同時(shí),精準(zhǔn)定制和擴(kuò)展功能,從而滿足復(fù)雜的業(yè)務(wù)需求。

以上就是一文詳解SpringBoot Redis多數(shù)據(jù)源配置的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot Redis多數(shù)據(jù)源配置的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java使用Redis實(shí)現(xiàn)秒殺功能

    Java使用Redis實(shí)現(xiàn)秒殺功能

    這篇文章主要為大家詳細(xì)介紹了Java使用Redis實(shí)現(xiàn)秒殺功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-09-09
  • Java concurrency線程池之線程池原理(一)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java concurrency線程池之線程池原理(一)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    這篇文章主要為大家詳細(xì)介紹了Java concurrency線程池之線程池原理,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-06-06
  • Spring Cloud出現(xiàn)Options Forbidden 403問(wèn)題解決方法

    Spring Cloud出現(xiàn)Options Forbidden 403問(wèn)題解決方法

    本篇文章主要介紹了Spring Cloud出現(xiàn)Options Forbidden 403問(wèn)題解決方法,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-11-11
  • java中l(wèi)ist的用法和實(shí)例講解

    java中l(wèi)ist的用法和實(shí)例講解

    這篇文章主要介紹了java中l(wèi)ist的用法和實(shí)例講解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-07-07
  • mybatis-plugin插件執(zhí)行原理解析

    mybatis-plugin插件執(zhí)行原理解析

    這篇文章主要介紹了mybatis-plugin插件執(zhí)行原理,我們就需要來(lái)研究下Executor,ParameterHandler,ResultSetHandler,StatementHandler這4個(gè)對(duì)象的具體跟sql相關(guān)的方法,然后再進(jìn)行修改,就可以直接起到aop的作用,需要的朋友可以參考下
    2022-10-10
  • 關(guān)于Spring框架中異常處理情況淺析

    關(guān)于Spring框架中異常處理情況淺析

    最近學(xué)習(xí)Spring時(shí),認(rèn)識(shí)到Spring異常處理的強(qiáng)大,這篇文章主要給大家介紹了關(guān)于Spring框架中異常處理情況的相關(guān)資料,通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-08-08
  • Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用詳解

    Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用詳

    這篇文章主要介紹了Springboot mybatis plus druid多數(shù)據(jù)源解決方案 dynamic-datasource的使用,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Java 集合去重的三種方法

    Java 集合去重的三種方法

    本主要介紹了Java 集合去重的三種方法,包含HashSet,Stream API和手動(dòng)遍歷并使用Map三種,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • 關(guān)于Springboot的擴(kuò)展點(diǎn)DisposableBean的原理解析

    關(guān)于Springboot的擴(kuò)展點(diǎn)DisposableBean的原理解析

    這篇文章主要介紹了關(guān)于Springboot的擴(kuò)展點(diǎn)DisposableBean的原理解析,DisposableBean是一個(gè)接口,為Spring bean提供了一種釋放資源的方式 ,只有一個(gè)擴(kuò)展方法destroy(),需要的朋友可以參考下
    2023-05-05
  • java設(shè)計(jì)模式-代理模式(實(shí)例講解)

    java設(shè)計(jì)模式-代理模式(實(shí)例講解)

    下面小編就為大家?guī)?lái)一篇java設(shè)計(jì)模式-代理模式(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-09-09

最新評(píng)論

阿拉善右旗| 晋城| 大同县| 昂仁县| 宁海县| 内黄县| 彭山县| 湘阴县| 特克斯县| 江阴市| 修文县| 凯里市| 衡南县| 香格里拉县| 余姚市| 南投县| 北川| 双城市| 安图县| 镇赉县| 永靖县| 樟树市| 乌什县| 灵寿县| 萨迦县| 泉州市| 革吉县| 鸡西市| 崇阳县| 基隆市| 江门市| 宁陕县| 阳原县| 两当县| 东城区| 衡东县| 大冶市| 宝鸡市| 志丹县| 海阳市| 三亚市|