基于Redis的Spring Boot 幂等性插件模块封装

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: [基于Redis支持集群、哨兵等模式]

幂等性注解定义

importjava.lang.annotation.*;
importjava.util.concurrent.TimeUnit;
/*** @author jeckxu*/@Inherited@Target(ElementType.METHOD)
@Retention(value=RetentionPolicy.RUNTIME)
public@interfaceIdempotent {
/*** 幂等操作的唯一标识,使用spring el表达式 用#来引用方法参数* @return Spring-EL expression*/Stringkey() default"";
/*** 有效期 默认:1 有效期要大于程序执行时间,否则请求还是可能会进来* @return expireTime*/intexpireTime() default1;
/*** 时间单位 默认:s* @return TimeUnit*/TimeUnittimeUnit() defaultTimeUnit.SECONDS;
/*** 提示信息,可自定义* @return String*/Stringinfo() default"ROOT.IDEMPOTENT.DO_NOT_REPEAT_THE_OPERATION";
/*** 是否在业务完成后删除key true:删除 false:不删除* @return boolean*/booleandelKey() defaulttrue;
}

幂等性配置类

importorg.springframework.boot.context.properties.ConfigurationProperties;
/*** @author Jeckxu*/@ConfigurationProperties(MagicalIdempotentProperties.PREFIX)
publicclassMagicalIdempotentProperties {
publicstaticfinalStringPREFIX="magical.idempotent";
/*** 是否开启:默认为:false,便于生成配置提示。*/privateBooleanenabled=Boolean.FALSE;
/*** 单机配置:redis 服务地址*/privateStringaddress="redis://127.0.0.1:6379";
/*** 密码配置*/privateStringpassword;
/*** db*/privateIntegerdatabase=0;
/*** 连接池大小*/privateIntegerpoolSize=20;
/*** 最小空闲连接数*/privateIntegeridleSize=5;
/*** 连接空闲超时,单位:毫秒*/privateIntegeridleTimeout=60000;
/*** 连接超时,单位:毫秒*/privateIntegerconnectionTimeout=3000;
/*** 命令等待超时,单位:毫秒*/privateIntegertimeout=10000;
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/privateModemode=Mode.single;
/*** 主从模式,主地址*/privateStringmasterAddress;
/*** 主从模式,从地址*/privateString[] slaveAddress;
/*** 哨兵模式:主名称*/privateStringmasterName;
/*** 哨兵模式地址*/privateString[] sentinelAddress;
/*** 集群模式节点地址*/privateString[] nodeAddress;
publicenumMode {
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/single, master, sentinel, cluster;
    }
/*** 是否开启:默认为:false,便于生成配置提示。*/publicBooleangetEnabled() {
returnthis.enabled;
    }
/*** 单机配置:redis 服务地址*/publicStringgetAddress() {
returnthis.address;
    }
/*** 密码配置*/publicStringgetPassword() {
returnthis.password;
    }
/*** db*/publicIntegergetDatabase() {
returnthis.database;
    }
/*** 连接池大小*/publicIntegergetPoolSize() {
returnthis.poolSize;
    }
/*** 最小空闲连接数*/publicIntegergetIdleSize() {
returnthis.idleSize;
    }
/*** 连接空闲超时,单位:毫秒*/publicIntegergetIdleTimeout() {
returnthis.idleTimeout;
    }
/*** 连接超时,单位:毫秒*/publicIntegergetConnectionTimeout() {
returnthis.connectionTimeout;
    }
/*** 命令等待超时,单位:毫秒*/publicIntegergetTimeout() {
returnthis.timeout;
    }
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/publicModegetMode() {
returnthis.mode;
    }
/*** 主从模式,主地址*/publicStringgetMasterAddress() {
returnthis.masterAddress;
    }
/*** 主从模式,从地址*/publicString[] getSlaveAddress() {
returnthis.slaveAddress;
    }
/*** 哨兵模式:主名称*/publicStringgetMasterName() {
returnthis.masterName;
    }
/*** 哨兵模式地址*/publicString[] getSentinelAddress() {
returnthis.sentinelAddress;
    }
/*** 集群模式节点地址*/publicString[] getNodeAddress() {
returnthis.nodeAddress;
    }
/*** 是否开启:默认为:false,便于生成配置提示。*/publicvoidsetEnabled(finalBooleanenabled) {
this.enabled=enabled;
    }
/*** 单机配置:redis 服务地址*/publicvoidsetAddress(finalStringaddress) {
this.address=address;
    }
/*** 密码配置*/publicvoidsetPassword(finalStringpassword) {
this.password=password;
    }
/*** db*/publicvoidsetDatabase(finalIntegerdatabase) {
this.database=database;
    }
/*** 连接池大小*/publicvoidsetPoolSize(finalIntegerpoolSize) {
this.poolSize=poolSize;
    }
/*** 最小空闲连接数*/publicvoidsetIdleSize(finalIntegeridleSize) {
this.idleSize=idleSize;
    }
/*** 连接空闲超时,单位:毫秒*/publicvoidsetIdleTimeout(finalIntegeridleTimeout) {
this.idleTimeout=idleTimeout;
    }
/*** 连接超时,单位:毫秒*/publicvoidsetConnectionTimeout(finalIntegerconnectionTimeout) {
this.connectionTimeout=connectionTimeout;
    }
/*** 命令等待超时,单位:毫秒*/publicvoidsetTimeout(finalIntegertimeout) {
this.timeout=timeout;
    }
/*** 集群模式,单机:single,主从:master,哨兵模式:sentinel,集群模式:cluster*/publicvoidsetMode(finalModemode) {
this.mode=mode;
    }
/*** 主从模式,主地址*/publicvoidsetMasterAddress(finalStringmasterAddress) {
this.masterAddress=masterAddress;
    }
/*** 主从模式,从地址*/publicvoidsetSlaveAddress(finalString[] slaveAddress) {
this.slaveAddress=slaveAddress;
    }
/*** 哨兵模式:主名称*/publicvoidsetMasterName(finalStringmasterName) {
this.masterName=masterName;
    }
/*** 哨兵模式地址*/publicvoidsetSentinelAddress(finalString[] sentinelAddress) {
this.sentinelAddress=sentinelAddress;
    }
/*** 集群模式节点地址*/publicvoidsetNodeAddress(finalString[] nodeAddress) {
this.nodeAddress=nodeAddress;
    }
}

幂等性切面类

importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.annotation.After;
importorg.aspectj.lang.annotation.Aspect;
importorg.aspectj.lang.annotation.Before;
importorg.aspectj.lang.annotation.Pointcut;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
importorg.jeckxu.magical.core.idempotent.exception.IdempotentException;
importorg.jeckxu.magical.core.idempotent.expression.KeyResolver;
importorg.redisson.api.RMapCache;
importorg.redisson.api.RedissonClient;
importorg.slf4j.Logger;
importorg.slf4j.LoggerFactory;
importorg.springframework.util.CollectionUtils;
importorg.springframework.util.StringUtils;
importorg.springframework.web.context.request.RequestContextHolder;
importorg.springframework.web.context.request.ServletRequestAttributes;
importjavax.servlet.http.HttpServletRequest;
importjava.lang.reflect.Method;
importjava.time.LocalDateTime;
importjava.util.Arrays;
importjava.util.HashMap;
importjava.util.Map;
importjava.util.concurrent.TimeUnit;
/*** @author jeckxu*/@AspectpublicclassIdempotentAspect {
privatestaticfinalLoggerLOGGER=LoggerFactory.getLogger(IdempotentAspect.class);
privateThreadLocal<Map<String, Object>>threadLocal=newThreadLocal();
privatestaticfinalStringRMAPCACHE_KEY="magical:idempotent";
privatestaticfinalStringKEY="key";
privatestaticfinalStringDELKEY="delKey";
privatefinalRedissonClientredissonClient;
privatefinalKeyResolverkeyResolver;
@Pointcut("@annotation(org.jeckxu.magical.core.idempotent.annotation.Idempotent)")
publicvoidpointCut() {
    }
@Before("pointCut()")
publicvoidbeforePointCut(JoinPointjoinPoint) throwsException {
ServletRequestAttributesrequestAttributes= (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequestrequest=requestAttributes.getRequest();
MethodSignaturesignature= (MethodSignature) joinPoint.getSignature();
Methodmethod=signature.getMethod();
if (!method.isAnnotationPresent(Idempotent.class)) {
return;
        }
Idempotentidempotent=method.getAnnotation(Idempotent.class);
Stringkey;
// 若没有配置 幂等 标识编号,则使用 url + 参数列表作为区分if (StringUtils.isEmpty(idempotent.key())) {
Stringurl=request.getRequestURL().toString();
StringargString=Arrays.asList(joinPoint.getArgs()).toString();
key=url+argString;
        } else {
// 使用jstl 规则区分key=keyResolver.resolver(idempotent, joinPoint);
        }
longexpireTime=idempotent.expireTime();
Stringinfo=idempotent.info();
TimeUnittimeUnit=idempotent.timeUnit();
booleandelKey=idempotent.delKey();
// do not need check nullRMapCache<String, Object>rMapCache=redissonClient.getMapCache(RMAPCACHE_KEY);
Stringvalue=LocalDateTime.now().toString().replace("T", " ");
Objectv1;
if (null!=rMapCache.get(key)) {
// had storedthrownewIdempotentException(info);
        }
synchronized (this) {
v1=rMapCache.putIfAbsent(key, value, expireTime, TimeUnit.SECONDS);
if (null!=v1) {
thrownewIdempotentException(info);
            } else {
LOGGER.info("[idempotent]:has stored key={},value={},expireTime={}{},now={}", key, value, expireTime, timeUnit, LocalDateTime.now().toString());
            }
        }
Map<String, Object>map=CollectionUtils.isEmpty(threadLocal.get()) ?newHashMap<>(4) : threadLocal.get();
map.put(KEY, key);
map.put(DELKEY, delKey);
threadLocal.set(map);
    }
@After("pointCut()")
publicvoidafterPointCut(JoinPointjoinPoint) {
Map<String, Object>map=threadLocal.get();
if (CollectionUtils.isEmpty(map)) {
return;
        }
RMapCache<Object, Object>mapCache=redissonClient.getMapCache(RMAPCACHE_KEY);
if (mapCache.size() ==0) {
return;
        }
Stringkey=map.get(KEY).toString();
booleandelKey= (boolean) map.get(DELKEY);
if (delKey) {
mapCache.fastRemove(key);
LOGGER.info("[idempotent]:has removed key={}", key);
        }
threadLocal.remove();
    }
publicIdempotentAspect(finalRedissonClientredissonClient, finalKeyResolverkeyResolver) {
this.redissonClient=redissonClient;
this.keyResolver=keyResolver;
    }
}

幂等性异常定义

/*** Idempotent Exception If there is a custom global exception, you need to inherit the* custom global exception.** @author jeckxu*/publicclassIdempotentExceptionextendsException {
publicIdempotentException() {
super();
   }
publicIdempotentException(Stringmessage) {
super(message);
   }
publicIdempotentException(Stringmessage, Throwablecause) {
super(message, cause);
   }
publicIdempotentException(Throwablecause) {
super(cause);
   }
protectedIdempotentException(Stringmessage, Throwablecause, booleanenableSuppression,
booleanwritableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
   }
}


幂等性唯一标志处理器

importorg.aspectj.lang.JoinPoint;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
/*** @author jeckxu* @date 2020/11/10* <p>* 唯一标志处理器*/publicinterfaceKeyResolver {
/*** 解析处理 key* @param idempotent 接口注解标识* @param point 接口切点信息* @return 处理结果*/Stringresolver(Idempotentidempotent, JoinPointpoint);
}
importorg.aspectj.lang.JoinPoint;
importorg.aspectj.lang.reflect.MethodSignature;
importorg.jeckxu.magical.core.idempotent.annotation.Idempotent;
importorg.springframework.core.LocalVariableTableParameterNameDiscoverer;
importorg.springframework.expression.Expression;
importorg.springframework.expression.spel.standard.SpelExpressionParser;
importorg.springframework.expression.spel.support.StandardEvaluationContext;
importjava.lang.reflect.Method;
/*** @author jeckxu*/publicclassExpressionResolverimplementsKeyResolver {
privatestaticfinalSpelExpressionParserPARSER=newSpelExpressionParser();
privatestaticfinalLocalVariableTableParameterNameDiscovererDISCOVERER=newLocalVariableTableParameterNameDiscoverer();
@OverridepublicStringresolver(Idempotentidempotent, JoinPointpoint) {
Object[] arguments=point.getArgs();
String[] params=DISCOVERER.getParameterNames(getMethod(point));
StandardEvaluationContextcontext=newStandardEvaluationContext();
for (intlen=0; len<params.length; len++) {
context.setVariable(params[len], arguments[len]);
      }
Expressionexpression=PARSER.parseExpression(idempotent.key());
returnexpression.getValue(context, String.class);
   }
/*** 根据切点解析方法信息* @param joinPoint 切点信息* @return Method 原信息*/privateMethodgetMethod(JoinPointjoinPoint) {
MethodSignaturesignature= (MethodSignature) joinPoint.getSignature();
Methodmethod=signature.getMethod();
if (method.getDeclaringClass().isInterface()) {
try {
method=joinPoint.getTarget().getClass().getDeclaredMethod(joinPoint.getSignature().getName(),
method.getParameterTypes());
         }
catch (SecurityException|NoSuchMethodExceptione) {
thrownewRuntimeException(e);
         }
      }
returnmethod;
   }
}

幂等性异常统一处理

importorg.jeckxu.magical.core.idempotent.exception.IdempotentException;
importorg.jeckxu.magical.core.launch.destroy.MagicalDestroying;
importorg.jeckxu.magical.core.launch.utils.I18nMessageUtils;
importorg.jeckxu.magical.core.tool.api.R;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.core.annotation.Order;
importorg.springframework.http.HttpStatus;
importorg.springframework.web.bind.annotation.ExceptionHandler;
importorg.springframework.web.bind.annotation.ResponseStatus;
importorg.springframework.web.bind.annotation.RestControllerAdvice;
importorg.springframework.web.servlet.DispatcherServlet;
importjavax.servlet.Servlet;
@Order(3)
@Configuration(proxyBeanMethods=false)
@ConditionalOnWebApplication(type=ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class})
@RestControllerAdvicepublicclassMagicalIdempotentExceptionTranslatorimplementsMagicalDestroying {
privatestaticfinalorg.slf4j.Loggerlog=org.slf4j.LoggerFactory.getLogger(MagicalIdempotentExceptionTranslator.class);
@ExceptionHandler(IdempotentException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
publicR<Object>handleError(IdempotentExceptione) {
e.printStackTrace();
returnR.fail(1999, I18nMessageUtils.get(e.getMessage()));
    }
}

幂等插件自动配置【初始化】

importorg.jeckxu.magical.core.idempotent.aspect.IdempotentAspect;
importorg.jeckxu.magical.core.idempotent.expression.ExpressionResolver;
importorg.jeckxu.magical.core.idempotent.expression.KeyResolver;
importorg.jeckxu.magical.core.idempotent.prop.MagicalIdempotentProperties;
importorg.jeckxu.magical.core.launch.destroy.MagicalDestroying;
importorg.jeckxu.magical.core.tool.utils.StringUtil;
importorg.redisson.Redisson;
importorg.redisson.api.RedissonClient;
importorg.redisson.config.*;
importorg.springframework.boot.autoconfigure.AutoConfigureAfter;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnClass;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
importorg.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
importorg.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
importorg.springframework.boot.context.properties.EnableConfigurationProperties;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.Configuration;
/*** 幂等插件初始化* @author jeckxu*/@Configuration(proxyBeanMethods=false)
@ConditionalOnClass(RedissonClient.class)
@EnableConfigurationProperties(MagicalIdempotentProperties.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnProperty(value="magical.idempotent.enabled", havingValue="true")
publicclassIdempotentAutoConfigurationimplementsMagicalDestroying {
privatestaticConfigsingleConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
SingleServerConfigserversConfig=config.useSingleServer();
serversConfig.setAddress(properties.getAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setConnectionPoolSize(properties.getPoolSize());
serversConfig.setConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigmasterSlaveConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
MasterSlaveServersConfigserversConfig=config.useMasterSlaveServers();
serversConfig.setMasterAddress(properties.getMasterAddress());
serversConfig.addSlaveAddress(properties.getSlaveAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigsentinelConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
SentinelServersConfigserversConfig=config.useSentinelServers();
serversConfig.setMasterName(properties.getMasterName());
serversConfig.addSentinelAddress(properties.getSentinelAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setDatabase(properties.getDatabase());
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
privatestaticConfigclusterConfig(MagicalIdempotentPropertiesproperties) {
Configconfig=newConfig();
ClusterServersConfigserversConfig=config.useClusterServers();
serversConfig.addNodeAddress(properties.getNodeAddress());
Stringpassword=properties.getPassword();
if (StringUtil.isNotBlank(password)) {
serversConfig.setPassword(password);
      }
serversConfig.setMasterConnectionPoolSize(properties.getPoolSize());
serversConfig.setMasterConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setSlaveConnectionPoolSize(properties.getPoolSize());
serversConfig.setSlaveConnectionMinimumIdleSize(properties.getIdleSize());
serversConfig.setIdleConnectionTimeout(properties.getConnectionTimeout());
serversConfig.setConnectTimeout(properties.getConnectionTimeout());
serversConfig.setTimeout(properties.getTimeout());
returnconfig;
   }
/*** 切面 拦截处理所有 @Idempotent* @return Aspect*/@BeanpublicIdempotentAspectidempotentAspect(MagicalIdempotentPropertiesproperties) {
returnnewIdempotentAspect(redissonClient(properties), newExpressionResolver());
   }
/*** key 解析器* @return KeyResolver*/@Bean@ConditionalOnMissingBean(KeyResolver.class)
publicKeyResolverkeyResolver() {
returnnewExpressionResolver();
   }
privatestaticRedissonClientredissonClient(MagicalIdempotentPropertiesproperties) {
MagicalIdempotentProperties.Modemode=properties.getMode();
Configconfig;
switch (mode) {
casesentinel:
config=sentinelConfig(properties);
break;
casecluster:
config=clusterConfig(properties);
break;
casemaster:
config=masterSlaveConfig(properties);
break;
casesingle:
config=singleConfig(properties);
break;
default:
config=newConfig();
break;
      }
returnRedisson.create(config);
   }
}

yml配置项

magical:  idempotent:    enabled: false    address: redis://192.168.0.1:26379    password: ${YOUR_PASSWORD}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
18天前
|
编解码 NoSQL Java
使用Spring Boot + Redis 队列实现视频文件上传及FFmpeg转码的技术分享
【8月更文挑战第30天】在当前的互联网应用中,视频内容的处理与分发已成为不可或缺的一部分。对于视频平台而言,高效、稳定地处理用户上传的视频文件,并对其进行转码以适应不同设备的播放需求,是提升用户体验的关键。本文将围绕使用Spring Boot结合Redis队列技术来实现视频文件上传及FFmpeg转码的过程,分享一系列技术干货。
55 3
|
23天前
|
缓存 NoSQL Java
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
【Azure Redis 缓存】示例使用 redisson-spring-boot-starter 连接/使用 Azure Redis 服务
|
29天前
|
NoSQL Java Redis
Redis6入门到实战------ 八、Redis与Spring Boot整合
这篇文章详细介绍了如何在Spring Boot项目中整合Redis,包括在`pom.xml`中添加依赖、配置`application.properties`文件、创建配置类以及编写测试类来验证Redis的连接和基本操作。
Redis6入门到实战------ 八、Redis与Spring Boot整合
|
2月前
|
NoSQL API Redis
c++开发redis module问题之为什么在使用RedisModule_GetApi之前要通过((void**)ctx)[0]这种方式获取其地址
c++开发redis module问题之为什么在使用RedisModule_GetApi之前要通过((void**)ctx)[0]这种方式获取其地址
|
17天前
|
NoSQL Redis 缓存
Redis 加持下的 Spring 应用性能革命:见证毫秒级响应速度,打造极致用户体验!
【8月更文挑战第31天】Redis 是一个高性能键值存储系统,常用于数据库、缓存及消息中间件。与 Spring 框架集成后,可显著提升应用性能,特别是在高并发场景下。本文通过电子商务网站商品详情页的例子,展示了如何配置 Redis 并使用 `RedisTemplate` 进行数据操作,通过缓存策略优化应用性能,减轻数据库压力。例如,在 `ProductService` 类中,先从 Redis 获取商品信息,若未命中则从数据库获取并缓存至 Redis。此外,还介绍了如何通过 REST 控制器模拟 HTTP 请求进行测试。在实际项目中,结合 Spring Cache 注解可更便捷地管理缓存策略。
30 0
|
19天前
|
缓存 NoSQL Java
惊!Spring Boot遇上Redis,竟开启了一场缓存实战的革命!
【8月更文挑战第29天】在互联网时代,数据的高速读写至关重要。Spring Boot凭借简洁高效的特点广受开发者喜爱,而Redis作为高性能内存数据库,在缓存和消息队列领域表现出色。本文通过电商平台商品推荐系统的实战案例,详细介绍如何在Spring Boot项目中整合Redis,提升系统响应速度和用户体验。
42 0
|
1月前
|
NoSQL Java Redis
Spring Boot集成Redis全攻略:高效数据存取,打造性能飞跃的Java微服务应用!
【8月更文挑战第3天】Spring Boot是备受欢迎的微服务框架,以其快速开发与轻量特性著称。结合高性能键值数据库Redis,可显著增强应用性能。集成步骤包括:添加`spring-boot-starter-data-redis`依赖,配置Redis服务器参数,注入`RedisTemplate`或`StringRedisTemplate`进行数据操作。这种集成方案适用于缓存、高并发等场景,有效提升数据处理效率。
224 2
|
1月前
|
NoSQL Java API
Spring Boot 中集成Redis
主要介绍了 redis 的使用场景、安装过程,以及 Spring Boot 中集成 redis 的详细步骤。在实际项目中,通常都用 redis 作为缓存,在查询数据库的时候,会先从 redis 中查找,如果有信息,则从 redis 中取;如果没有,则从数据库中查,并且同步到 redis 中,下次 redis 中就有了。更新和删除也是如此,都需要同步到 redis。redis 在高并发场景下运用的很多。
|
24天前
|
缓存 NoSQL Java
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
【Azure Redis 缓存】定位Java Spring Boot 使用 Jedis 或 Lettuce 无法连接到 Redis的网络连通性步骤
|
2月前
|
NoSQL 编译器 Redis
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响
c++开发redis module问题之如果Redis加载了多个C++编写的模块,并且它们都重载了operator new,会有什么影响