天天看點

Redis工具類與Redis基本操作

一、簡介

1、Jedis就是內建了Redis的一些指令操作,封裝了Redis的java用戶端

2、本文使用JedisPool封裝擷取jedis的工具類,包括一些基本配置介紹。

3、調用JedisPool封裝的工具類封裝了jedis的一些基本操作。

4、本人水準有限,還望各位大佬指正文中的問題。

二、使用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);
    }

}
           

繼續閱讀