SpringCache整合redis

公司最近打算做高可用,分布式,集群之类的,原有的缓存架构是springcache整合的ehcache,ehcache做分布式并不适合,因此改为了redis做分布式缓存,由于原来项目中就引用了redis,用来存储一些业务数据,且默认链接的0数据库。现在打算把缓存数据和业务数据区分开,所以一个项目进行了多连接,缓存数据存在了2数据库,(目前有个问题是,以后做集群的话,会只复制0数据库内容,相应的配置还需要修改)

pom.xml

在这里插入图片描述

appolication.yml
spring:
	cache:
		type: redis

redis:
	# cluster: 
	# (普通集群, 不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
	# max-redirects:
	# (普通集群, 不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
	# nodes:
	# Redis数据库索引(默认为0)
	database: 0
	# Redis服务器地址
	host:  xxx
	# Redis服务器连接端口
	port: 6379
	# Redis服: 务器连接密码(默认为空)
	password: xxx
	#连接超时时间(毫秒)
	timeout: 1000
	jedis:
		pool :
			#连接池最大连接数(使用负值表示没有限制)
			max-active: 100
			#连接池最大阻塞等待时间(使用负值表示没有限制)
			max-wait: 1000
			#连接池中的最大空闲连接
			max- idle: 50
			#连接池中的最小空闲连接
			min-idle: 5

redisTwo:
	# cluster: 
	# (普通集群, 不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
	# max-redirects:
	# (普通集群, 不使用则不用开启)以逗号分隔的“主机:端口”对列表进行引导。
	# nodes:
	# Redis数据库索引(默认为0)
	database: 2
	# Redis服务器地址
	host:  xxx
	# Redis服务器连接端口
	port: 6379
	# Redis服: 务器连接密码(默认为空)
	password: xxx
	#连接超时时间(毫秒)
	timeout: 1000
	jedis:
		pool :
			#连接池最大连接数(使用负值表示没有限制)
			max-active: 100
			#连接池最大阻塞等待时间(使用负值表示没有限制)
			max-wait: 1000
			#连接池中的最大空闲连接
			max- idle: 50
			#连接池中的最小空闲连接
			min-idle: 5	
配置文件代码:
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
	@Bean(name ="redisConnectionFactoryTwo")
	public RedisConnectionFactory redisConnectionFactoryTwo (
		@Value("${spring.redisTwo.host}") String hostName,
		@Value("${spring.redisTwo.database}") int database,
		@Value("${spring.redisTwo.port}") int port,
		@Value("${spring.redisTwo.password}") String password,
		@Value("${spring.redisTwo.timeout}") int timeout,
		@Value("${spring.redisTwo.jedis.pool.max-active}") int maxActive,
		@Value("${spring.redisTwo.jedis.pool.max-wait}") long maxWait ,
		@Value("${spring.redisTwo.jedis.pool.max-idle}") int maxIdle,
		@Value("${spring.redisTwo.jedis.pool.min-idle}") int minIdle) {
		return getRedisConnectionFactory(hostName, database, port,password, timeout, maxActive, maxWait,maxIdle, minIdle);
	}
	
	
	@Bean(name = "redisTemplateTwo")
	public RedisTemplate<String, Serializable> redisTemplateTwo(RedisConnectionFactory  redisConnectionFactoryTwo) {
	    RedisTemplate<String, Serializable> redisTemplate = new RedisTemplate<>();
	    //redisT emplate.setKeySerializer(new StringRedisSerializer());
	    //redisTemplate.setVa lueSerializer(new Jackson2JsonRedisSerializer<>(0bject.class));
	    redisTemplate.setConnectionFactory( redisConnectionFactoryTwo);
	    return redisTemplate;
	}    
	
	@Bean(name ="cacheManagerTwo" )
	public CacheManager cacheManagerTwo(RedisTemplate redisTemplateTwo) {
		Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
		configMap.put("user_service", getCacheConfigurationWithTtl(redisTemplateTwo, 60));
		return RedisCacheManager.RedisCacheManagerBuilder
			.fromConnectionFactory(redisT emplateTwo.getConnectionFactory())
			.cacheDefaults(getCacheConfigurationWithTtl(redisTemplateTwo, 10))
			.withInitialCacheConfigurations(configMap)
			.transactionAware()
			.build();
	}
	
	@Bean(name = "redisConnectionFactory" )
	public RedisConnectionFactory redisConnectionFactory (
										@Value("${spring.redis.host}") String hostName,
										@Value("${spring.redis.database}") int database,
										@Value("${spring.redis.port}") int port,
										@Value("${spring.redis.password}") String password,
										@Value("${spring.redis.timeout}") int timeout
										@Value("${spring.redis.jedis.pool.max-active}") int maxActive,
										@Value("${spring.redis.jedis.pool.max-wait}") long maxWait,
										@Value("${spring.redis.jedis.pool.max-idle}") int maxIdle,
										@Value("${spring.redis.jedis.pool.min-idle}") int minIdle) 
		return getRedisConnectionFactory(hostName, database, port, password, timeout, maxActive, maxWait, maxIdle, minIdle);
	}	
	@Primary
	@Bean(name = "cacheManager")
	public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
		//全局redis缓存过期时间
		RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig(); 
		return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory), redisCacheConfiguration);
	}	
	@Override
	public KeyGenerator keyGenerator() {
		return (clazz, method, args) -> {
			StringBuilder sb = new StringBuilder();
			sb.append(clazz.getClass().getName()).append("#" );
			sb.append(method.getName()).append("(");
			for (0bject obj : args) {
				sb.append(obj.toString()).append(",");
			}
			sb.deleteCharAt(sb.length()-1);
			sb.append(")");
			return sb.toString(); 
		};
	}	
	private RedisCacheConfiguration getCacheConfigurationWithTtl(RedisTemplate<String, 0bject> template, long minutes) {
		return RedisCacheConfiguration
			.defaul tCacheConfig()
			//设置key为String
			.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template. getStringSerializer()))
			//设置value
			.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template. getValueSerializer()))
			//不缓存值为null的值
			//.disableCachingNullValues()
			//缓存数据保存时间
			.entryTtl(Duration.ofMinutes(minutes));
}	
	private RedisConnectionFactory getRedisConnectionFactory(String hostName, int database, int port, String password, int timeout,int maxActive long maxWait, int maxIdle, int minIdle) {
		JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
		jedisConnectionFactory.setHostlarme(hostName);
		jedisConnectionFactory.setDatabase(database);
		jedisConnectionFactory.setPort(port);
		jedisConnectionFactory.setPassword(password);
		jedisConnectionFactory.setTieouE(timeout);
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxTotal(maxActive) ;
		jedisPoolConfig.setMaxWaitMillis(maxWait);
		jedisPoolConfig.setMaxIdle(maxIdle) ;
		jedisPoolConfig.setMinIdle(minIdle);
		jedisConnectionFactory.setRoelConfig(jedisPoolConfig);
		return jedisConnectionFactory;
	}
}		

关于业务数据的redisdao的封装:

@Repository
public class RedisDao {
	private final StringRedisTemplate redisTemplate;
	private final static String ASTERISK = "*";
	@Autowired
	public RedisDao(StringRedisTemplate redisTemplate) {
		this.redisTemplate = redisTemplate; 
	}
	/**
	*根据Key键进行自增操作
	*@param key键
	*@return key键对应的自增后的值
	*/
	public long incr(String key) {
		ValueOperations<String, String> ops = redisTemplate.opsForValue();
		Long value = ops.increment(key, delta: 1) ;
		Assert.notNull(value, message: "[ERR276676010] Failed to incr"+ key);
		return value;
	}
	/**
	*根据Key键进行自诚操作
	*@param key键
	*@return key键 对应的自减后的值
	**/
	public long decr(String key) {
		ValueOperations<String, String> ops = redisTemplate.opsForValue();
		Long value = ops.increment(key, delta: -1) ;
		Assert.notNull(value, message: "[ERR277653053] Failed to decr"+ key);
		return value;
	}
	/**
	*set存储键值对
	*@param key键
	*@param value值
	*return key键对应的原值
	**/
	public void set(String key, String value) (
	try{
		ValueOperations<String, String> ops = redisTemplate.opsForValue( ) ;
		ops.set(key,value);
		}catch(Exception e){
			e.printStackTrace();
		}
	}
	/**
	* set存储键值对
	*@param key键
	*@param value值
	*@param time 存储时间
	*@return  key键对应的原值
	*/
	public void set(String key , String value, long time) {
	try{
		ValueOperations<String, String> ops = redisTemplate.opsForValue( );
		ops.set (key,value,time, TimeUnit.SECONDS);
		}catch(Exception e){
			e. printStackTrace();
		}
	}

	/**
	*根据key键获取value值
	*@param keyb 键
	*@return key键 对应的值
	**/
	public String get(String key) { return key==nul1?null:redisTemplate.opsForValue().get(key); }
	/** 
	*根据单个key键删除
	*@param key 键
	**/
	public void del(String key) { 
		if (key!=nu1l){
			redisTemplate.delete(key);
		}
	}
	/* *
	*根据多个key键批量
	*@param keys 键值组
	**/
	public void del(String keys) {
		if(keys!=null){
			redisTemplate.delete(Arrays.asList(keys));
		}
	}	
	/**
	*指定缓存失效时间
	*@param key 键
	*@paramtime 时间(秒)
	*@return是否设 置成功
	*/
	public boolean expire(String key, long time){
		try{
			if(time>0){
				redisTemplate.expire(key , time , TimeUnit.SECONDS) ; 
			}
			return true;
		}catch(Exception e){
			e.printStackTrace();
			return false;
		}
	}

	/**
	根据key获取过期时间
	@param key键
	@return时间 (秒),返回e代表永久有
	*/
	public long getExpire(String key) { return redisTemplate.getExpire(key, TimeUnit.SECONDS); }
	
	/**
	*判断key是否存在
	@param key
	键
	@return true存在, false不存
	**/
	public boolean hasKey(String key){
		try{
			return redis Template.hasKey(key);
		}catch (Exception e){
			e. printStackTrace() ;
			return false;
		}
	}

	/** 
	*hash表get操作
	@param key  hash表的key
	@param f ields hash表中的字段名
	@return查出结果
	**/
	public List<String> hmget(String key, String fields) {
		HashOperations<String, String, String> ops = redisTemplate.opsForHash();
		return ops.multiGet(key, Arrays.asList(fields));
	}
	/**
	*$查询list中start  stop范围内的值
	*@param key   list key
	*@param start  开始索引
	* @param stop  结束索引
	*@return查出的结果
	**/
	public List<string> lrange(String key, int start, int stop) {
		return redisTemplate.opsForList().range(key, start, stop);
	}
	/**
	*数据Left push到list
	*@param key list key 
	*@param values  push值列表通配符删除
	*@return插入的值个数
	*/
	public long lpush(String key, String values) {
		Long result = redisTemplate.opsForList().leftPushAll(key, values);
		return result != null ? result : 0;
	}
	/**
	*清理批次执行redis缓存数据
	*/
	protected void clearOrderData(String key){
		Set<String> keys = this.redisTemplate.keys("*" +key + "*"); 
		redisTemplate.delete(keys);
	}
	/**
	*通配符删除 
	* @param wildcardKey 用于删除的key 通配符,例如: EV_ *,*EV*
	*/
	public long wildcardDel(@NotNu1l String wildcardKey) {
		Set<String> keys = this.redisTemplate.keys(wildcardKey);
		Long result = redisTemplate.delete(keys);
	return result != null ? result : 0;
	}
	/** 
	通配符删除
	@param wildcardKey 用于删除的key 通配符,例如: EV_ *,*EV*
	**/
	public int wildcardCount(@NotNu1l String wildcardKey) {
		intlimit=1000;
		Set<String> keys = this.scanValues(wildcardRight(wildcardKey), limit);
		return keys.size( );
	}
	/**
	通配符删除
	@param wildcardKey 用于删除的key 通配符,例如: EV_ *,*EV* 
	**/
	public Set<String> gueryKeys (@NotNu1l String wildcardKey){
		intlimit=1000;
		return this.scanKeys(wiLdcardRight(wildcardKey), limit);
	}
	/**
	*左右增加通配符
	*/
	public static String wildcard(String str) { return ASTERISK + str + ASTERISK; }
	/**
	*左边增加通配符
	*/
	public static String wildcardLeft(String str) { return ASTERISK + str; }
	/**
	*右边增加通配符
	*/
	public static String wildcardRight(String str) { return str + ASTERISK;}
	/**
	自定义redis scan操作获取vaLue集合
	* @param pattern
	*@paramL imit
	*@return {@Link Cursor< String>}
	**/
	private Set<String> scanValues(String pattern, int limit) {
		return (Set<String>)redisTemplate.execute(new.RedisCallback<Set<String>>() {
			@Override
			public Set<String> doInRedis( Redi sConnection connection) throws DataAccessException {
				Set<String> valueSet = new HashSet<>( ) ;
				try (Cursor<byte[]> cursor = connection.scan(new ScanOptions.ScanOptionsBuilder()
						.match(pattern).count(limit).build())) {
					while(cursor.hasNext()) {
						byte[] bytes = connection.get(cursor.next());
						String value = String.valueOf(redisTemplate.getValueSerializer().deserialize(bytes));
						valueSet.add(value);
					}
				} catch (IOException e) {
					log.error(String.format("get cursor close {%s}", e));
				}
				return valueSet ; 
			}
		});
	}
	/**
	*自定义redis scan操作 获取key集合
	@param pattern
	@paramL imit
	@return
	{@link Cursor<String>}
	**/
	private Set<String> scanKeys(String pattern, int limit) {
		return redisTemplate.execute((RedisCallback<Set<String>>) connection -> {
			Set<String> keySet = new HashSet<>();
			ScanOptions options = ScanOptions.scanOptions().match(pattern).count(limit).build();
			Cursor<byte[]> cursor = connection.scan(options ) ;
			while (cursor.hasNext()){
				keySet.add(new String(cursor.next()));
			}
			return keySet;
		});
	}
}	
问题1. 多数据源连接配置

上述配置文件中,redisConnectionFactory和redisConnectionFactoryTwo两个连接配置,同样也创建了两个cacheManager,需要注意的地方是要默认指定一个一个cacheManager,直接加上@Primary注解。在使用缓存注解的时候,需要加上cacheManger,指定要使用的cacheManager,否则默认加了@Primary注解的cacheManager。如下图所示。
在这里插入图片描述
关于springCache可以参考这篇文章

问题2. 指定缓存有效时间

RedisCacheManager类中有一个withInitialCacheConfigurations这里可以传入自定义配置,注意这个方法的参数是一个map类型,key为缓存名称对应缓存注解中的cacheNames,value 为RedisCacheConfiguration 。configMap.put(“user_service”, getCacheConfigurationWithTtl(redisTemplateTwo, 60));
再提供一个getCacheConfigurationWithTtl 方法 把有效期作为参数传进去

	private RedisCacheConfiguration getCacheConfigurationWithTtl(RedisTemplate<String, 0bject> template, long minutes) {
		return RedisCacheConfiguration
			.defaul tCacheConfig()
			//设置key为String
			.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(template. getStringSerializer()))
			//设置value
			.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(template. getValueSerializer()))
			//不缓存值为null的值
			//.disableCachingNullValues()
			//缓存数据保存时间
			.entryTtl(Duration.ofMinutes(minutes));
}
问题3. 允许缓存值为null的数据

RedisCacheConfiguration该配置类中,封装了disableCachingNullValues()方法,就是给配置类中的cacheNullValues赋值为false;该类的默认构造方法中是给cacheNullValues赋值为true;如果不想缓存null的值,可以在配置创建的时候给配置类调用disableCachingNullValues()方法。
在这里插入图片描述
除了配置上面添加不允许存储值为null的配置以外,还可以在缓存注解上添加 unless= “#result == null”
在这里插入图片描述

问题4. 序列化失败问题

缓存的时候采取的是jdk自带的序列化,因为要缓存的对象由于业务需要里面有很多嵌套对象,如果用jackson序列化会OOM,采用jdk序列化就要注意类中的属性(除去静态变量)是否都实现了序列化接口,或者添加了transient关键字。
在这里插入图片描述

问题5. 反序列化失败问题

堆栈异常信息:
在这里插入图片描述
在这里插入图片描述
项目中是因为服务器上配置错了,,存储到数据库2和0的两种序列化方式不同,0的用的jackson,2用的jdk自带的序列化。当时以为是由于提的common包中 的对象版本不一致,,且对象的序列化id没有声明,所以先是把对象的序列化id都加上了
在这里插入图片描述
idea中配置提示实现序列化接口的类,声明序列化id

备注一张图:
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值