SpringBoot2.0(13)集成Redis详解及踩过的坑(Could not get a resource from the pool)

SpringBoot2.0集成Redis

首先安装的过程就不提了。上一个项目的redis是配置在Windows下的,集成很简单,也没有做什么配置。这次为了进行测试,装在了linux下。在SpringBoot集成的过程中遇到了一些小坑,分享一下。

pom文件中添加依赖

<!-- https://mvnrepository.com/artifact/redis.clients/jedis -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.0</version>
</dependency>

<!--引入Json依赖-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.47</version>
</dependency>

可以看到除了Redis我还加入了JSON的依赖,这是因为我之后读取出来的数据要转换成JSON串的格式,数据可读,方便开发。

application.properties文件中添加配置

#redis配置
#Linux主机地址
spring.redis.host=192.168.78.131
#端口
spring.redis.port=6379
#超时时间,ms
spring.redis.timeout=3000
#连接池的最大连接数
spring.redis.jedis.pool.max-active=10
#链接的最大等待时间ms
spring.redis.jedis.pool.max-wait=3000
#连接池中的最大的等待数量
spring.redis.jedis.pool.max-idle=10

在SpringBoot2.0的版本中timeout变成了Duration类型的,从其源码中我们可以看一下是怎么定义的:

public void setTimeout(Duration timeout) {
        this.timeout = timeout;
    }

所以正常的写法应该是:

spring.redis.timeout=3000ms

但是这样也带来了一个问题,下面再说这个问题是什么,这里先按照初始的定义,不会报错的

正常的人可能还会在配置文件中添加redis的密码的配置,没有密码的话就不要添加这个配置,就算默认为空也会导致出现错误,不能实现OAuth认证,要是有密码的话就添加上并写上自己的密码。

spring.redis.password=

Redis的自定义初始化

首先写redisConfig的文件,读取application.properties文件中yml的配置

package com.springboot.SecKill.redis;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;


/**
 * Redis的配置
 * @author WilsonSong
 * @date 2018/8/1/001
 */
//作为组件扫描进来
@Component
//读取配置文件
@Configuration
@ConfigurationProperties(prefix = "spring.redis")
public class RedisConfig {
    private String host;  //主机
    private int port;  //端口
    private int timeout;  //超时时间

    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;  //连接池最大线程数

    @Value("${spring.redis.jedis.pool.max-wait}")
    private long  maxWait;  //等待时间

    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;//最大空闲连接

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }


    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public int getMaxActive() {
        return maxActive;
    }

    public void setMaxActive(int maxActive) {
        this.maxActive = maxActive;
    }

    public long getMaxWait() {
        return maxWait;
    }

    public void setMaxWait(long maxWait) {
        this.maxWait = maxWait;
    }

    public int getMaxIdle() {
        return maxIdle;
    }

    public void setMaxIdle(int maxIdle) {
        this.maxIdle = maxIdle;
    }
}

上面用了两种方式来读取application.properties中的配置,一种是直接设置@ConfigurationProperties(prefix = “spring.redis”),然后变量名与application.properties中的变量名一样,这样就可以读出来了,然后像max-wait这种变量名没法定义啊,所以又用了@Value(“${spring.redis.jedis.pool.max-idle}”)这种注解的方式来读取,当然你可以全部影注解的方式来读取,注意过程中的每一个变量的基本数据类型定义准确。maxWait和timeout这两个本来都是Duration类型的,但是这里分别写成long和int类型的。一会儿再解释这个问题。

过程中需要从redis连接池中获取redis服务,所以这里初始化jedisPool的配置

package com.springboot.SecKill.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author WilsonSong
 * @date 2018/8/1/001
 */
@Service
public class RedisPoolFactory {

    @Autowired
    RedisConfig redisConfig;
    /**
     * redis连接池的一些配置
     * @return
     */
    @Bean
    public JedisPool JedisPoolFactory(){
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(redisConfig.getMaxIdle());
        poolConfig.setMaxTotal(redisConfig.getMaxActive());
        poolConfig.setMaxWaitMillis((redisConfig.getMaxWait()));

        JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(),redisConfig.getPort(),redisConfig.getTimeout());
        return jedisPool;
    }

}

这里解释下maxWait和timeout这两个数据类型的问题。首先

poolConfig.setMaxWaitMillis((redisConfig.getMaxWait()));

用到了maxWait这个变量,看一下setMaxWaitMillis()函数的源码

public void setMaxWaitMillis(long maxWaitMillis) {
        this.maxWaitMillis = maxWaitMillis;
    }

maxWaitMillis这个变量是long类型的,所以执勤才那么定义。

同理

 JedisPool jedisPool = new JedisPool(poolConfig, redisConfig.getHost(),redisConfig.getPort(),redisConfig.getTimeout());

这里面有用到redisConfig.getTimeout(),看一下JedisPool是怎么初始化的

public JedisPool(GenericObjectPoolConfig poolConfig, String host, int port, int timeout) {
        this(poolConfig, host, port, timeout, (String)null, 0, (String)null);
    }

可以看到timeout是int类型的,你要一开始定义成Duration类型的,然后可能过程中会涉及到数据类型的强制准换,会不会报错不知道,有兴趣的可以试一下。

最后就是初始化redis的方法如get,set等等

package com.springboot.SecKill.redis;

import com.alibaba.fastjson.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;



/**
 * @author WilsonSong
 * @date 2018/8/1
 */
@Service
public class RedisService {
    private static final Logger logger = LoggerFactory.getLogger(RedisService.class);

    @Autowired
    JedisPool jedisPool;


    public <T> T get(KeyPrefix prefix, String key, Class<T> clazz){
        Jedis jedis = null;
        try{
          jedis = jedisPool.getResource();
          //生成real  key
          String realKey = prefix.getPrefix() + key;
          String str = jedis.get(realKey);
          T t = String2Bean(str, clazz);
          return t;
        }catch (Exception e){
            logger.error("redis连接池异常"+e.getMessage());
            return null;
        }finally {
            if (jedis != null){
               jedis.close();
            }
        }
    }

    public <T> boolean set(KeyPrefix prefix,String key, T value){
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
            String value_new = Bean2String(value);
            if (value_new == null || value_new.length() <0){
                return false;
            }

            //生成real  key
            String realKey = prefix.getPrefix() + key;
            //过期时间
            int seconds = prefix.expireSeconds();
            if (seconds <= 0){
                jedis.set(realKey, value_new);
            }else {
                jedis.setex(realKey,seconds,value_new);
            }

            return true;
        }catch (Exception e){
            logger.error("redis连接池异常"+e.getMessage());
            return false;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    //key 是否存在
    public <T> Boolean exists(KeyPrefix prefix, String key){
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
            //生成real  key
            String realKey = prefix.getPrefix() + key;
            return jedis.exists(realKey);

        }catch (Exception e){
            logger.error("redis连接池异常"+e.getMessage());
            return null;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    //增加key对应的值
    public <T> Long incr(KeyPrefix prefix, String key){
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
            //生成real  key
            String realKey = prefix.getPrefix() + key;
            return jedis.incr(realKey);
        }catch (Exception e){
            logger.error("redis连接池异常"+e.getMessage());
            return null;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    //减少key对应的对象的值
    public <T> Long decr(KeyPrefix prefix, String key){
        Jedis jedis = null;
        try{
            jedis = jedisPool.getResource();
            //生成real  key
            String realKey = prefix.getPrefix() + key;
            return jedis.decr(realKey);
        }catch (Exception e){
            logger.error("redis连接池异常"+e.getMessage());
            return null;
        }finally {
            if (jedis != null){
                jedis.close();
            }
        }
    }

    //bean对象准换为String
    private <T> String Bean2String(T value) {
        if (value == null){
            return null;
        }
        Class<?> clazz = value.getClass();
        if (clazz == int.class || clazz == Integer.class){
            return  ""+value;
        }else if (clazz == String.class){
            return (String)value;
        }else if (clazz == long.class || clazz == Long.class){
            return  ""+value;
        }else {
            return JSON.toJSONString(value);
        }

    }

    //String转换为bean
    private <T> T String2Bean(String str, Class<T> clazz) {
        if (str == null || str.length() <0 || clazz == null){
            return null;
        }

        if (clazz == int.class || clazz == Integer.class){
            return  (T)Integer.valueOf(str);
        }else if (clazz == String.class){
            return (T)str;
        }else if (clazz == long.class || clazz == Long.class){
            return (T)Long.valueOf(str);
        }else {
            return JSON.toJavaObject(JSON.parseObject(str),clazz);
        }
    }
}

最后在controller中编写一个方法实现对service层的调用即可,就不赘述了。

踩过的坑

运行程序发现报出异常

Could not get a resource from the pool。

其实也就是在执行

jedis = jedisPool.getResource();这一步时出错了

  1. 首先第一种可能是你的redis服务没开启,检查下。
  2. 确认开启了之后就修改你的redis.conf文件,把bind 127.0.0.1改成bind 0.0.0.0,

​ 也就是把redis的访问权限有只能本机访问改成所有的都能访问。

  1. 最后可能的原因有你的Linux的防火墙没有开放Redis的端口,可以配置打开,也可以直接关掉防火墙,我这里直接关掉了Linux的防火墙。

我用的是centos7

永久关闭防火墙的方法

//临时关闭
systemctl stop firewalld
//禁止开机启动
systemctl disable firewalld

这样你再测试下,要是还不能用还有别的原因,可以自己上网去看一下具体怎么解决。

猜你喜欢

转载自blog.csdn.net/WilsonSong1024/article/details/81513399
今日推荐