Jedis连接池封装工具类与Jedis基本操作封装

一、简介

1、本文使用JedisPool封装获取jedis的工具类,包括一些基本配置介绍。

2、调用JedisPool封装的工具类封装了jedis的一些基本操作。

3、本人水平有限,还望各位大佬指正文中的问题。

二、使用Jedis时遇到的问题

1、在高并发操作redis时,如果对Jedis操作不当会抛出 java.lang.ClassCastException: [B cannot be cast to java.lang.Long,类似的异常,然后redis就挂掉了,只能重新启动服务。

上网查询原因,发现在多线程中,使用 Jedis操作redis的时候,对底层执行redis命令做了缓存,所以如果某一次redis操作出现异常,jedis实例中的缓存数据不会被清空,而直接放回连接池中。下一次从池中取出了同一个jedis对象,发送的命令用的还是上一个线程的数据。所以如果两个线程使用的数据类型不一样,就会发生上面的问题。但是加入两个线程使用的数据类型是一样的,那么系统不会报异常,但是数据可能全是错乱的,后果将不可设想。

有兴趣的可以执行以下Demo重现。(此Demo放到文章最后)

三、Jedis连接池封装工具类

package com.gscaifu.gsTool.util;

import com.gscaifu.gsTool.RedisConfig;
import com.gscaifu.gsTool.StringUtil;
import org.apache.log4j.Logger;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * redis连接池工具类
 * Created by qx.zhangbj02320 on 2018/4/23.
 */
public class JedisPoolUtils {

    private static Logger logger = Logger.getLogger(JedisPoolUtils.class);

    //访问密码
    private static String AUTH = "";

    //Redis的端口号
    private static int PORT = Integer.parseInt(RedisConfig.getInstance().getValue("redis.port"));

    private static String redisHost = com.gscaifu.gsTool.RedisConfig.getInstance().getValue("redis.host");
    /**
     * 可用连接实例的最大数目,默认为8
     * 如果赋值为-1,则表示不限制;如果pool已经分配了maxActive个jedis实例,则此时pool的状态为exhausted(耗尽)。
     */
    private static int MAX_ACTIVE = 500;
    /**
     * 控制一个pool最多有多少个状态为idle(空闲的)的jedis实例,默认值也是8
     */
    private static int MAX_IDLE = 100;

    /**
     * 控制一个pool最少有多少个状态为idle(空闲的)的jedis实例,默认值也是8
     */
    private static int MIN_IDLE = 50;
    /**
     * 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时。如果超过等待时间,则直接抛出JedisConnectionException;
     */
    private static int MAX_WAIT = 10 * 1000;
    /**
     * 超时时间,当池内没有可用对象返回时,最大等待时间
     */
    private static int TIMEOUT = 10 * 1000;
    /**
     * 在borrow一个jedis实例时,是否提前进行validate操作;如果为true,则得到的jedis实例均是可用的;
     */
    private static boolean TEST_ON_BORROW = true;

    /**
     * 在return给pool时,是否提前进行validate操作;
     */
    private static boolean TEST_ON_RETURN = true;

    /**
     * 如果为true,表示有一个idle object evitor线程对idle object进行扫描,如果validate失败,此object会被从pool中drop掉;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义;
     */
    private static boolean TEST_WHILE_IDLE = true;

    private static JedisPool jedisPool = null;

    /**
     * 初始化Redis连接池
     */
    private static void createJedisPool() {
        try {
            JedisPoolConfig config = new JedisPoolConfig();
            config.setMaxTotal(MAX_ACTIVE);
            config.setMaxIdle(MAX_IDLE);
            config.setMinIdle(MIN_IDLE);
            config.setMaxWaitMillis(MAX_WAIT);
            config.setTestOnBorrow(TEST_ON_BORROW);//使用时进行扫描,确保都可用
            config.setTestWhileIdle(TEST_WHILE_IDLE);//Idle时进行连接扫描
            config.setTestOnReturn(TEST_ON_RETURN);//还回线程池时进行扫描
            //表示idle object evitor两次扫描之间要sleep的毫秒数
            config.setTimeBetweenEvictionRunsMillis(30000);
            //表示idle object evitor每次扫描的最多的对象数
            config.setNumTestsPerEvictionRun(50);
            //表示一个对象至少停留在idle状态的最短时间,然后才能被idle object evitor扫描并驱逐;这一项只有在timeBetweenEvictionRunsMillis大于0时才有意义
            config.setMinEvictableIdleTimeMillis(60000);
            if (StringUtil.isValid(AUTH)) {
                jedisPool = new JedisPool(config, redisHost, PORT, TIMEOUT, AUTH);
            } else {
                jedisPool = new JedisPool(config, redisHost, PORT, TIMEOUT);
            }
        } catch (Exception e) {
            logger.error("First create JedisPool error : " + e);
            try {
                //如果第一个IP异常,则访问第二个IP
                JedisPoolConfig config = new JedisPoolConfig();
                config.setMaxTotal(MAX_ACTIVE);
                config.setMaxIdle(MAX_IDLE);
                config.setMaxWaitMillis(MAX_WAIT);
                config.setTestOnBorrow(TEST_ON_BORROW);
                jedisPool = new JedisPool(config, redisHost, PORT, TIMEOUT, AUTH);
            } catch (Exception e2) {
                logger.error("Second create JedisPool error : " + e2);
            }
        }
    }

    /**
     * 在多线程环境同步初始化
     */
    private static synchronized void poolInit() {
        if (jedisPool == null) {
            createJedisPool();
        }
    }

    /**
     * 同步获取Jedis实例
     *
     * @return Jedis
     */
    public static Jedis getJedis() {
        if (jedisPool == null) {
            poolInit();
        }
        Jedis jedis = null;
        try {
            if (jedisPool != null) {
                jedis = jedisPool.getResource();
            }
        } catch (Exception e) {
            logger.error("Get jedis Error : " + e.getMessage(), e);
        }
        return jedis;
    }

    /**
     * 释放jedis资源
     *
     * @param jedis
     */
    public static void returnResource(final Jedis jedis) {
        logger.debug("执行释放jedis资源方法returnResource:");
        if (jedis != null && jedisPool != null) {
            jedisPool.returnResource(jedis);
        }
    }

    /**
     * 关闭连接池
     */
    public static void closePool() {
        if (jedisPool != null) {
            jedisPool.close();
        }
    }

}

四、Jedis基本操作封装

package com.gscaifu.gsMMscene.service.impl;

import com.gscaifu.gsTool.util.JedisPoolUtils;
import redis.clients.jedis.Jedis;

import java.util.Map;
import java.util.Set;

/**
 * Created by qx.zhangbj02320 on 2017/11/13.
 */
public class BaseRedisServiceImpl {

    /**
     * 将字符串保存到redis中
     *
     * @param key
     * @param value
     * @return 成功 OK
     */
    protected String set(String key, String value) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.set(key, value);
        } finally {
            //使用结束后要释放jedis资源
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 获取指定的值
     *
     * @param key redis中值的键
     * @return
     */
    protected String get(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.get(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 追加字符串
     *
     * @param key
     * @param value
     * @return
     */
    protected long append(String key, String value) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.append(key, value);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 判断指定的键是否存在
     *
     * @param key
     * @return true-存在,false-不存在
     */
    protected boolean exists(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.exists(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 删除指定的键,
     *
     * @param key
     * @return 0表示指定的键不存在, 大于0表示删除一个或多个。
     */
    protected long del(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.del(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 将Map保存到redis中
     *
     * @param key
     * @param value
     * @return
     */
    protected String setMap(String key, Map<String, String> value) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.hmset(key, value);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 向已有Map中添加键值对
     *
     * @param key   Map的key
     * @param field map集合中的key
     * @param value map集合中的value
     * @return 0-更新   1-创建
     */
    protected long setMapValue(String key, String field, String value) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.hset(key, field, value);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 获取redis中的Map
     *
     * @param key
     * @return
     */
    protected Map<String, String> getMap(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.hgetAll(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 获取指定Map中的某键对应的值
     *
     * @param key   Map对应的key值
     * @param field Map中键值对中的键
     * @return
     */
    protected String getMapValue(String key, String field) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.hget(key, field);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 删除已有Map中的键值对
     *
     * @param key
     * @param fields
     * @return
     */
    protected long delMapValues(String key, String... fields) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.hdel(key, fields);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 删除Map
     *
     * @param key
     * @return
     */
    protected long delMap(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.del(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 设置键的超时时间
     *
     * @param key     键
     * @param seconds 秒
     * @return
     */
    protected long setExpire(String key, int seconds) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.expire(key, seconds);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 向集合中添加元素
     *
     * @param key
     * @param members
     * @return
     */
    protected long setList(String key, String... members) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.sadd(key, members);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 取出集合中所有的元素
     *
     * @param key
     * @return
     */
    protected Set<String> getList(String key) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.smembers(key);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

    /**
     * 移除集合中指定元素
     *
     * @param key
     * @param members
     * @return
     */
    protected long delList(String key, String... members) {
        Jedis jedis = JedisPoolUtils.getJedis();
        try {
            return jedis.srem(key, members);
        } finally {
            JedisPoolUtils.returnResource(jedis);
        }
    }

}

五、Bug复现

调用setMapValue方法的同时,快速持续调用getMapValue方法。由于 setMapValue方法中有循环设置值,并且循环每次都会sleep 10毫秒。所以调用一次setMapValue方法,在setMapValue方法结束前快速调用getMapValue方法,问题将会重现。话不多说,直接贴代码。
package com.gscaifu.gsMMscene.service.impl;

import com.gscaifu.gsTool.util.JedisPoolUtils;
import redis.clients.jedis.Jedis;

import java.util.Map;
import java.util.Set;

/**
 * Created by qx.zhangbj02320 on 2017/11/13.
 */
public class BaseRedisServiceImpl {
    private static final String redisHost = com.gscaifu.gsTool.RedisConfig.getInstance().getValue("redis.host");
    private static final Integer redisPort = Integer.parseInt(com.gscaifu.gsTool.RedisConfig.getInstance().getValue("redis.port"));
    private static Jedis jedis = new Jedis(redisHost, redisPort);

    private Jedis getValidJedis() {
        try {
            System.out.println(jedis.ping());
        } catch (Exception e) {
            jedis = new Jedis(redisHost, redisPort);
            e.printStackTrace();
        }
        return jedis;
    }


    /**
     * 将字符串保存到redis中
     *
     * @param key
     * @param value
     * @return 成功 OK
     */
    protected String set(String key, String value) {
        getValidJedis();
        return jedis.set(key, value);
    }

    /**
     * 获取指定的值
     *
     * @param key redis中值的键
     * @return
     */
    protected String get(String key) {
        getValidJedis();
        return jedis.get(key);
    }

    /**
     * 追加字符串
     *
     * @param key
     * @param value
     * @return
     */
    protected long append(String key, String value) {
        getValidJedis();
        return jedis.append(key, value);
    }

    /**
     * 判断指定的键是否存在
     *
     * @param key
     * @return true-存在,false-不存在
     */
    protected boolean exists(String key) {
        getValidJedis();
        return jedis.exists(key);
    }

    /**
     * 删除指定的键,
     *
     * @param key
     * @return 0表示指定的键不存在, 大于0表示删除一个或多个。
     */
    protected long del(String key) {
        getValidJedis();
        return jedis.del(key);
    }

    /**
     * 将Map保存到redis中
     *
     * @param key
     * @param value
     * @return
     */
    protected String setMap(String key, Map<String, String> value) {
        getValidJedis();
        return jedis.hmset(key, value);
    }

    /**
     * 向已有Map中添加键值对
     *
     * @param key   Map的key
     * @param field map集合中的key
     * @param value map集合中的value
     * @return 0-更新   1-创建
     */
    protected long setMapValue(String key, String field, String value) {
        getValidJedis();
        for (int i=0;i<10000;i++){
            try {
                Thread.currentThread().sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            jedis.hset(key, field, value+i);
        }
        return 0;
//        Jedis jedis = JedisPoolUtils.getJedis();
//        try {
//            return jedis.hset(key, field, value);
//        } finally {
//            JedisPoolUtils.returnResource(jedis);
//        }
    }

    /**
     * 获取redis中的Map
     *
     * @param key
     * @return
     */
    protected Map<String, String> getMap(String key) {
        getValidJedis();
        return jedis.hgetAll(key);
    }

    /**
     * 获取指定Map中的某键对应的值
     *
     * @param key   Map对应的key值
     * @param field Map中键值对中的键
     * @return
     */
    protected String getMapValue(String key, String field) {
        getValidJedis();
        String value = jedis.hget(key, field);
        return value;
    }

    /**
     * 删除已有Map中的键值对
     *
     * @param key
     * @param fields
     * @return
     */
    protected long delMapValues(String key, String... fields) {
        getValidJedis();
        return jedis.hdel(key, fields);
    }

    /**
     * 删除Map
     *
     * @param key
     * @return
     */
    protected long delMap(String key) {
        getValidJedis();
        return jedis.del(key);
    }

    /**
     * 设置键的超时时间
     *
     * @param key     键
     * @param seconds 秒
     * @return
     */
    protected long setExpire(String key, int seconds) {
        getValidJedis();
        return jedis.expire(key, seconds);
    }

    /**
     * 向集合中添加元素
     *
     * @param key
     * @param members
     * @return
     */
    protected long setList(String key, String... members) {
        getValidJedis();
        return jedis.sadd(key, members);
    }

    /**
     * 取出集合中所有的元素
     *
     * @param key
     * @return
     */
    protected Set<String> getList(String key) {
        getValidJedis();
        return jedis.smembers(key);
    }

    /**
     * 移除集合中指定元素
     *
     * @param key
     * @param members
     * @return
     */
    protected long delList(String key, String... members) {
        getValidJedis();
        return jedis.srem(key, members);
    }

}








扫描二维码关注公众号,回复: 657882 查看本文章

猜你喜欢

转载自blog.csdn.net/biedazhangshu/article/details/80282533