关于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 如下

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>

		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

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 <p>TODO</p>
 * @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<Object, Object> 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<Object, Object> 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 <p>TODO</p>
 * @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<Object, Object> redisTemplate;




   
    public void reset(String key, Long seconds){
        stringRedisTemplate.expire(key, seconds, TimeUnit.SECONDS);
    }

    /**
     * 获取匹配的key
     * @param pattern
     * @return Set<String>
     */
    public Set<String> 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<String, String> 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<String> keys = stringRedisTemplate.keys(key);
            Iterator<String> 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<Object, Object> 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> 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<String> range(String key, int start, int end) {
        List<String> 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的区别!

猜你喜欢

转载自blog.csdn.net/jakemanse/article/details/83832307