SpringBoot 1.5.22 Release 版通过 yml 文件配置redis 切换 database 实现
创始人
2024-06-03 13:39:01

文章目录

    • 1. 项目背景
    • 2. 知识准备
    • 3. 正确配置
    • 4.小结

1. 项目背景

问题:最近在做一个项目,通过jackson 的方式将数据 缓存到 redis 后,再根据key 取出数据为 null。

原因:SpringBoot 版本采用 SpringBoot 1.5.22 Release, 采用yml 配置 redis ,database 采用 1。但实际上系统一直使用的数据库0,所以导致取出的数据是null。

 redis:database: 1host: 192.168.1.xxxpassword: xxxpool:max-active: 8max-idle: 8max-wait: -1min-idle: 0port: 6380timeout: 60000

原来的 redisConfig 配置:

	@Bean(name="redisTemplate")public RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(factory);RedisSerializer redisSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(redisSerializer);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);RedisSerializer stringSerializer = new StringRedisSerializer();redisTemplate.setHashKeySerializer(stringSerializer);redisTemplate.setHashValueSerializer(stringSerializer);return redisTemplate;}

2. 知识准备

Jedis和Lettuce的区别
jedis和Lettuce都是Redis的客户端,它们都可以连接Redis服务器,但是在SpringBoot2.0之后默认都是使用的Lettuce这个客户端连接Redis服务器。因为当使用Jedis客户端连接Redis服务器的时候,每个线程都要拿自己创建的Jedis实例去连接Redis客户端,当有很多个线程的时候,不仅开销大需要反复的创建关闭一个Jedis连接,而且也是线程不安全的,一个线程通过Jedis实例更改Redis服务器中的数据之后会影响另一个线程;

但是如果使用Lettuce这个客户端连接Redis服务器的时候,就不会出现上面的情况,Lettuce底层使用的是Netty,当有多个线程都需要连接Redis服务器的时候,可以保证只创建一个Lettuce连接,使所有的线程共享这一个Lettuce连接,这样可以减少创建关闭一个Lettuce连接时候的开销;而且这种方式也是线程安全的,不会出现一个线程通过Lettuce更改Redis服务器中的数据之后而影响另一个线程的情况;

spring-boot-starter-data-redis有两种实现:lettuce 和 jedis 。然而默认是使用lettuce.

spring boot 2的spring-boot-starter-data-redis中,默认使用的是lettuce作为redis客户端,它与jedis的主要区别如下:

1.Jedis:
Jedis是同步的,不支持异步,Jedis客户端实例不是线程安全的,需要每个线程一个Jedis实例,所以一般通过连接池来使用Jedis.

优点:

提供了比较全面的 Redis 操作特性的 API
API 基本与 Redis 的指令一一对应,使用简单易理解
缺点:

同步阻塞 IO
不支持异步
线程不安全
如果不使用默认的lettuce,使用jedis的话,可以排除lettuce的依赖,手动加入jedis依赖,配置如下:

org.springframework.bootspring-boot-starter-data-redisio.lettucelettuce-core

redis.clientsjedis2.9.0

2.Lettuce:
Lettuce是基于Netty框架的事件驱动的Redis客户端,其方法调用是异步的,Lettuce的API也是线程安全的,所以多个线程可以操作单个Lettuce连接来完成各种操作,同时Lettuce也支持连接池.

优点:

线程安全
基于 Netty 框架的事件驱动的通信,可异步调用
适用于分布式缓存
缺点:

API 更抽象,学习使用成本高
pom:

org.springframework.bootspring-boot-starter-data-redis



org.apache.commonscommons-pool2

3. 正确配置

SpringBoot 2.0 以下 RedisConnectionFactory 通过properties 文件配置database 没有任何问题,但是通过yml 文件时,默认为0,修改为其他database 不生效,需要通过JedisConnectionFactory 重新设置一下。

@Bean(name="redisTemplate")public RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();//因为配置文件改为 yml以后,配置文件中的spring.redis.database 非0时无效,此处手动设置成配置文件中内容。int database = Integer.valueOf(environment.getProperty("spring.redis.database"));JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;jedisConnectionFactoryFactory.setDatabase(database);redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);RedisSerializer redisSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(redisSerializer);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);jackson2JsonRedisSerializer.setObjectMapper(om);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);RedisSerializer stringSerializer = new StringRedisSerializer();redisTemplate.setHashKeySerializer(stringSerializer);redisTemplate.setHashValueSerializer(stringSerializer);return redisTemplate;}

完整代码:
RedisCacheConfig

package com.wzw.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;@Configuration
@EnableCaching
public class RedisCacheConfig {@Resourceprivate Environment environment;@Beanpublic CacheManager cacheManager(RedisTemplate redisTemplate){RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);cacheManager.setDefaultExpiration(3600);//默认缓存1小时Map expiresMap=new HashMap();expiresMap.put("ibsLongTimeCache", 7*24*3600L);//长时间缓存区域   7天   缓存基本不会改变的数据expiresMap.put("ibsTempCache", 600L);//临时缓存区域  10分钟  改动基本稍多实时性要求不高的数据cacheManager.setExpires(expiresMap);return cacheManager;}@Bean(name="redisTemplate")public RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();//因为配置文件改为 yml以后,配置文件中的spring.redis.database 非0时无效,此处手动设置成配置文件中内容。int database = Integer.valueOf(environment.getProperty("spring.redis.database"));JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;jedisConnectionFactoryFactory.setDatabase(database);redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);RedisSerializer redisSerializer = new StringRedisSerializer();redisTemplate.setKeySerializer(redisSerializer);Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);jackson2JsonRedisSerializer.setObjectMapper(om);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);RedisSerializer stringSerializer = new StringRedisSerializer();redisTemplate.setHashKeySerializer(stringSerializer);redisTemplate.setHashValueSerializer(stringSerializer);return redisTemplate;}@Beanpublic KeyGenerator keyGenerator() {return new KeyGenerator() {@Overridepublic Object generate(Object target, Method method, Object... params) {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getName());sb.append(method.getName());for (Object obj : params) {sb.append(obj.toString());}return sb.toString();}};}}

RedisService

package com.wzw.service;import java.util.List;
import java.util.Map;public interface RedisService {/*** 批量删除对应的value** @param keys*/public void remove(final String... keys);/*** 批量删除key** @param pattern*/public void removePattern(final String pattern);/*** 删除对应的value** @param key*/public void remove(final String key);/*** 判断缓存中是否有对应的value** @param key* @return*/public boolean exists(final String key);/*** 读取缓存** @param key* @return*/public Object get(final String key);/*** 读取缓存** @param key* @return*/public Map getHashKey(final String key);public List getHashKey(final String key, List hashkeys);/*** 写入缓存** @param key* @param value* @return*/public boolean set(final String key, Object value);/*** 写入缓存* @param key* @param value* @return*/public boolean set(final String key, Object value, Integer expireTime);/*** 设置失效时间* @param key* @param expireTime* @return*/public boolean expire(final String key, Integer expireTime);
} 

RedisServiceImpl

package com.wzw.service.impl;import com.wzw.service.RedisService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;@Service("redisService")
public class RedisServiceImpl implements RedisService{@Autowired@Qualifier("redisTemplate")private RedisTemplate redisTemplate;/*** 批量删除对应的value** @param keys*/@Overridepublic void remove(final String... keys) {for (String key : keys) {remove(key);}}/*** 批量删除key** @param pattern*/@Overridepublic void removePattern(final String pattern) {Set keys = redisTemplate.keys(pattern);if (keys.size() > 0)redisTemplate.delete(keys);}/*** 删除对应的value** @param key*/@Overridepublic void remove(final String key) {if (exists(key)) {redisTemplate.delete(key);}}/*** 判断缓存中是否有对应的value** @param key* @return*/@Overridepublic boolean exists(final String key) {return redisTemplate.hasKey(key);}/*** 读取缓存** @param key* @return*/@Overridepublic Object get(final String key) {Object result = null;ValueOperations operations = redisTemplate.opsForValue();result = operations.get(key);return result;}/*** 读取缓存** @param key* @return*/@Overridepublic Map getHashKey(final String key) {Map objectMap = redisTemplate.opsForHash().entries(key);;return objectMap;}/*** 读取缓存** @param key* @return*/@Overridepublic List getHashKey(final String key, List hashkeys) {List objectMap = redisTemplate.opsForHash().multiGet(key,hashkeys);return objectMap;}/*** 写入缓存** @param key* @param value* @return*/@Overridepublic boolean set(final String key, Object value) {boolean result = false;try {ValueOperations operations = redisTemplate.opsForValue();operations.set(key, value);result = true;} catch (Exception e) {e.printStackTrace();}return result;}/*** 写入缓存* @param key* @param value* @return*/@Overridepublic boolean set(final String key, Object value, Integer expireTime) {boolean result = false;try {ValueOperations operations = redisTemplate.opsForValue();operations.set(key, value);redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch (Exception e) {e.printStackTrace();}return result;}/*** 设置失效时间* @param key* @param expireTime* @return* @author Jone* create date:2018年2月27日*/@Overridepublic boolean expire(final String key, Integer expireTime) {boolean result = false;try {redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);result = true;} catch (Exception e) {e.printStackTrace();}return result;}
} 

yml

spring:redis:database: 1host: 192.168.1.xxxpassword: asdffdsapool:max-active: 8max-idle: 8max-wait: -1min-idle: 0port: 6380timeout: 60000

4.小结

  1. spirngboot 2.0 以下 redis 采用jedis,jedis 同步、不支持异步、线程不安全; 2.0 以后采用 Lettuce,底层采用redis 支持异步、线程安全 。
  2. springboot 2.0 一下采用properties 配置 redis 的database 没有任何问题,当采用yml 文件配置时,默认时0 ,通过配置文件修改也不生效,需要在redisConfig 里通过JedisConnectionFactory 重新指定一下。
int database = Integer.valueOf(environment.getProperty("spring.redis.database"));JedisConnectionFactory jedisConnectionFactoryFactory = (JedisConnectionFactory)factory;jedisConnectionFactoryFactory.setDatabase(database);redisTemplate.setConnectionFactory(jedisConnectionFactoryFactory);

相关内容

热门资讯

华凯易佰涨8.81%,成交额7... 1月12日,华凯易佰涨8.81%,成交额7.07亿元,换手率15.62%,总市值53.46亿元。异动...
美康生物涨0.88%,成交额4... 1月12日,美康生物涨0.88%,成交额4951.34万元,换手率1.65%,总市值39.51亿元。...
康弘药业涨0.17%,成交额5... 1月12日,康弘药业涨0.17%,成交额5.34亿元,换手率2.26%,总市值316.84亿元。异动...
国信证券涨0.52%,成交额6... 1月12日,国信证券涨0.52%,成交额6.27亿元,换手率0.49%,总市值1384.68亿元。异...
开立医疗涨1.66%,成交额1... 1月12日,开立医疗涨1.66%,成交额1.58亿元,换手率1.25%,总市值127.43亿元。异动...