JedisException: Could not get a

2020-03-13  本文已影响0人  秋元_92a3

错误堆栈信息

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'redisConfig': Invocation of init method failed; nested exception is redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:139)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsBeforeInitialization(AbstractAutowireCapableBeanFactory.java:419)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1737)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:576)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:846)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:863)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:546)
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:142)
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:775)
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:316)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
    at com.ruoyi.RuoYiApplication.main(RuoYiApplication.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: redis.clients.jedis.exceptions.JedisException: Could not get a resource from the pool
    at redis.clients.util.Pool.getResource(Pool.java:51)
    at redis.clients.jedis.ShardedJedisPool.getResource(ShardedJedisPool.java:36)
    at com.ruoyi.redis.util.InitRedisUtil.getInstance(InitRedisUtil.java:106)
    at com.ruoyi.redis.util.InitRedisUtil.init(InitRedisUtil.java:32)
    at com.ruoyi.redis.config.RedisConfig.initRedis(RedisConfig.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleElement.invoke(InitDestroyAnnotationBeanPostProcessor.java:363)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor$LifecycleMetadata.invokeInitMethods(InitDestroyAnnotationBeanPostProcessor.java:307)
    at org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor.postProcessBeforeInitialization(InitDestroyAnnotationBeanPostProcessor.java:136)
    ... 23 common frames omitted
Caused by: java.util.NoSuchElementException: Unable to validate object
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:498)
    at org.apache.commons.pool2.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:365)
    at redis.clients.util.Pool.getResource(Pool.java:49)
    ... 34 common frames omitted

分析

上面的报错堆栈中,有两个Caused by,分别是

1、 Could not get a resource from the pool
2、java.util.NoSuchElementException: Unable to validate object

说的大概是一个意思,连接池中获取不到资源/找不到有效的对象。查看报错位置的源码,如下:

public T borrowObject(long borrowMaxWaitMillis) throws Exception {
        this.assertOpen();
        AbandonedConfig ac = this.abandonedConfig;
        if (ac != null && ac.getRemoveAbandonedOnBorrow() && this.getNumIdle() < 2 && this.getNumActive() > this.getMaxTotal() - 3) {
            this.removeAbandoned(ac);
        }

        PooledObject<T> p = null;
        boolean blockWhenExhausted = this.getBlockWhenExhausted();
        long waitTime = System.currentTimeMillis();

        while(true) {
            boolean create;
            do {
                do {
                    do {
                        if (p != null) {
                            this.updateStatsBorrow(p, System.currentTimeMillis() - waitTime);
                            return p.getObject();
                        }

                        create = false;
                        p = (PooledObject)this.idleObjects.pollFirst();
                        if (p == null) {
                            p = this.create();
                            if (p != null) {
                                create = true;
                            }
                        }

                        if (blockWhenExhausted) {
                            if (p == null) {
                                if (borrowMaxWaitMillis < 0L) {
                                    p = (PooledObject)this.idleObjects.takeFirst();
                                } else {
                                    p = (PooledObject)this.idleObjects.pollFirst(borrowMaxWaitMillis, TimeUnit.MILLISECONDS);
                                }
                            }

                            if (p == null) {
                                throw new NoSuchElementException("Timeout waiting for idle object");
                            }
                        } else if (p == null) {
                            throw new NoSuchElementException("Pool exhausted");
                        }

                        if (!p.allocate()) {
                            p = null;
                        }
                    } while(p == null);

                    try {
                        this.factory.activateObject(p);
                    } catch (Exception var13) {
                        try {
                            this.destroy(p);
                        } catch (Exception var12) {
                            ;
                        }

                        p = null;
                        if (create) {
                            NoSuchElementException nsee = new NoSuchElementException("Unable to activate object");
                            nsee.initCause(var13);
                            throw nsee;
                        }
                    }
                } while(p == null);
            } while(!this.getTestOnBorrow() && (!create || !this.getTestOnCreate()));

            boolean validate = false;
            Throwable validationThrowable = null;

            try {
                validate = this.factory.validateObject(p);
            } catch (Throwable var15) {
                PoolUtils.checkRethrow(var15);
                validationThrowable = var15;
            }

            if (!validate) {
                try {
                    this.destroy(p);
                    this.destroyedByBorrowValidationCount.incrementAndGet();
                } catch (Exception var14) {
                    ;
                }

                p = null;
                if (create) {
                    NoSuchElementException nsee = new NoSuchElementException("Unable to validate object");
                    nsee.initCause(validationThrowable);
                    throw nsee;
                }
            }
        }
    }

查看源码,发下报错的主要原因是上面源码中变量validate的值是false导致的,而左右这个变量的值的代码是

validate = this.factory.validateObject(p);

跟进去看源码:

public boolean validateObject(PooledObject<Jedis> pooledJedis) {
        BinaryJedis jedis = (BinaryJedis)pooledJedis.getObject();

        try {
            HostAndPort hostAndPort = (HostAndPort)this.hostAndPort.get();
            String connectionHost = jedis.getClient().getHost();
            int connectionPort = jedis.getClient().getPort();
            return hostAndPort.getHost().equals(connectionHost) && hostAndPort.getPort() == connectionPort && jedis.isConnected() && jedis.ping().equals("PONG");
        } catch (Exception var6) {
            return false;
        }
    }

这个方法是判断连接有效性的,这里校验不通过,说明reids地址配置的有问题,当然,也可以通过配置设置,启动的时候不进行redis地址有效性的校验,配置如下:

 // 在获取连接的时候检查有效性, 默认false
config.setTestOnBorrow(true);          

将这个配置去掉,问题解决,当然,去掉的前提是你得保证自己配置的redis地址正确,配个错误地址,运营的时候才发现,也是不好的。

探究

配置config.setTestOnBorrow(true); 改为false,启动时这个错误不报了,但是出现了新错误,如下:

22:11:44.234 [restartedMain] ERROR c.r.r.u.InitRedisUtil - [setStringValue,192] - java.net.ConnectException: Connection refused (Connection refused)
redis.clients.jedis.exceptions.JedisConnectionException: java.net.ConnectException: Connection refused (Connection refused)
    at redis.clients.jedis.Connection.connect(Connection.java:207)
    at redis.clients.jedis.BinaryClient.connect(BinaryClient.java:93)
    at redis.clients.jedis.Connection.sendCommand(Connection.java:126)
    at redis.clients.jedis.BinaryClient.set(BinaryClient.java:110)
    at redis.clients.jedis.Client.set(Client.java:47)
    at redis.clients.jedis.Jedis.set(Jedis.java:120)
    at redis.clients.jedis.ShardedJedis.set(ShardedJedis.java:43)
    at com.ruoyi.redis.util.InitRedisUtil.setStringValue(InitRedisUtil.java:190)
    at com.ruoyi.redis.util.RedisUtil.setStringValue(RedisUtil.java:77)
    at com.ruoyi.redis.util.RedisUtil.putDefaultInfo(RedisUtil.java:35)
    at com.ruoyi.redis.runner.InitRedisCacheRunner.run(InitRedisCacheRunner.java:25)
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:813)
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:797)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:324)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1260)
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1248)
    at com.ruoyi.RuoYiApplication.main(RuoYiApplication.java:25)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
Caused by: java.net.ConnectException: Connection refused (Connection refused)
    at java.net.PlainSocketImpl.socketConnect(Native Method)
    at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
    at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
    at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
    at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
    at java.net.Socket.connect(Socket.java:589)
    at redis.clients.jedis.Connection.connect(Connection.java:184)
    ... 21 common frames omitted

我的连接地址配置如下,我可以确定我的ip和端口没有问题

redis.address=redis://root:123456@121.271.21.198:6379/0,redis://root:123456@121.271.21.198:6379/1,redis://root:123456@121.271.94.217:6379/2

上面的配置中,123456是redis密码,访问redis只需要密码,不需要用户名,但是这里却配置了用户名,这是因为程序在解析该地址的时候使用的URI进行解析,这个用户名密码是这个对象的解析标准服务器地址用的。我这里的root是随便写的,因为jedis使用url解析完配置之后,直接使用的是host,port,pwd,除了上面三个外,还有一个参数ssl,这个是根据地址前面前缀redis,匹配的,如果以rediss开头,则ssl为true,但是我之前配的都是redis,所以它这个参数配置的都是false,将地址中的redis改成rediss,重新运行服务,问题解决。
上面描述对应的源码如下:

/**
* 这里传入的参数实际上就是:redis://root:123456@121.271.21.198:6379/0
*/
public JedisShardInfo(String host) {
        super(1);
        this.password = null;
        this.name = null;
        this.db = 0;
        URI uri = URI.create(host);
        if (JedisURIHelper.isValid(uri)) {
            this.host = uri.getHost();
            this.port = uri.getPort();
            this.password = JedisURIHelper.getPassword(uri);
            this.db = JedisURIHelper.getDBIndex(uri);
            this.ssl = uri.getScheme().equals("rediss");
        } else {
            this.host = host;
            this.port = 6379;
        }

    }
public static String getPassword(URI uri) {
        String userInfo = uri.getUserInfo();//root:123456
        return userInfo != null ? userInfo.split(":", 2)[1] : null;//123456
    }
public static int getDBIndex(URI uri) {
        String[] pathSplit = uri.getPath().split("/", 2);
        if (pathSplit.length > 1) {
            String dbIndexStr = pathSplit[1];//0
            return dbIndexStr.isEmpty() ? 0 : Integer.parseInt(dbIndexStr);//0
        } else {
            return 0;
        }
    }

从上面的源码知,jedis在初始化连接工厂的时候,解析地址参数并没有使用那个用户名。

SSL

Secure socket layer(SSL)安全协议主要用来提供对用户和服务器的认证;对传送的数据进行加密和隐藏;确保数据在传送中不被改变,即数据的完整性,现已成为该领域中全球化的标准。

上一篇 下一篇

猜你喜欢

热点阅读