"Could not return the resource to the pool" Spring 和 Redis 出错

"Could not return the resource to the pool" error with Spring and Redis

我试图用 Spring 和基于 XML 的配置来实现 Redis。我的配置文件如下:

<!-- declare Redis Cache Manager -->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
      c:template-ref="redisTemplate" />

<bean id="jedisConnectionFactory"
      class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
      p:host-name="172.17.0.2" p:use-pool="true" />

<!-- redis template definition -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
      p:connection-factory-ref="jedisConnectionFactory" />

我的缓存定义如下:

@Cacheable(value = "my-cache-key", key = "{#argSomeObj, #argSomeString, #argSomeInt}")
public MyAwesomeDTO getSomeResult(SomeObject argSomeObj, String argSomeString, int argSomeInt, Request argRequest)
{
    // this part calculates and returns some values regarding to given parameters
    return awesomeDTO;
}

当我第一次点击这个功能时,我得到了如下所述的错误。当我第二次尝试时,它可以正常工作并存储在 Redis 中。

redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
    at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:103)
    at redis.clients.jedis.JedisPool.returnBrokenResource(JedisPool.java:239)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:255)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:16)
    at org.springframework.data.redis.connection.jedis.JedisConnection.close(JedisConnection.java:257)
    at org.springframework.data.redis.core.RedisConnectionUtils.releaseConnection(RedisConnectionUtils.java:206)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:205)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:153)
    at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:140)
    at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:125)
    at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:82)
    at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:651)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:358)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)

有人知道我该如何解决这个错误吗?

这是完整的堆栈跟踪:

01-30 12:34:35 [Thread@12:34:35.259:https://www.vtokmak.office.com:8443/some-url] ERROR com.tokmak.frontend.MyApplication - A 500 error (redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool) occured on URL [https://www.vtokmak.office.com:8443/some-url], userAgent [Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36]
redis.clients.jedis.exceptions.JedisException: Could not return the resource to the pool
    at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:103)
    at redis.clients.jedis.JedisPool.returnBrokenResource(JedisPool.java:239)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:255)
    at redis.clients.jedis.JedisPool.returnResource(JedisPool.java:16)
    at org.springframework.data.redis.connection.jedis.JedisConnection.close(JedisConnection.java:257)
    at org.springframework.data.redis.core.RedisConnectionUtils.releaseConnection(RedisConnectionUtils.java:206)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:205)
    at org.springframework.data.redis.core.RedisTemplate.execute(RedisTemplate.java:153)
    at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:140)
    at org.springframework.data.redis.cache.RedisCache.put(RedisCache.java:125)
    at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:82)
    at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:651)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:358)
    at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:299)
    at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:644)
    at com.tokmak.frontend2.service.misc.world.WorldService$$EnhancerBySpringCGLIB$a70244e.getWorld(<generated>)
    at com.tokmak.frontend2.controller.world.AbstractWorldController.handle(AbstractWorldController.java:69)
    at com.tokmak.frontend.Router.getView(Router.java:1069)
    at com.tokmak.frontend.Router.routeRequest(Router.java:320)
    at com.tokmak.webframework.RouterServlet.doRequestProcessingUnchecked(RouterServlet.java:145)
    at com.tokmak.webframework.RouterServlet.doRequestProcessing(RouterServlet.java:93)
    at com.tokmak.webframework.RouterServlet.doGet(RouterServlet.java:68)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:620)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:170)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:98)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:315)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.IllegalStateException: Invalidated object not currently part of this pool
    at org.apache.commons.pool2.impl.GenericObjectPool.invalidateObject(GenericObjectPool.java:640)
    at redis.clients.util.Pool.returnBrokenResourceObject(Pool.java:101)
    ... 44 more

// 01-30 12:34:35 [Thread@12:34:35.259:https://www.vtokmak.office.com:8443/some-url] WARN  com.tokmak.webframework.RouterServlet - Exception [/some-url], message: "Could not return the resource to the pool"]
01-30 12:34:35 [Thread@12:34:35.259:https://www.vtokmak.office.com:8443/some-url] WARN  com.tokmak.frontend.Router - I have to make use of static fail over [http://192.168.3.14/]...
01-30 12:34:35 [Thread@12:34:35.259:https://www.vtokmak.office.com:8443/some-url] WARN  com.tokmak.frontend.Router - ... but since there is a static fail-over URL present in config, I try to load the data from [http://192.168.3.14/www.vtokmak.office.com:8443/some-url]
01-30 12:34:35 [Thread@12:34:35.259:https://www.vtokmak.office.com:8443/some-url] WARN  com.tokmak.frontend.Router - fuck, even though I have a static fail-over URL, I failed!
Jan 30, 2018 12:34:36 PM org.apache.jasper.compiler.TldLocationsCache tldScanJar
INFO: At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.

这里是基于java的配置。尝试并分享结果。

import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.cache.interceptor.SimpleKeyGenerator;
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.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

import redis.clients.jedis.JedisPoolConfig;

@Configuration
@EnableCaching
public class RedisCacheConfig extends CachingConfigurerSupport {



    @Override
    @Bean
    public CacheManager cacheManager() {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate());
        cacheManager.setUsePrefix(true);
        //cacheManager.setCacheNames(getCacheNames());
        return cacheManager;
    }

    @Bean
    public RedisTemplate<Object, Object> redisTemplate() {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(jedisConnectionFactory());
        redisTemplate.setExposeConnection(true);
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<Object>(
                Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        om.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        om.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false);
        om.setSerializationInclusion(Include.NON_NULL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        redisTemplate.setDefaultSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setEnableDefaultSerializer(true);
        return redisTemplate;
    }

    @Bean
    public JedisConnectionFactory jedisConnectionFactory() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setTestOnBorrow(false);
        config.setTestOnCreate(false);
        config.setTestOnReturn(false);
        config.setTestWhileIdle(false);
        config.setMaxTotal(1000);
        config.setMaxIdle(1000);
        config.setMinIdle(0);
        config.setMaxWaitMillis(10000);
        JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
        connectionFactory.setHostName("172.17.0.2");
        //connectionFactory.setPort(6379);
        connectionFactory.setUsePool(true);
        connectionFactory.setPoolConfig(config);
        return connectionFactory;
    }

    @Override
    @Bean
    public KeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }

}

虽然我看不到你的代码,但我已经看到当你试图关闭一个 jedis 实例两次而不注意时会发生这种情况。例如: Jedis实现Closable and when inside a try-with-Resources会自动关闭

try(Jedis jedis = getJedis()){

}catch{...}

因此,如果您还调用 jedis.close(),它将抛出该错误。

或者

try{
    Jedis jedis = getJedis();
}catch(){
}finally{
    jedis.close()
}

或者查看您是否多次调用 close()

如@mp911de 所述,此问题与 Spring Data Redis 版本有关。当我升级时它解决了。