关于Spring Boot + Redis 的 no such key 异常

 

在使用Spring Boot2.x 与 Redis 集成时,遇到如下异常,解决这个问题,让我费了些工夫,走了些弯路。

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR no such key

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR no such key
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:54)
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:52)\
at org.springframework.data.redis.connection.lettuce.LettuceExceptionConverter.convert(LettuceExceptionConverter.java:41)
at org.springframework.data.redis.PassThroughExceptionTranslationStrategy.translate(PassThroughExceptionTranslationStrategy.java:44)
at org.springframework.data.redis.FallbackExceptionTranslationStrategy.translate(FallbackExceptionTranslationStrategy.java:42)
at org.springframework.data.redis.connection.lettuce.LettuceConnection.convertLettuceAccessException(LettuceConnection.java:257)
at org.springframework.data.redis.connection.lettuce.LettuceKeyCommands.convertLettuceAccessException(LettuceKeyCommands.java:650)
at org.springframework.data.redis.connection.lettuce.LettuceKeyCommands.rename(LettuceKeyCommands.java:249)
at org.springframework.data.redis.connection.lettuce.LettuceClusterKeyCommands.rename(LettuceClusterKeyCommands.java:119)
at org.springframework.data.redis.connection.DefaultedRedisConnection.rename(DefaultedRedisConnection.java:96)
at org.springframework.data.redis.core.RedisTemplate.lambda$rename$13(RedisTemplate.java:889)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:224)
at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:184)
at org.springframework.data.redis.core.RedisTemplate.rename(RedisTemplate.java:888)
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveChangeSessionId(RedisOperationsSessionRepository.java:815)
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:772)
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:649)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:384)
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:234)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:197)
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryResponseWrapper.onResponseCommitted(SessionRepositoryFilter.java:185)
at org.springframework.session.web.http.OnCommittedResponseWrapper.doOnResponseCommitted(OnCommittedResponseWrapper.java:227)
at org.springframework.session.web.http.OnCommittedResponseWrapper.access$000(OnCommittedResponseWrapper.java:38)
at org.springframework.session.web.http.OnCommittedResponseWrapper$SaveContextServletOutputStream.flush(OnCommittedResponseWrapper.java:494)
at org.springframework.security.web.util.OnCommittedResponseWrapper$SaveContextServletOutputStream.flush(OnCommittedResponseWrapper.java:514)
at com.fasterxml.jackson.core.json.UTF8JsonGenerator.flush(UTF8JsonGenerator.java:1100)
at com.fasterxml.jackson.databind.ObjectWriter.writeValue(ObjectWriter.java:915)
at org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter.writeInternal(AbstractJackson2HttpMessageConverter.java:286)
at org.springframework.http.converter.AbstractGenericHttpMessageConverter.write(AbstractGenericHttpMessageConverter.java:102)
at org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:271)
at org.springframework.web.servlet.mvc.method.annotation.HttpEntityMethodProcessor.handleReturnValue(HttpEntityMethodProcessor.java:218)
at org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:119)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:870)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:776)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at ....

 

 除了网上所说的那些解决方法外,试过了都没有解决我的现实问题。

此类的解决方案大致有如下几种方式:

1.添加 Spring boot  Redis   和  Spring Session 支持, pom.xml 如下


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

		
			org.springframework.session
			spring-session-data-redis
		

 

2.注意版本问题,我使用的Spring Boot 2.0   ,要使用与之匹配的 Redis 版本 。

3.Spring 提供 的StringRedisTemplate和RedisTempate的两个类的使用 ,如下错误代码也会报 no such key的异常,根本不管spring session什么事。

public boolean setString(String key,String value ){
        try {
            stringRedisTemplate.opsForList().set(key,value);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

所以在网上Copy工具类时,也不可完全放心的用,还是得仔细检查一翻较好!

以下配置文件及工具类仅供参考:

1.application.yml

spring:
  # REDIS(RedisProperties)
  # (普通集群,不使用则不用开启)在群集中执行命令时要遵循的最大重定向数目。
  redis:
    database: 0
    host: 127.0.0.1
    port: 6379
    timeout: 60000
    password:
    ssl: false
    pool:

      max-active: 8
      max-idle: 1
      max-wait: -1
      min-idle: 0
      # Redis服务器端口。
      #spring.redis.port=6379
      # (哨兵模式,不使用则不用开启)Redis服务器的名称。
      # spring.redis.sentinel.master=
      # (哨兵模式,不使用则不用开启)主机:端口对的逗号分隔列表。
      # spring.redis.sentinel.nodes=
      # 以毫秒为单位的连接超时。

2.RedisConfig.java



import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @ClassName RedisConfig
 * @Description 

TODO

* @Author Jakemanse * @Date 2018/11/7 16:50 * @Version 1.0 **/ @Configuration @EnableCaching public class RedisConfig { /** * 从application.yml取得redis的host地址. */ @Value("${spring.redis.host}") private String redisHost; /** * 从application.yml取得redis的端口号. */ @Value("${spring.redis.port}") private Integer redisPort; /** * Jedis 连接工厂. * * @return 配置好的Jedis连接工厂 */ @Bean public JedisConnectionFactory jedisConnectionFactory() { RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration(redisHost, redisPort); return new JedisConnectionFactory(configuration); } @Bean(name = "redisTemplate") public RedisTemplate redisTemplate(RedisConnectionFactory factory) { /* * Redis 序列化器. * * RedisTemplate 默认的系列化类是 JdkSerializationRedisSerializer,用JdkSerializationRedisSerializer序列化的话, * 被序列化的对象必须实现Serializable接口。在存储内容时,除了属性的内容外还存了其它内容在里面,总长度长,且不容易阅读。 * * Jackson2JsonRedisSerializer 和 GenericJackson2JsonRedisSerializer,两者都能系列化成 json, * 但是后者会在 json 中加入 @class 属性,类的全路径包名,方便反系列化。前者如果存放了 List 则在反系列化的时候如果没指定 * TypeReference 则会报错 java.util.LinkedHashMap cannot be cast to */ RedisSerializer genericJackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); RedisSerializer stringRedisSerializer = new StringRedisSerializer(); // 定义RedisTemplate,并设置连接工程 RedisTemplate redisTemplate = new RedisTemplate<>(); // key 的序列化采用 StringRedisSerializer redisTemplate.setKeySerializer(stringRedisSerializer); redisTemplate.setHashKeySerializer(stringRedisSerializer); // value 值的序列化采用 GenericJackson2JsonRedisSerializer redisTemplate.setValueSerializer(genericJackson2JsonRedisSerializer); redisTemplate.setHashValueSerializer(genericJackson2JsonRedisSerializer); // 设置连接工厂 redisTemplate.setConnectionFactory(factory); return redisTemplate; } @Bean public CacheManager initRedisCacheManager(RedisConnectionFactory factory) { RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager .RedisCacheManagerBuilder.fromConnectionFactory(factory); return builder.build(); } }

 

2.RedisUtils.java




import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

import java.text.MessageFormat;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName RedisUtils
 * @Description 

TODO

* @Author Jakemanse * @Date 2018/11/7 13:55 * @Version 1.0 **/ @Component public class RedisUtils { private static final Logger logger = Logger.getLogger(RedisUtils.class); /** 字符串缓存模板 */ @Autowired private StringRedisTemplate stringRedisTemplate; /** 对象,集合缓存模板 */ @Autowired private RedisTemplate redisTemplate; public void reset(String key, Long seconds){ stringRedisTemplate.expire(key, seconds, TimeUnit.SECONDS); } /** * 获取匹配的key * @param pattern * @return Set */ public Set keys(String pattern){ return stringRedisTemplate.keys(pattern); } /** * 批量删除keys * @param pattern */ public void delKeys(String pattern){ redisTemplate.delete(stringRedisTemplate.keys(pattern)); } /** * 添加Set集合 * @param key * @param set */ public void addSet(String key ,Set set){ try{ redisTemplate.opsForSet().add(key, set); }catch(Exception ex){ ex.printStackTrace(); } } /** * 获取Set集合 * @param key * @return */ public Set getSet(String key){ try{ return redisTemplate.opsForSet().members(key); }catch(Exception ex){ ex.printStackTrace(); } return null ; } public void addString(String key , String value,Long seconds){ try { // 字符串redis 存储 ValueOperations valOps = stringRedisTemplate.opsForValue(); if(seconds!=null){ valOps.set(key, value,seconds,TimeUnit.SECONDS); }else{ valOps.set(key, value); } } catch (Exception e) { logger.warn(spellString("addString {0}={1},{2}", key,value,seconds),e); } } public String getString(String key) { String result = ""; try { result = stringRedisTemplate.opsForValue().get(key); } catch (Exception e) { logger.warn(spellString("getString {0}", key), e); } return result; } public void delString(String key) { try { stringRedisTemplate.delete(key); } catch (Exception e) { logger.warn(spellString("delString {0}", key),e); } } public void delAllString(String key) { if(key==null || "".equals(key)){ return; } try { if (!key.endsWith("*")) { key += "*"; } Set keys = stringRedisTemplate.keys(key); Iterator it = keys.iterator(); while (it.hasNext()) { String singleKey = it.next(); delString(singleKey); } } catch (Exception e) { logger.warn(spellString("delString {0}", key), e); } } /** * @see add 缓存数据 * @param key * @param value * @param time */ public void addObj(String key ,Object obj, Long seconds){ try { //对象redis存储 ValueOperations objOps = redisTemplate.opsForValue(); if(seconds!=null){ objOps.set(key, obj, seconds, TimeUnit.SECONDS); }else{ objOps.set(key, obj); } } catch (Exception e) { logger.warn(spellString("addObj {0}={1},{2}", key,obj,seconds),e); } } /** * @see get 缓存数据 * @param key * @return Object */ public Object getObject(String key) { Object object = null; try { object = redisTemplate.opsForValue().get(key); } catch (Exception e) { logger.warn(spellString("getObj {0}", key), e); } return object; } /** * @see get 缓存数据 * @param key * @return Object */ @SuppressWarnings({ "unchecked"}) public T getObj(String key, T t) { Object o = null; try { o = redisTemplate.opsForValue().get(key); } catch (Exception e) { logger.warn(spellString("getObj {0}->{1}", key, t), e); } return o == null ? null : (T) o; } public void expire(String key,long second){ try { stringRedisTemplate.expire(key, second, TimeUnit.SECONDS); } catch (Exception e) { logger.warn(spellString("expire {0}={1}", key, second),e); } } /** * @see del 缓存数据 * @param key */ public void delObj(String key) { try { redisTemplate.delete(key); } catch (Exception e) { logger.warn(spellString("delObj {0}", key),e); } } /** * 压栈 * * @param key * @param value * @return */ public Long push(String key, String value) { Long result = 0l; try { result = stringRedisTemplate.opsForList().leftPush(key, value); } catch (Exception e) { logger.warn(spellString("push {0}={1}", key,value),e); } return result; } /** * 出栈 * * @param key * @return */ public String pop(String key) { String popResult = ""; try { popResult = stringRedisTemplate.opsForList().leftPop(key); } catch (Exception e) { logger.warn(spellString("pop {0}", key), e); } return popResult; } /** * 入队 * * @param key * @param value * @return */ public Long in(String key, String value) { Long inResult = 0l; try { inResult = stringRedisTemplate.opsForList().rightPush(key, value); } catch (Exception e) { logger.warn(spellString("in {0}={1}", key, value), e); } return inResult; } /** * 出队 * * @param key * @return */ public String out(String key) { String outResult = ""; try { outResult = stringRedisTemplate.opsForList().leftPop(key); } catch (Exception e) { logger.warn(spellString("out {0}", key),e); } return outResult; } /** * 栈/队列长 * * @param key * @return */ public Long length(String key) { Long lengthResult = 0l; try { lengthResult = stringRedisTemplate.opsForList().size(key); } catch (Exception e) { logger.warn(spellString("length {0}", key), e); } return lengthResult; } /** * 范围检索 * * @param key * @param start * @param end * @return */ public List range(String key, int start, int end) { List rangeResult = null; try { rangeResult = stringRedisTemplate.opsForList().range(key, start, end); } catch (Exception e) { logger.warn(spellString("range {0},{1}-{2}", key, start, end), e); } return rangeResult; } /** * 移除 * * @param key * @param i * @param value */ public void remove(String key, long i, String value) { try { stringRedisTemplate.opsForList().remove(key, i, value); } catch (Exception e) { logger.warn(spellString("remove {0}={1},{2}", key,value,i),e); } } /** * 检索 * * @param key * @param index * @return */ public String index(String key, long index) { String indexResult = ""; try { indexResult = stringRedisTemplate.opsForList().index(key, index); } catch (Exception e) { logger.warn(spellString("index {0}", key), e); } return indexResult; } /** * 置值 * * @param key * @param index * @param value */ public void setObject(String key, Object value,long index) { try { redisTemplate.opsForValue().set(key,value,index); } catch (Exception e) { logger.warn(spellString("set {0}={1},{2}", key,value,index),e); } } public boolean setString(String key,String value ){ try { stringRedisTemplate.opsForValue().set(key,value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 裁剪 * * @param key * @param start * @param end */ public void trim(String key, long start, int end) { try { stringRedisTemplate.opsForList().trim(key, start, end); } catch (Exception e) { logger.warn(spellString("trim {0},{1}-{2}", key,start,end),e); } } /** * 方法说明: 原子性自增 * @param key 自增的key * @param value 每次自增的值 * @time: 2017年3月9日 下午4:28:21 * @return: Long */ public Long incr(String key, long value) { Long incrResult = 0l; try { incrResult = stringRedisTemplate.opsForValue().increment(key, value); } catch (Exception e) { logger.warn(spellString("incr {0}={1}", key, value), e); } return incrResult; } /** * 拼异常内容 * @param errStr * @param arguments * @return */ private String spellString(String errStr,Object ... arguments){ return MessageFormat.format(errStr,arguments); } }

 

使用工具类时,要特别注意,StringRedisTemplate和RedisTemplate的区别!

 

 

 

 

 

 

你可能感兴趣的:(Redis,Java,Spring,Boot,Spring)