redis配置
redis配置(举例ruoyi框架源码实例)
·
--痛苦是进步的试金石
redis配置
文章目录
前言
redis的配置信息,记录一下,也可以拿来就用,也可以做一个实例,举一反三。
一、引入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
二、配置信息
spring: redis: # 地址 host: localhost # 端口,默认为6379 port: 6379 # 数据库索引 database: 0 # 密码 password: # 连接超时时间 timeout: 10s lettuce: pool: # 连接池中的最小空闲连接 min-idle: 0 # 连接池中的最大空闲连接 max-idle: 8 # 连接池的最大数据库连接数 max-active: 8 # #连接池最大阻塞等待时间(使用负值表示没有限制) max-wait: -1ms
三、序列化文件
redis会乱码,就需要序列化,这里是序列化文件,可以直接拿来用
package com.ruoyi.framework.config; import java.nio.charset.Charset; import org.springframework.data.redis.serializer.RedisSerializer; import org.springframework.data.redis.serializer.SerializationException; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONReader; import com.alibaba.fastjson2.JSONWriter; /** * Redis使用FastJson序列化 * redis直接存中文会乱码,就需要序列化一下,这里是序列化的配置文件,在RedisConfig里面调用进行真正的序列化 */ public class FastJson2JsonRedisSerializer<T> implements RedisSerializer<T> { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8"); private Class<T> clazz; public FastJson2JsonRedisSerializer(Class<T> clazz) { super(); this.clazz = clazz; } @Override public byte[] serialize(T t) throws SerializationException { if (t == null) { return new byte[0]; } return JSON.toJSONString(t, JSONWriter.Feature.WriteClassName).getBytes(DEFAULT_CHARSET); } @Override public T deserialize(byte[] bytes) throws SerializationException { if (bytes == null || bytes.length <= 0) { return null; } String str = new String(bytes, DEFAULT_CHARSET); return JSON.parseObject(str, clazz, JSONReader.Feature.SupportAutoType); } }
四、redis配置文件
package com.ruoyi.framework.config; import org.springframework.cache.annotation.CachingConfigurerSupport; 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.core.RedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.data.redis.serializer.StringRedisSerializer; /** * redis配置 * * @author ruoyi */ @Configuration @EnableCaching public class RedisConfig extends CachingConfigurerSupport { @Bean public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Object, Object> template = new RedisTemplate<>(); template.setConnectionFactory(connectionFactory); // FastJson2JsonRedisSerializer对所有对象序列化 FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class); // 使用StringRedisSerializer来序列化和反序列化redis的key值 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(serializer); // Hash的key也采用StringRedisSerializer的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(serializer); template.afterPropertiesSet(); return template; } // redis接口限流,好东西啊, @Bean public DefaultRedisScript<Long> limitScript() { DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>(); redisScript.setScriptText(limitScriptText()); redisScript.setResultType(Long.class); return redisScript; } /** * 限流脚本,脚本的意思就是获取传进来的 count time,根据传进来的 ip-类名-方法名 这个类型的key获取对应的数据current。和现有的count比,大就返回current。没有的话就给current+1, * 然后判断这个current是不是第一次出现,就是他的值是不是1,是的话说明是刚创建的,就给他搞个过期时间(传进来参数的默认值是120s)。不是就直接返回current。 */ private String limitScriptText() { return "local key = KEYS[1]\n" + "local count = tonumber(ARGV[1])\n" + "local time = tonumber(ARGV[2])\n" + "local current = redis.call('get', key);\n" + "if current and tonumber(current) > count then\n" + " return tonumber(current);\n" + "end\n" + "current = redis.call('incr', key)\n" + "if tonumber(current) == 1 then\n" + " redis.call('expire', key, time)\n" + "end\n" + "return tonumber(current);"; } }
五、redis工具类
package com.ruoyi.common.core.redis; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.BoundSetOperations; import org.springframework.data.redis.core.HashOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Component; /** * spring redis 工具类 * * @author ruoyi **/ @Component public class RedisCache { @Autowired public RedisTemplate redisTemplate; /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 */ public <T> void setCacheObject(final String key, final T value) { redisTemplate.opsForValue().set(key, value); } /** * 缓存基本的对象,Integer、String、实体类等 * * @param key 缓存的键值 * @param value 缓存的值 * @param timeout 时间 * @param timeUnit 时间颗粒度 */ public <T> void setCacheObject(final String key, final T value, final Integer timeout, final TimeUnit timeUnit) { redisTemplate.opsForValue().set(key, value, timeout, timeUnit); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout) { return expire(key, timeout, TimeUnit.SECONDS); } /** * 设置有效时间 * * @param key Redis键 * @param timeout 超时时间 * @param unit 时间单位 * @return true=设置成功;false=设置失败 */ public boolean expire(final String key, final long timeout, final TimeUnit unit) { return redisTemplate.expire(key, timeout, unit); } /** * 获取有效时间 * * @param key Redis键 * @return 有效时间 */ public long getExpire(final String key) { return redisTemplate.getExpire(key); } /** * 判断 key是否存在 * * @param key 键 * @return true 存在 false不存在 */ public Boolean hasKey(String key) { return redisTemplate.hasKey(key); } /** * 获得缓存的基本对象。 * * @param key 缓存键值 * @return 缓存键值对应的数据 */ public <T> T getCacheObject(final String key) { ValueOperations<String, T> operation = redisTemplate.opsForValue(); return operation.get(key); } /** * 删除单个对象 * * @param key */ public boolean deleteObject(final String key) { return redisTemplate.delete(key); } /** * 删除集合对象 * * @param collection 多个对象 * @return */ public boolean deleteObject(final Collection collection) { return redisTemplate.delete(collection) > 0; } /** * 缓存List数据 * * @param key 缓存的键值 * @param dataList 待缓存的List数据 * @return 缓存的对象 */ public <T> long setCacheList(final String key, final List<T> dataList) { Long count = redisTemplate.opsForList().rightPushAll(key, dataList); return count == null ? 0 : count; } /** * 获得缓存的list对象 * * @param key 缓存的键值 * @return 缓存键值对应的数据 */ public <T> List<T> getCacheList(final String key) { return redisTemplate.opsForList().range(key, 0, -1); } /** * 缓存Set * * @param key 缓存键值 * @param dataSet 缓存的数据 * @return 缓存数据的对象 */ public <T> BoundSetOperations<String, T> setCacheSet(final String key, final Set<T> dataSet) { BoundSetOperations<String, T> setOperation = redisTemplate.boundSetOps(key); Iterator<T> it = dataSet.iterator(); while (it.hasNext()) { setOperation.add(it.next()); } return setOperation; } /** * 获得缓存的set * * @param key * @return */ public <T> Set<T> getCacheSet(final String key) { return redisTemplate.opsForSet().members(key); } /** * 缓存Map * * @param key * @param dataMap */ public <T> void setCacheMap(final String key, final Map<String, T> dataMap) { if (dataMap != null) { redisTemplate.opsForHash().putAll(key, dataMap); } } /** * 获得缓存的Map * * @param key * @return */ public <T> Map<String, T> getCacheMap(final String key) { return redisTemplate.opsForHash().entries(key); } /** * 往Hash中存入数据 * * @param key Redis键 * @param hKey Hash键 * @param value 值 */ public <T> void setCacheMapValue(final String key, final String hKey, final T value) { redisTemplate.opsForHash().put(key, hKey, value); } /** * 获取Hash中的数据 * * @param key Redis键 * @param hKey Hash键 * @return Hash中的对象 */ public <T> T getCacheMapValue(final String key, final String hKey) { HashOperations<String, String, T> opsForHash = redisTemplate.opsForHash(); return opsForHash.get(key, hKey); } /** * 获取多个Hash中的数据 * * @param key Redis键 * @param hKeys Hash键集合 * @return Hash对象集合 */ public <T> List<T> getMultiCacheMapValue(final String key, final Collection<Object> hKeys) { return redisTemplate.opsForHash().multiGet(key, hKeys); } /** * 删除Hash中的某条数据 * * @param key Redis键 * @param hKey Hash键 * @return 是否成功 */ public boolean deleteCacheMapValue(final String key, final String hKey) { return redisTemplate.opsForHash().delete(key, hKey) > 0; } /** * 获得缓存的基本对象列表 * * @param pattern 字符串前缀 * @return 对象列表 */ public Collection<String> keys(final String pattern) { return redisTemplate.keys(pattern); } }
六、redis接口限流–注解实现
package com.ruoyi.framework.aspectj; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.script.RedisScript; import org.springframework.stereotype.Component; import com.ruoyi.common.annotation.RateLimiter; import com.ruoyi.common.enums.LimitType; import com.ruoyi.common.exception.ServiceException; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.ip.IpUtils; /** * 限流处理 */ @Aspect // 把这个类加入到容器中,然后再添加上注解就可以就可以看成是一个切面容器。 @Component public class RateLimiterAspect { private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class); // 引入redis模板 private RedisTemplate<Object, Object> redisTemplate; // 引入redis限流脚本 private RedisScript<Long> limitScript; // 这玩意没用到,啥玩意,还有上面的两个为啥不从容器里面拿? @Autowired public void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate) { this.redisTemplate = redisTemplate; } // 这玩意没用到 @Autowired public void setLimitScript(RedisScript<Long> limitScript) { this.limitScript = limitScript; } // 方法之前用 @RateLimiter 修饰的方法,再执行之前会调用这个方法模块 @Before("@annotation(rateLimiter)") public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable { // 获取注解里面的参数,可能是自定义参数,要不就是默认参数 int time = rateLimiter.time(); int count = rateLimiter.count(); // 获取限流的 ip-类名-方法名 String combineKey = getCombineKey(rateLimiter, point); // emm,说白了这样获取的list中,有切只有一个值,多一个少一个都报错 List<Object> keys = Collections.singletonList(combineKey); try { // 带着脚本文件去执行,获取到数量。脚本中写法一般来说不会返回null,写了保险点吧 Long number = redisTemplate.execute(limitScript, keys, count, time); if (StringUtils.isNull(number) || number.intValue() > count) { throw new ServiceException("访问过于频繁,请稍候再试"); } log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey); } catch (ServiceException e) { throw e; } catch (Exception e) { throw new RuntimeException("服务器限流异常,请稍候再试"); } } // 获取限流的 ip-类名-方法名 public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) { StringBuffer stringBuffer = new StringBuffer(rateLimiter.key()); // 如果限流类型是ip,那就把IP也加进去 if (rateLimiter.limitType() == LimitType.IP) { stringBuffer.append(IpUtils.getIpAddr()).append("-"); } // 获取被注解标注的方法信息 MethodSignature signature = (MethodSignature) point.getSignature(); Method method = signature.getMethod(); // 获取方法所在的类对象 Class<?> targetClass = method.getDeclaringClass(); stringBuffer.append(targetClass.getName()).append("-").append(method.getName()); return stringBuffer.toString(); } }
七、redis接口限流–注解代码
package com.ruoyi.common.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import com.ruoyi.common.constant.CacheConstants; import com.ruoyi.common.enums.LimitType; /** * 限流注解 */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface RateLimiter { /** * 限流key */ public String key() default CacheConstants.RATE_LIMIT_KEY; /** * 限流时间,单位秒 */ public int time() default 60; /** * 限流次数 */ public int count() default 100; /** * 限流类型 */ public LimitType limitType() default LimitType.DEFAULT; }
总结
应该可以拿来直接用,使用脚本限流也可以,限流用的不多的情况下方便一点,不过 一般来说nginx、getway限流好一些吧。Hyst
后记
redis中的# 数据库索引 database: 0 解释
数据库索引 database: 0"似乎是指Redis中的一个数据库索引,Redis默认提供了16个逻辑数据库,索引从0到15。
在Redis中,可以通过SELECT
命令来切换不同的数据库。例如,要切换到索引为1的数据库,可以使用以下命令:
SELECT 1
在Redis中,每个数据库是相互独立的,它们之间的key-value对不会互相影响。这与传统的关系型数据库有所不同,在关系型数据库中,通常会有多个schema来划分不同的数据集合。
如果你想知道当前正在使用哪个数据库,可以使用INFO keyspace
命令来查看当前数据库的键分布情况,这个命令会展示出所有数据库的键的数量。
如果你想清空当前数据库的所有键,可以使用FLUSHDB
命令。
如果你想清空所有数据库的所有键,可以使用FLUSHALL
命令。
以下是一些示例代码,展示如何在Redis中使用这些命令:
# 切换到索引为1的数据库
SELECT 1
# 查看数据库的键分布情况
INFO keyspace
# 清空当前数据库的所有键
FLUSHDB
# 清空所有数据库的所有键
FLUSHALL
以上命令需要在Redis客户端或者命令行中执行。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
已为社区贡献8条内容
所有评论(0)