Redis学习(三) - Redis客户端对比及配置(SpringBoot)

1.常用的redis客户端介绍以及对比

Jedis api 在线网址:http://tool.oschina.net/uploads/apidocs/redis/clients/jedis/Jedis.html
redisson 官网地址:https://redisson.org/redisson
git项目地址:https://github.com/redisson/redisson
lettuce 官网地址:https://lettuce.io/lettuce
git项目地址:https://github.com/lettuce-io/lettuce-core
在springboot2之后,对redis连接的支持,默认就采用了lettuce。这就一定程度说明了lettuce 和Jedis 的优劣。

概念:

  • Jedis:是老牌的Redis的Java实现客户端,提供了比较全面的Redis命令的支持,
  • Redisson:实现了分布式和可扩展的Java数据结构。
  • Lettuce:高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。

优点:

  • Jedis:比较全面的提供了Redis的操作特性
  • Redisson:促使使用者对Redis的关注分离,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,可通过 Redis支持延迟队列
  • Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作

可伸缩:

  • Jedis:使用阻塞的I/O,且其方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。 Jedis客户端实例不是线程安全的,所以需要通过连接池来使用Jedis。
  • Redisson:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Redisson的API是线程安全的,所以可以操作单个Redisson连接来完成各种操作
  • Lettuce:基于Netty框架的事件驱动的通信层,其方法调用是异步的。Lettuce的API是线程安全的,所以可以操作单个Lettuce连接来完成各种操作 lettuce能够支持redis4,需要java8及以上。lettuce是基于netty实现的与redis进行同步和异步的通信。

lettuce和jedis比较:

  • jedis使直接连接redis server,如果在多线程环境下是非线程安全的,这个时候只有使用连接池,为每个jedis实 例增加物理连接;
  • lettuce的连接是基于Netty的,连接实例(StatefulRedisConnection)可以在多个线程间并发访问,
    StatefulRedisConnection是线程安全的,所以一个连接实例可以满足多线程环境下的并发访问,当然这也是可伸缩的设计,一个连接实例不够的情况也可以按需增加连接实例。
  • Redisson实现了分布式和可扩展的Java数据结构,和Jedis相比,功能较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。Redisson的宗旨是促进使用者对Redis的关注分离,从而让使用者能够将精力更集中地放在处理业务逻辑上。

总结:

  • 优先使用Lettuce,如果需要分布式锁,分布式集合等分布式的高级特性,添加Redisson结合使用,因为Redisson本身对字符串的操作支持很差。
  • 在一些高并发的场景中,比如秒杀,抢票,抢购这些场景,都存在对核心资源,商品库存的争夺,控制不好,库存数量可能被减少到负数,出现超卖的情况,或者 产生唯一的一个递增ID,由于web应用部署在多个机器上,简单的同步加锁是无法实现的,给数据库加锁的话,对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害。那相对而言,redis的分布式锁,相对而言,是个很好的选择,redis官方推荐使用的Redisson就提供了分布式锁和 相关服务。

2.SpringBoot整合Jedis

简介

我们在使用springboot搭建微服务的时候,在很多时候还是需要redis的高速缓存来缓存一些数据,存储一些高频率访问的数据,如果直接使用redis的话又比较麻烦,在这里,我们使用jedis来实现redis缓存来达到高效缓存的目的

引入Jedis依赖

<dependency> 
	<groupId>redis.clientsgroupId> 
	<artifactId>jedisartifactId> 
dependency>

因为 SpringBoot 内默认引用了jedis版本。 所以我们直接引入jedis 依赖 无需在配置 jedis的版本号了。

application.yml

spring:
  redis:
    port: 6379
    password: 123456
    host: 192.168.20.135
    jedis:
      pool:
        max-idle: 6 #最大空闲数 
        max-active: 10 #最大连接数 
        min-idle: 2 #最小空闲数 
        timeout: 2000 #连接超时

编写Config

@Configuration
public class JedisConfig {
     
    private Logger logger = LoggerFactory.getLogger(JedisConfig.class);
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.jedis.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.jedis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.jedis.pool.min-idle}")
    private int minIdle;

    @Bean
    public JedisPool jedisPool() {
     
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        jedisPoolConfig.setMaxIdle(maxIdle);
        jedisPoolConfig.setMinIdle(minIdle);
        jedisPoolConfig.setMaxTotal(maxActive);
        JedisPool jedisPool = new JedisPool(jedisPoolConfig, host, port, timeout, password);
        logger.info("JedisPool连接成功:" + host + "\t" + port);
        return jedisPool;
    }
}

测试配置

@SpringBootTest
public class JedisApplicationTests {
     
    @Autowired
    private JedisPool jedisPool;

    @Test
    public void contextLoads() {
     
        System.out.println(jedisPool);
        //在连接池中得到Jedis连接 
        Jedis jedis = jedisPool.getResource();
        jedis.set("haha", "你好");
        jedis.set("name", "aaa");
        //关闭当前连接 
        jedis.close();
    }
}

封装工具类

@Component
public class JedisUtils {
     

    private static Logger logger = LoggerFactory.getLogger(JedisUtils.class);
    private static JedisPool jedisPool = SpringContextHolder
            .getBean(JedisPool.class);

    /**
     * 获取缓存
     * @param key 键
     * @return 值
     */
    public static String get(String key) {
     
        String value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                value = jedis.get(key);
                value = StringUtils.isNotBlank(value)
                        && !"nil".equalsIgnoreCase(value) ? value : null;
                logger.debug("get {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("get {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Object getObject(String key) {
     
        Object value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                value = toObject(jedis.get(getBytesKey(key)));
                logger.debug("getObject {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getObject {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 设置缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static String set(String key, String value, int cacheSeconds) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.set(key, value);
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("set {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("set {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 设置缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static String setObject(String key, Object value, int cacheSeconds) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.set(getBytesKey(key), toBytes(value));
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setObject {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setObject {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 获取List缓存
     *
     * @param key 键
     * @return 值
     */
    public static List<String> getList(String key) {
     
        List<String> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                value = jedis.lrange(key, 0, -1);
                logger.debug("getList {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getList {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 获取List缓存
     *
     * @param key 键
     * @return 值
     */
    public static List<Object> getObjectList(String key) {
     
        List<Object> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                List<byte[]> list = jedis.lrange(getBytesKey(key), 0, -1);
                value = Lists.newArrayList();
                for (byte[] bs : list) {
     
                    value.add(toObject(bs));
                }
                logger.debug("getObjectList {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getObjectList {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 设置List缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static long setList(String key, List<String> value, int cacheSeconds) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                jedis.del(key);
            }
            result = jedis.rpush(key, (String[]) value.toArray());
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setList {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setList {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 设置List缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static long setObjectList(String key, List<Object> value,
                                     int cacheSeconds) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                jedis.del(key);
            }
            List<byte[]> list = Lists.newArrayList();
            for (Object o : value) {
     
                list.add(toBytes(o));
            }
            result = jedis.rpush(getBytesKey(key), (byte[][]) list.toArray());
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setObjectList {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setObjectList {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向List缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long listAdd(String key, String... value) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.rpush(key, value);
            logger.debug("listAdd {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("listAdd {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向List缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long listObjectAdd(String key, Object... value) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            List<byte[]> list = Lists.newArrayList();
            for (Object o : value) {
     
                list.add(toBytes(o));
            }
            result = jedis.rpush(getBytesKey(key), (byte[][]) list.toArray());
            logger.debug("listObjectAdd {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("listObjectAdd {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Set<String> getSet(String key) {
     
        Set<String> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                value = jedis.smembers(key);
                logger.debug("getSet {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getSet {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 获取缓存
     *
     * @param key 键
     * @return 值
     */
    public static Set<Object> getObjectSet(String key) {
     
        Set<Object> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                value = Sets.newHashSet();
                Set<byte[]> set = jedis.smembers(getBytesKey(key));
                for (byte[] bs : set) {
     
                    value.add(toObject(bs));
                }
                logger.debug("getObjectSet {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getObjectSet {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 设置Set缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static long setSet(String key, Set<String> value, int cacheSeconds) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                jedis.del(key);
            }
            result = jedis.sadd(key, (String[]) value.toArray());
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setSet {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setSet {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 设置Set缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static long setObjectSet(String key, Set<Object> value,
                                    int cacheSeconds) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                jedis.del(key);
            }
            Set<byte[]> set = Sets.newHashSet();
            for (Object o : value) {
     
                set.add(toBytes(o));
            }
            result = jedis.sadd(getBytesKey(key), (byte[][]) set.toArray());
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setObjectSet {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setObjectSet {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向Set缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long setSetAdd(String key, String... value) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.sadd(key, value);
            logger.debug("setSetAdd {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setSetAdd {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向Set缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long setSetObjectAdd(String key, Object... value) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            Set<byte[]> set = Sets.newHashSet();
            for (Object o : value) {
     
                set.add(toBytes(o));
            }
            result = jedis.rpush(getBytesKey(key), (byte[][]) set.toArray());
            logger.debug("setSetObjectAdd {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setSetObjectAdd {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public static Map<String, String> getMap(String key) {
     
        Map<String, String> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                value = jedis.hgetAll(key);
                logger.debug("getMap {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getMap {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 获取Map缓存
     *
     * @param key 键
     * @return 值
     */
    public static Map<String, Object> getObjectMap(String key) {
     
        Map<String, Object> value = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                value = Maps.newHashMap();
                Map<byte[], byte[]> map = jedis.hgetAll(getBytesKey(key));
                for (Map.Entry<byte[], byte[]> e : map.entrySet()) {
     
                    value.put(StringUtils.toString(e.getKey()),
                            toObject(e.getValue()));
                }
                logger.debug("getObjectMap {} = {}", key, value);
            }
        } catch (Exception e) {
     
            logger.warn("getObjectMap {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return value;
    }

    /**
     * 设置Map缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static String setMap(String key, Map<String, String> value,
                                int cacheSeconds) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                jedis.del(key);
            }
            result = jedis.hmset(key, value);
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setMap {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setMap {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 设置Map缓存
     *
     * @param key          键
     * @param value        值
     * @param cacheSeconds 超时时间,0为不超时
     * @return
     */
    public static String setObjectMap(String key, Map<String, Object> value,
                                      int cacheSeconds) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                jedis.del(key);
            }
            Map<byte[], byte[]> map = Maps.newHashMap();
            for (Map.Entry<String, Object> e : value.entrySet()) {
     
                map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
            }
            result = jedis.hmset(getBytesKey(key), (Map<byte[], byte[]>) map);
            if (cacheSeconds != 0) {
     
                jedis.expire(key, cacheSeconds);
            }
            logger.debug("setObjectMap {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("setObjectMap {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向Map缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static String mapPut(String key, Map<String, String> value) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.hmset(key, value);
            logger.debug("mapPut {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("mapPut {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 向Map缓存中添加值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static String mapObjectPut(String key, Map<String, Object> value) {
     
        String result = null;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            Map<byte[], byte[]> map = Maps.newHashMap();
            for (Map.Entry<String, Object> e : value.entrySet()) {
     
                map.put(getBytesKey(e.getKey()), toBytes(e.getValue()));
            }
            result = jedis.hmset(getBytesKey(key), (Map<byte[], byte[]>) map);
            logger.debug("mapObjectPut {} = {}", key, value);
        } catch (Exception e) {
     
            logger.warn("mapObjectPut {} = {}", key, value, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 移除Map缓存中的值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long mapRemove(String key, String mapKey) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.hdel(key, mapKey);
            logger.debug("mapRemove {}  {}", key, mapKey);
        } catch (Exception e) {
     
            logger.warn("mapRemove {}  {}", key, mapKey, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 移除Map缓存中的值
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static long mapObjectRemove(String key, String mapKey) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.hdel(getBytesKey(key), getBytesKey(mapKey));
            logger.debug("mapObjectRemove {}  {}", key, mapKey);
        } catch (Exception e) {
     
            logger.warn("mapObjectRemove {}  {}", key, mapKey, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 判断Map缓存中的Key是否存在
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static boolean mapExists(String key, String mapKey) {
     
        boolean result = false;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.hexists(key, mapKey);
            logger.debug("mapExists {}  {}", key, mapKey);
        } catch (Exception e) {
     
            logger.warn("mapExists {}  {}", key, mapKey, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 判断Map缓存中的Key是否存在
     *
     * @param key   键
     * @param value 值
     * @return
     */
    public static boolean mapObjectExists(String key, String mapKey) {
     
        boolean result = false;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.hexists(getBytesKey(key), getBytesKey(mapKey));
            logger.debug("mapObjectExists {}  {}", key, mapKey);
        } catch (Exception e) {
     
            logger.warn("mapObjectExists {}  {}", key, mapKey, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 删除缓存
     *
     * @param key 键
     * @return
     */
    public static long del(String key) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(key)) {
     
                result = jedis.del(key);
                logger.debug("del {}", key);
            } else {
     
                logger.debug("del {} not exists", key);
            }
        } catch (Exception e) {
     
            logger.warn("del {}", key, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 删除缓存
     *
     * @param key 键
     * @return
     */
    public static long delObject(String key) {
     
        long result = 0;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            if (jedis.exists(getBytesKey(key))) {
     
                result = jedis.del(getBytesKey(key));
                logger.debug("delObject {}", key);
            } else {
     
                logger.debug("delObject {} not exists", key);
            }
        } catch (Exception e) {
     
            logger.warn("delObject {}", key, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    public static boolean exists(String key) {
     
        boolean result = false;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.exists(key);
            logger.debug("exists {}", key);
        } catch (Exception e) {
     
            logger.warn("exists {}", key, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 缓存是否存在
     *
     * @param key 键
     * @return
     */
    public static boolean existsObject(String key) {
     
        boolean result = false;
        Jedis jedis = null;
        try {
     
            jedis = getResource();
            result = jedis.exists(getBytesKey(key));
            logger.debug("existsObject {}", key);
        } catch (Exception e) {
     
            logger.warn("existsObject {}", key, e);
        } finally {
     
            returnResource(jedis);
        }
        return result;
    }

    /**
     * 获取资源
     *
     * @return
     * @throws JedisException
     */
    public static Jedis getResource() throws JedisException {
     
        Jedis jedis = null;
        try {
     
            jedis = jedisPool.getResource();
            // logger.debug("getResource.", jedis);
        } catch (JedisException e) {
     
            logger.warn("getResource.", e);
            returnBrokenResource(jedis);
            throw e;
        }
        return jedis;
    }

    /**
     * 归还资源
     *
     * @param jedis
     * @param isBroken
     */
    public static void returnBrokenResource(Jedis jedis) {
     
        if (jedis != null) {
     
            jedisPool.returnBrokenResource(jedis);
        }
    }

    /**
     * 释放资源
     *
     * @param jedis
     * @param isBroken
     */
    public static void returnResource(Jedis jedis) {
     
        if (jedis != null) {
     
            jedisPool.returnResource(jedis);
        }
    }

    /**
     * 获取byte[]类型Key
     *
     * @param key
     * @return
     */
    public static byte[] getBytesKey(Object object) {
     
        if (object instanceof String) {
     
            return StringUtils.getBytes((String) object);
        } else {
     
            return ObjectUtils.serialize(object);
        }
    }

    /**
     * Object转换byte[]类型
     *
     * @param key
     * @return
     */
    public static byte[] toBytes(Object object) {
     
        return ObjectUtils.serialize(object);
    }

    /**
     * byte[]型转换Object
     *
     * @param key
     * @return
     */
    public static Object toObject(byte[] bytes) {
     
        return ObjectUtils.unserialize(bytes);
    }

}

测试JedisUtils

@SpringBootTest
public class JedisTests {
     
    @Autowired
    private JedisUtils jedisUtils;

    @Test
    void test() {
     
        String val = jedisUtils.set("key", "value" 10);
        System.out.println(jedisUtils.get("key"));
    }
}

3. SpringBoot2.x中redis使用(lettuce)

java代码操作Redis,需要使用Jedis,也就是redis支持java的第三方类库 注意:Jedis2.7以上的版本才支持集群操作

maven配置

<dependencies>
	
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
    <dependency>
        <groupId>org.apache.commonsgroupId>
        <artifactId>commons-pool2artifactId>
    dependency>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-testartifactId>
        <scope>testscope>
    dependency>
dependencies>

配置文件配置

spring:
  # redis 配置
  redis:
    # 地址
    host: localhost
    # 端口,默认为6379
    port: 6379
    # 密码
    password:
    # 连接超时时间
    timeout: 10s
    lettuce:
      pool:
        # 连接池中的最小空闲连接
        min-idle: 0
        # 连接池中的最大空闲连接
        max-idle: 8
        # 连接池的最大数据库连接数
        max-active: 8
        # #连接池最大阻塞等待时间(使用负值表示没有限制)
        max-wait: -1ms

redis配置类

JdbcTemplate–>JDBC 进一步封装。
RedisTemplate–>redis进行了进一步封装 (lettuce)

** 简介**

编写缓存配置类RedisConfig用于调优缓存默认配置,RedisTemplate的类型兼容性更高
大家可以看到在redisTemplate()这个方法中用JacksonJsonRedisSerializer更换掉了Redis默认的序列化方式:JdkSerializationRedisSerializer
spring-data-redis中序列化类有以下几个:

  • GenericToStringSerializer:可以将任何对象泛化为字符创并序列化
  • Jackson2JsonRedisSerializer:序列化Object对象为json字符创(与JacksonJsonRedisSerializer相同)
  • JdkSerializationRedisSerializer:序列化java 对象
  • StringRedisSerializer:简单的字符串序列化
  • JdkSerializationRedisSerializer序列化被序列化对象必须实现Serializable接口,被序列化除属性内容还有其他内容,长度长且不易阅读,默认就是采用这种序列化方式
  • JacksonJsonRedisSerializer序列化,被序列化对象不需要实现Serializable接口,被序列化的结果清晰,容易阅读,而且存储字节少,速度快存储内容如下:"{“userName”:“guoweixin”,“age”:20}"
  • StringRedisSerializer序列化一般如果key、value都是string字符串的话,就是用这个就可以了

RedisConfig类

@Configuration
public class RedisConfig extends CachingConfigurerSupport {
     
    /*** 自定义缓存key的生成策略。默认的生成策略是看不懂的(乱码内容) 通过Spring 的依赖注入特性进行自定义的 配置注入并且此类是一个配置类可以更多程度的自定义配置 ** @return */
    @Bean
    @Override
    public KeyGenerator keyGenerator() {
     
        return new KeyGenerator() {
     
            @Override
            public Object generate(Object target, Method method, Object... params) {
     
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
     
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    /*** 缓存配置管理器 */
    @Bean
    public CacheManager cacheManager(LettuceConnectionFactory factory) {
     
        //以锁写入的方式创建RedisCacheWriter对象 
        RedisCacheWriter writer = RedisCacheWriter.lockingRedisCacheWriter(factory);
        //创建默认缓存配置对象 
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();
        RedisCacheManager cacheManager = new RedisCacheManager(writer, config);
        return cacheManager;
    }

    @Bean
    public RedisTemplate<String, Object> redisTemplate(LettuceConnectionFactory factory) {
     
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        // 在使用注解@Bean返回RedisTemplate的时候,同时配置hashKey与hashValue的序列化方式。 
        // key采用String的序列化方式 
        template.setKeySerializer(stringRedisSerializer);
        // value序列化方式采用jackson 
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // hash的key也采用String的序列化方式 
        template.setHashKeySerializer(stringRedisSerializer);
        // hash的value序列化方式采用jackson 
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

测试

@Log
@Service
public class RedisServiceImpl {
     
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /*** 普通缓存放入 * @param key 键 * @return true成功 false失败 */
    public String getString(String key) {
     
        if (redisTemplate.hasKey(key)) {
     
            log.info("Redis中查询");
            return (String) redisTemplate.opsForValue().get(key);
        } else {
     
            String val = "guoweixin";
            redisTemplate.opsForValue().set(key, val);
            log.info("数据库中查询的");
            return val;
        }
    }

    /*** 普通缓存放入 * @param key 键 * @param value 值 * @param expireTime 超时时间(秒) * @return true成功 false失败 */
    public Boolean set(String key, Object value, int expireTime) {
     
        try {
     
            redisTemplate.opsForValue().set(key, value, expireTime, TimeUnit.SECONDS);
            return true;
        } catch (Exception e) {
     
            e.printStackTrace();
            return false;
        }
    }
}

4.springBoot集成redisson(单机,集群,哨兵)

以下部分参考:https://zhuanlan.zhihu.com/p/47563733
引入依赖

<dependency>
      <groupId>org.redissongroupId>
      <artifactId>redissonartifactId>
      <version>3.8.2version>
dependency>

配置文件

application.properties

spring.redis.database=0
spring.redis.password=
spring.redis.timeout=3000
#sentinel/cluster/single
spring.redis.mode=single
#连接池配置
spring.redis.pool.max-idle=16
spring.redis.pool.min-idle=8
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=3000
spring.redis.pool.conn-timeout=3000
spring.redis.pool.so-timeout=3000
spring.redis.pool.size=10
#单机配置
spring.redis.single.address=192.168.60.23:6379
#集群配置
spring.redis.cluster.scan-interval=1000
spring.redis.cluster.nodes=
spring.redis.cluster.read-mode=SLAVE
spring.redis.cluster.retry-attempts=3
spring.redis.cluster.failed-attempts=3
spring.redis.cluster.slave-connection-pool-size=64
spring.redis.cluster.master-connection-pool-size=64
spring.redis.cluster.retry-interval=1500
#哨兵配置
spring.redis.sentinel.master=business-master
spring.redis.sentinel.nodes=
spring.redis.sentinel.master-onlyWrite=true
spring.redis.sentinel.fail-max=3

配置文件读取
RedisProperties

@ConfigurationProperties(prefix = "spring.redis", ignoreUnknownFields = false)
@Data
@ToString
public class RedisProperties {
     

    private int database;

    /**
     * 等待节点回复命令的时间。该时间从命令发送成功时开始计时
     */
    private int timeout;

    private String password;

    private String mode;

    /**
     * 池配置
     */
    private RedisPoolProperties pool;

    /**
     * 单机信息配置
     */
    private RedisSingleProperties single;

    /**
     * 集群 信息配置
     */
    private RedisClusterProperties cluster;

    /**
     * 哨兵配置
     */
    private RedisSentinelProperties sentinel;
}

池配置RedisPoolProperties

@Data
@ToString
public class RedisPoolProperties {
     

    private int maxIdle;

    private int minIdle;

    private int maxActive;

    private int maxWait;

    private int connTimeout;

    private int soTimeout;

    /**
     * 池大小
     */
    private  int size;

}

RedisSingleProperties

@Data
@ToString
public class RedisSingleProperties {
     
    private  String address;
}

集群配置RedisClusterProperties

@Data
@ToString
public class RedisClusterProperties {
     

    /**
     * 集群状态扫描间隔时间,单位是毫秒
     */
    private int scanInterval;

    /**
     * 集群节点
     */
    private String nodes;

    /**
     * 默认值: SLAVE(只在从服务节点里读取)设置读取操作选择节点的模式。 可用值为: SLAVE - 只在从服务节点里读取。
     * MASTER - 只在主服务节点里读取。 MASTER_SLAVE - 在主从服务节点里都可以读取
     */
    private String readMode;
    /**
     * (从节点连接池大小) 默认值:64
     */
    private int slaveConnectionPoolSize;
    /**
     * 主节点连接池大小)默认值:64
     */
    private int masterConnectionPoolSize;

    /**
     * (命令失败重试次数) 默认值:3
     */
    private int retryAttempts;

    /**
     *命令重试发送时间间隔,单位:毫秒 默认值:1500
     */
    private int retryInterval;

    /**
     * 执行失败最大次数默认值:3
     */
    private int failedAttempts;
}

哨兵配置 RedisSentinelProperties

@Data
@ToString
public class RedisSentinelProperties {
     

    /**
     * 哨兵master 名称
     */
    private String master;

    /**
     * 哨兵节点
     */
    private String nodes;

    /**
     * 哨兵配置
     */
    private boolean masterOnlyWrite;

    /**
     *
     */
    private int failMax;
}

CacheConfiguration

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class CacheConfiguration {
     

    @Autowired
    RedisProperties redisProperties;

    @Configuration
    @ConditionalOnClass({
     Redisson.class})
    @ConditionalOnExpression("'${spring.redis.mode}'=='single' or '${spring.redis.mode}'=='cluster' or '${spring.redis.mode}'=='sentinel'")
    protected class RedissonSingleClientConfiguration {
     

        /**
         * 单机模式 redisson 客户端
         */

        @Bean
        @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "single")
        RedissonClient redissonSingle() {
     
            Config config = new Config();
            String node = redisProperties.getSingle().getAddress();
            node = node.startsWith("redis://") ? node : "redis://" + node;
            SingleServerConfig serverConfig = config.useSingleServer()
                    .setAddress(node)
                    .setTimeout(redisProperties.getPool().getConnTimeout())
                    .setConnectionPoolSize(redisProperties.getPool().getSize())
                    .setConnectionMinimumIdleSize(redisProperties.getPool().getMinIdle());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
     
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }


        /**
         * 集群模式的 redisson 客户端
         *
         * @return
         */
        @Bean
        @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "cluster")
        RedissonClient redissonCluster() {
     
            System.out.println("cluster redisProperties:" + redisProperties.getCluster());

            Config config = new Config();
            String[] nodes = redisProperties.getCluster().getNodes().split(",");
            List<String> newNodes = new ArrayList(nodes.length);
            Arrays.stream(nodes).forEach((index) -> newNodes.add(
                    index.startsWith("redis://") ? index : "redis://" + index));

            ClusterServersConfig serverConfig = config.useClusterServers()
                    .addNodeAddress(newNodes.toArray(new String[0]))
                    .setScanInterval(
                            redisProperties.getCluster().getScanInterval())
                    .setIdleConnectionTimeout(
                            redisProperties.getPool().getSoTimeout())
                    .setConnectTimeout(
                            redisProperties.getPool().getConnTimeout())
                    .setFailedAttempts(
                            redisProperties.getCluster().getFailedAttempts())
                    .setRetryAttempts(
                            redisProperties.getCluster().getRetryAttempts())
                    .setRetryInterval(
                            redisProperties.getCluster().getRetryInterval())
                    .setMasterConnectionPoolSize(redisProperties.getCluster()
                            .getMasterConnectionPoolSize())
                    .setSlaveConnectionPoolSize(redisProperties.getCluster()
                            .getSlaveConnectionPoolSize())
                    .setTimeout(redisProperties.getTimeout());
            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
     
                serverConfig.setPassword(redisProperties.getPassword());
            }
            return Redisson.create(config);
        }

       /**  
         * 哨兵模式 redisson 客户端
         * @return
         */

        @Bean
        @ConditionalOnProperty(name = "spring.redis.mode", havingValue = "sentinel")
        RedissonClient redissonSentinel() {
     
            System.out.println("sentinel redisProperties:" + redisProperties.getSentinel());
            Config config = new Config();
            String[] nodes = redisProperties.getSentinel().getNodes().split(",");
            List<String> newNodes = new ArrayList(nodes.length);
            Arrays.stream(nodes).forEach((index) -> newNodes.add(
                    index.startsWith("redis://") ? index : "redis://" + index));

            SentinelServersConfig serverConfig = config.useSentinelServers()
                    .addSentinelAddress(newNodes.toArray(new String[0]))
                    .setMasterName(redisProperties.getSentinel().getMaster())
                    .setReadMode(ReadMode.SLAVE)
                    .setFailedAttempts(redisProperties.getSentinel().getFailMax())
                    .setTimeout(redisProperties.getTimeout())
                    .setMasterConnectionPoolSize(redisProperties.getPool().getSize())
                    .setSlaveConnectionPoolSize(redisProperties.getPool().getSize());

            if (StringUtils.isNotBlank(redisProperties.getPassword())) {
     
                serverConfig.setPassword(redisProperties.getPassword());
            }

            return Redisson.create(config);
        }
    }
}

使用时候直接注入RedissClient客户端就可以使用,如下这样写的目的是为了统一给其它服务提供接口

 @Autowired
    RedissonClient redisson;

    @RequestMapping(value = "lock", method = RequestMethod.POST, consumes = "application/json;charset=UTF-8", produces = "application/json;charset=UTF-8")
    public @ResponseBody
    ServerResponse<RLock> lock(@RequestBody ServerRequest<LockReqBody> req) {
     
        return callR(redisson -> {
     
            RLock lock = redisson.getLock(req.getReqBody().getLockKey());
            lock.lock(req.getReqBody().getTimeout(), req.getReqBody().getUnit());
            return lock;
        });
    }
    //省略部分代码
    private <R> ServerResponse callR(Function<RedissonClient, R> function) {
     
        ServerResponse dv = RespHelper.serverResponse(RespCodeService.ERROR, "");
        try {
     
            long startTime = System.currentTimeMillis();
            dv = RespHelper.serverResponse(RespCodeService.SUCCESS, function.apply(redisson));
            logger.info("CALLR METHOD USE TIME:{}", System.currentTimeMillis() - startTime);
        } catch (Throwable e) {
               
            System.out.println("callR error");
        }
        return dv;
    }

你可能感兴趣的:(redis,java,学习,java,redis,jedis,分布式)