总线 控制器 逻辑门 主频 缓存 处理器
主页 正文

SpringDataRedis动态数据源切换攻略

spring-data-redis动态切换数据源

最近他遇到了一个烦人的请求。
通常我们不会这样使用Redis,但是这两个Redis本来是不同的业务集群,现在我们需要通过一个微服务来一起访问它们。

其实我们在实际业务开发中也能遇到类似的场景。
比如Redis读写分离就是spring-data-Redis不提供的功能。

-data -r 编辑此接口

不封装返回架构的结构,默认被云提供商隐藏。
目前对外开放的只有动态VIP域​​名。

因此,需要实现一种在春转红的基础上动态改变Redis连接的机制。

spring-data-redis类的配置为:org.springframework.boot.autoconfigure.data.redis.RedisProperties,可以进行配置。
单个 Redis 连接配置实例或 Redis 集群。
基于这些配置,会生成Redis连接工厂RedisConnectionFactory

核心接口核心与连接基础之间的抽象连接:

通过这个我们可以知道该板子可以实现可以动态发送到不同网络的程序。
连接的RedisConnectionFactory就足够了,根据源码加载源码我们可以知道所有的RedisConnectionFactory都在@ConditionalOnMissingBean框架中,也就是说我们可以用我们的RedisConnectionFactory来替换它。

项目地址:https://github.com/JoJoTec/spring-boot-starter-redis-lated

我们可以为RedisProperties Encapsulate配置screen配置多Redis连接,即MultiRedisProperties:

@Data@NoArgsConstructor@ConfigurationProperties(pre fix="spring.redis") publicclassMultiRe DisProperties{/*** 必须配置默认连接。
配置键为默认 */publicstaticfinalStringDEFAULT="default";privatebooleanenableMulti=false;privateMapmulti;}

此配置基于原始架构。
即用户可以使用原来的配置,也可以使用这个多Redis配置,这就需要配置true.redis.enable-multi=true。
关键放在多形式表中,即概念名称的来源。

接下来我们将实现MultiRedisLettuceConnectionFactory,这是一个会动态改变Redis连接的RedisConnectionFactory。
entsInitializingBean,DisposableBean,RedisConnectionFactory,ReactiveRedisConnectionFactory{ privatefinalMapconnectionFactoryMap,private aticfinalThreadLocalcurrentRedis=newThreadLocal<>(); publicMultiRedisLettuceConnectionFactory(MapconnectionFactoryMap){ this.conn ectionFactoryMap=connectionFactoryMap;}publicvoidsetCurrentRedis(StringcurrentRedis){if(!connectionFactoryMap.containsKey(currentRedis)){thrownewRedisRelatedException("i .owsException{connectionFactoryMap.values().forEach(LettuceConnectionFactory :: destroy);}@OverridepublicvoidafterPropertiesSet()throwsException{connectionFactoryMap 。
价值 s().forEach(LettuceConnectionFactory::afterPropertiesSet);}privateLettuceConnectionFactorycurrentLettuceConnectionFactory() onFactory.currentRedis.get(); if(StringUtils.isNotBlank(currentRedis)){MultiRedisLettuceConnectionFactory.currentRedis.remove();returnconnectionFactoryMap.gets);}returnconnectionFactoryMap.get(MultiRedisPropert ies.DEFAULT);}@OverridepublicReactiveRedisConnectiongetReactiveConnection(){returncurrentLettuceConnectionFactory().getReactiveConnection();}@OverridepublicReactiveRedisClusterConnectiongetReactiveClusterConnection ction(){returncurrentLettuceConnectionFactory().getReactiveClusterConnection();}@OverridepublicRedisConnectiongetConnection(){returncurrentLettuceConnectionFactory().getConnection();}@OverridepublicRedisClusterConnectiongetClusterConnection(){returncurrentLettuceeConnectionFactory ().getClusterConnection() );}}

逻辑很简单,就是给出一个接口,并将其返回的数据放在ThreadLocal中。

接下来,将 MultiRedisLettuceConnectionFactory 注册为 ApplicationContext 中的 Bean:

@ConditionalOnProperty(pre fix="spring.redis", value="enable-multi", matchIfMissing=false)@Configurationis(proxyBe anMethods=false)publicclassRedisCustomizedConfiguration{/***@parambuilderCustomizers*@paramclientResources*@parammultiRedisProperties*@return*@seeorg.springframework.boot.autoconfigure.data.r edis.LettuceConnectionConfiguration*/@BeanpublicMultiRedisLettuceConnectionFactorymultiRedisLettuceConnectionFactory(ObjectProviderbuilderCustomizers, ClientResourcesclientResources,MultiRedisPropertiesmultiRedisProperties,ObjectProvidersentinelConfigurationProvider,ObjectProviderc lusterConfigurationProvider){ // 读取配置 MapconnectionFactoryMap=Maps.newHashMap(); Mapmulti=multiRedisProperties.getMulti(); k,v)->{ // 这是框架中原始源代码使用RedisProperties的方式。
onProvider, clusterConfigurationProvider);LettuceConnectionFactorylettuceConnectionFactory=lettuceConnectionConfiguration.redisConnectionFactory(builderCustomizers,clientResources);connectionFactoryMap.put(k,lettuceConnectionFactory);});redinnewMultiRedisLettuceConnectionFactory(connectionFactoryMap);}}

让我们尝试使用嵌入式返回来启动本地返回。
和这个单元测试。
我们启动两个Redis,两个Redis中不同的Key,是否存在,并且我们测试同步接口,我们从几个线程调用同步接口,异步接口会订阅多次,无需等待测试有效性。

导入com.github.jo。
.api.BeforeAll,导入器 g.junit.jupiter.api.Test;importorg.junit.jupiter.api.extension.ExtendWith;importorg.redisson.api.RedissonClient;importorg.springframework.beans.factory.annotation.Autowired;importorg. springframework.boot.autoconfigure。
启用AutoCon配置; 导入org.springframework.boot.test.context.SpringBootTest;导入org.springframework.context.annotation.Bean;导入org.springframework.context.annotation.Configuration;导入org.springframework.data.redis.core.ReactiveSt ringRedisTemplate;importorg.springframework.data.redis.core.StringRedisTemplate;importorg.springframework.test.context.junit.jupiter.SpringExtension;importreactor.core.publisher.Mono;importredis.embedded.RedisS erver;importjava.util.concurrent.TimeUnit;importjava.util.concurrent.atomic.AtomicBoolean;@ExtendWith(SpringExtension.class)@SpringBootTest(properties={"spring.redis.enable-multi=true""spring.redis.multi 。
默认 .host=127.0.0.1","spring.redis.multi.default.port=6379","spring.redis.multi.test.host=27.0.0.1","spring.redis.multi.test.port=6380 ",}) publicclassMultiRedisTest{ // 初始化两个redisprivatestaticRedisSe rverredisServer;privatestaticRedisServerredisServer2;@BeforeAllpublicstaticvoidsetUp() throwsException{System.out.println("startredis");redisServer=RedisServer.builder().port(6379).profecta("最大堆200m").adificate();redisServer2=RedisServer.生成器()。
端口(6380)。
设置(“maxheap200m”)。
“重新启动”);}@AfterAllpublicstat icvoidtearDown()抛出异常{System.out.println(“stopre dis”);redisServer.stop();redisServer2.stop();System.out.println(“redisstopped”);}@EnableAutoConfiguration @Configurationpublicstaticcl {assApp} @AutowiredprivateStringRedisTemplateredisTemplate; @AutowiredprivateReactiveStringRedisTemplatereactiveRedisTemplate; @ AutowiredprivateMultiRedisLettuceConnectionFactorymultiRedisLettuce ConnectionFactory; privatevoidtestMulti(Stringsuffix){ // 使用默认连接,设置 "testDefault" + suffix, "testDefault" 键值对 redisTemplate.opsForValue().set("testDefault" + suffix, "testDefault");测试链接,设置“testSecond”d“+后缀”testDefault”键值 multiRedisLettuceConnectionFactory.setCurrentRedis("test"); "+ 后缀" 存在 "testSe 。
" + 后缀不存在 Assertions.assertTrue(redisTemplate.hasKey("testDefault" + suffix); Assertions.assertFalse(redisTemplate.hasKey("testSecond" +后缀); 后缀不是“testSe”。
Assertions.assertFalse(redisTemplate.hasKey("testDefault"+suffix); test"); Assertions.assertTrue(redisTemplate.hasKey("testSecond"+suffix); }//验证详情 @TestpublicvoidtestMultiBlock(){testMulti(""); }//多线程验证@TestpublicvoidtestMultiBlockMultiThread()中断 异常{Threadthread[]=newThread[50];AtomicBooleanresult=newAtomicBoolean(true);for(int=0;i{ 尝试{testMulti(""+finalI);} catch(异常e)。
0; ireactiveMulti(Stringsuffix){returnreactiveRedisTemplate.opsForValue( ).set("testReactiveDefault"+后缀"testReactiveDefault").flatMap(b->{multiRedisLettuceConnection Factory.setCurrentRedis("test");returnreactiveRedisTemplate.opsForValue().set("testReactiveSecond" + suffix, "testReactiveSecond");}).flatMap(b->{returnreactiveRedisTemplate.hasKey("testReactiveDefault"+suffix);} ).map(b->{Assertions.assertTrue(b);System.out.println(Thread.currentThread().getName());returnb;}).flatMap(b->{ returnreactiveRedisTemplate.hasKey("testReactiveSecond" + 后缀);

讲讲本地锁至分布式锁的演进和Redis实现,扩展 Redlock 红锁

本文主要讲述从本地锁到分布式锁的演变过程以及如何应用Redis来实现Redlock(红锁算法)。
本地锁常用于解决单个服务的读写并发问题。
但随着服务规模的扩大,如果多个服务并发运行,可能会导致多个用户同时获取锁,从而导致数据不一致。
为了解决这个问题,引入分布式锁来抽象锁定操作,使所有服务可以共享锁定信息。
Redis 提供互斥和共享锁定功能作为其常见实现之一。
Redis最初使用setnx命令来实现基本的分布式锁定,但这存在死锁的风险。
这可以通过设置过期时间来解决,但是需要注意的是,带选项的Redis set命令可以实现原子操作,保证锁和设置时间的同步。
为了防止他人解锁,引入身份验证,解锁时进行验证。
我们还使用Redis的Lua脚本实现原子解锁,以保证我们操作的可靠性。
然而,这并不能完全解决所有问题,例如锁更新、网络通信的不确定性等。
Redisson库基于Redis提供了更全面的锁管理,包括自动续订和正确的锁使用原则。
实际部署必须考虑Redis的集群模式和网络通信的影响,例如主从复制、哨兵模式等,这可能会影响正确的锁获取和同步。
最终,Redis 的 Redlock 算法跨多个 Redis 实例协作,在分布式环境中提供安全的锁定机制。
我们强调客户端必须在锁有效期内完成任务,并通过随机重试策略减少并发冲突。
不过,对于数据一致性要求较高的场景,可能需要考虑Zookeeper等其他机制来评估性能和安全性。
本文重点介绍了分布式锁定的复杂性以及选择合适的实现方法的重要性,以及实现代码时的注意事项,例如避免无限递归调用以及由于重试机制可能出现的问题。

Redis 实现分布式锁 +Redisson 源码解析

在某些场景下,多个进程需要以互斥的方式独占共享资源。
这时候分布式锁就成了一个非常有用的工具。
随着互联网技术的快速发展,数据规模不断扩大,分布式系统也越来越普遍。
一个应用程序通常部署在多台机器(多个节点)上。
某些情况下,为了保证数据不重复,同一个任务在同一时间只能运行在一个节点上,即保证某一方法只能在一个节点上运行。
由线程执行。
在单机环境下,应用程序处于同一个进程中,线程安全只需要通过Java提供的concurrent包下的volatile、ReentrantLock、synchronized和线程安全类来保证。
在多机部署环境中,不同机器、不同进程需要保证多进程下的线程安全,于是分布式锁应运而生。
实现分布式锁的三种主要方式包括:zookeeper、Redis和Redisson。
三种方式都可以实现分布式锁,但是基于Redis实现的性能通常更好,具体选择取决于业务需求。
本文主要讨论基于Redis实现分布式锁的解决方案,并对Redisson的RedissonLock和RedissonRedLock的源码进行了分析和比较。
为了保证分布式锁的可用性,实现上至少需要满足以下四个条件:互斥性、过期自动解锁、请求识别、正确解锁。
实现方法是使用Redis set命令加上nx和px参数实现锁定,使用Lua脚本解锁。
实现代码包括加锁和解锁流程、核心实现命令和Lua脚本。
这种实现的主要优点是可以保证互斥和自动解锁,但是存在一个单点风险,就是如果Redis存储锁key对应的节点挂掉了,锁可能会丢失,从而导致 多个客户端持有锁。
健康)状况。
Redisson提供了更高级的实现分布式可重入锁的方法,包括RedLock算法。
Redisson不仅支持单点模式、主从模式、哨兵模式和集群模式,还提供了一系列分布式Java通用对象和锁的实现,如可重入锁、公平锁、互锁、读写锁等 Redisson 使用简单,旨在分离对 Redis 的关注点,让开发人员更加专注于业务逻辑。
通过Redisson实现的分布式锁比纯Redis实现拥有更完整的功能,比如可重入锁、失败重试、最大等待时间设置等。
同时RedissonLock也面临着节点挂掉时失去锁的风险。
为了解决这个问题,Redisson提供了实现RedLock算法的RedissonRedLock,能够真正解决单点故障的问题,但是需要为RedissonRedLock额外搭建Redis环境。
如果业务场景可以容忍这种小概率的错误,建议使用RedissonLock。
如果不能容忍,建议使用RedissonRedLock。
另外,RedLock算法假设存在N个独立的Redismaster节点,并保证在N个实例上获取和释放锁,以提高分布式系统的可靠性。
在实现分布式锁时,还需要注意实现RedLock算法所需的Redission节点的构建。
这些节点可以是单机模式、主从模式、哨兵模式或者集群模式,保证任意一个节点挂掉后,仍然可以保持分布式锁可用。
使用Redisson实现分布式锁时,尝试通过RedissonMultiLock获取和释放锁的核心代码为RedLock算法的实现提供了支持。

热门资讯
精简指令集的优缺点
Redis缓存MySQL数据最佳实践指南
国家大基金力挺,半导体芯片领域基金投资潜力分析
MySQL缓存机制解析其缓存特性与应用
详解如何快速区分电脑CPU性能高低
3.1ghz是什么水平
cpu取消超频开不开机
e5cpu玩游戏有什么区别