优先使用Lettuce,
需要分布式锁,分布式集合等分布式的高级特性,添加Redisson结合使用。
对于高并发,1000/s的并发,数据库可能由行锁变成表锁,性能下降会厉害。
老牌Redis的Java客户端,提供比较全面的Redis命令的支持,
使用阻塞的I/O,方法调用都是同步的,程序流需要等到sockets处理完I/O才能执行,不支持异步。
Jedis客户端实例不是线程安全的,使直接连接redis server,需要通过连接池来使用Jedis,为每个jedis实例增加物理连接。
SpringBoot2之后,默认就采用了lettuce。
高级Redis客户端,基于Netty框架的事件驱动的通信层,用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器。
Lettuce的API是线程安全的,可以操作单个Lettuce连接来完成各种操作,连接实例(StatefulRedisConnection)可在多个线程间并发访问。
基于Netty框架的事件驱动的通信层,方法是异步的,API线程安全,可操作单个Redisson连接来完成各种操作。
实现了分布式和可扩展的Java数据结构,不支持字符串操作,不支持排序、事务、管道、分区等Redis特性。
提供很多分布式相关操作服务,如,分布式锁,分布式集合,可通过 Redis支持延迟队列。
org.springframework.boot spring-boot-starter-data-redisorg.apache.commons commons-pool2org.redisson redisson3.23.2
####################redis连接配置############ redis: # cluster: # nodes: # - 127.0.0.1:7001 # - 127.0.0.1:7002 # - 127.0.0.1:7003 # host: 127.0.0.1 port: 6379 password: 123456 database: 0 timeout: 2000ms lettuce: pool: # 连接池最大连接数 max-active: 20 # 连接池中的最小空闲连接 max-idle: 10 # 连接池最大阻塞等待时间(使用负数表示没有限制,单位ms) max-wait: 3000
@Configuration public class RedisConfig { /** * 创建 RedisTemplate,注入IOC容器 */ @Bean public RedisTemplateredisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); // 设置数据源的连接工厂(默认会传入框架中自带的(也就是读取完配置文件装配的)LettuceConnectionFactory) // 也可以自己定义,注入容器,再通过@Qualifier("")传进来 template.setConnectionFactory(factory); //设置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class)); // hash的key也采用String的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class)); return template; } }
@Component public class RedisClient { @Autowired private RedisTemplateredisTemplate; /** * 指定缓存失效时间 * @param key 键 * @param time 时间(秒) */ public boolean expire(String key,long time){ try { if(time>0){ redisTemplate.expire(key, time, TimeUnit.SECONDS); } return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 根据key 获取过期时间 * @param key 键 不能为null * @return 时间(秒) 返回0代表为永久有效 */ public long ttl(String key){ return redisTemplate.getExpire(key,TimeUnit.SECONDS); } //============================String============================= /** * 普通缓存获取 * @param key 键 * @return 值 */ public Object get(String key){ return key==null?null:redisTemplate.opsForValue().get(key); } /** * 普通缓存放入 * @param key 键 * @param value 值 * @return true成功 false失败 */ public boolean set(String key,Object value) { try { redisTemplate.opsForValue().set(key, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除缓存 * @param key 可以传一个值 或多个 */ public Boolean del(String key){ return redisTemplate.delete(key); } //================================hash================================= /** * HashGet * @param key 键 不能为null * @param item 项 不能为null */ public Object hget(String key,String item){ return redisTemplate.opsForHash().get(key, item); } /** * 向 hash 表中放入数据,如果不存在将创建 * @param key 键 * @param item 项 * @param value 值 * @return true 成功 false失败 */ public boolean hset(String key,String item,Object value) { try { redisTemplate.opsForHash().put(key, item, value); return true; } catch (Exception e) { e.printStackTrace(); return false; } } /** * 删除hash表中的值 * @param key 键 不能为null * @param item 项 可以使多个 不能为null */ public void hdel(String key, Object... item){ redisTemplate.opsForHash().delete(key,item); } //============================set============================= /** * 根据key获取Set中的所有值 * @param key 键 */ public Set
Redisson官方文档: https://github.com/redisson/redisson/wiki
@Configuration public class RedisConfig { // 锁前缀 private static final String SCHEMA_PREFIX = "redis://"; // 超时时间 private final long lockWatchTimeOut = 3000; /** * 创建 RedisTemplate,注入IOC容器 */ @Bean public RedisTemplateredisTemplate(RedisConnectionFactory factory) { RedisTemplate template = new RedisTemplate<>(); // 设置数据源的连接工厂(默认会传入框架中自带的(也就是读取完配置文件装配的)LettuceConnectionFactory) // 也可以自己定义,注入容器,再通过@Qualifier("")传进来 template.setConnectionFactory(factory); //设置key的序列化器 template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class)); // hash的key也采用String的序列化方式 template.setHashKeySerializer(new StringRedisSerializer()); template.setHashValueSerializer(new Jackson2JsonRedisSerializer(Object.class)); return template; } /** * 创建 RedissonClient,注入IOC容器 */ @Bean public RedissonClient redissonClient(RedisProperties redisProperties) { Config config = new Config(); RedisProperties.Sentinel sentinel = redisProperties.getSentinel(); RedisProperties.Cluster redisPropertiesCluster = redisProperties.getCluster(); if (redisPropertiesCluster != null) { //集群redis ClusterServersConfig clusterServersConfig = config.useClusterServers(); for (String cluster : redisPropertiesCluster.getNodes()) { clusterServersConfig.addNodeAddress(SCHEMA_PREFIX + cluster); } if (StringUtils.hasText(redisProperties.getPassword())) { clusterServersConfig.setPassword(redisProperties.getPassword()); } clusterServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis()); clusterServersConfig.setPingConnectionInterval(30000); } else if (StringUtils.hasText(redisProperties.getHost())) { //单点redis SingleServerConfig singleServerConfig = config.useSingleServer(). setAddress(SCHEMA_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort()); if (StringUtils.hasText(redisProperties.getPassword())) { singleServerConfig.setPassword(redisProperties.getPassword()); } singleServerConfig.setTimeout((int) redisProperties.getTimeout().toMillis()); singleServerConfig.setPingConnectionInterval(30000); singleServerConfig.setDatabase(redisProperties.getDatabase()); } else if (sentinel != null) { //哨兵模式 SentinelServersConfig sentinelServersConfig = config.useSentinelServers(); sentinelServersConfig.setMasterName(sentinel.getMaster()); for (String node : sentinel.getNodes()) { sentinelServersConfig.addSentinelAddress(SCHEMA_PREFIX + node); } if (StringUtils.hasText(redisProperties.getPassword())) { sentinelServersConfig.setPassword(redisProperties.getPassword()); } sentinelServersConfig.setTimeout((int) redisProperties.getTimeout().toMillis()); sentinelServersConfig.setPingConnectionInterval(30000); sentinelServersConfig.setDatabase(redisProperties.getDatabase()); } config.setLockWatchdogTimeout(lockWatchTimeOut); return Redisson.create(config); } }
Redisson续期机制—看门狗机制:
1.启动定时任务重新给锁设置过期时间,默认过期时间是 30 秒,每 10 秒(默认事件的1/3)续期一次(补到 30 秒)
2.如果线程挂掉(服务器宕机),则不会续期。
3.只有lock.lock(); 会有看门狗机制;
4.lock.lock(10,,TimeUnit.SECONDS);手动设置过期时间的话,则不会有看门狗机制。
/** * 分布式Redis锁 */ @Slf4j public class DistributedRedisLock { @Autowired private RedissonClient redissonClient; // 加锁 public Boolean lock(String lockName) { if (redissonClient == null) { log.info("DistributedRedisLock redissonClient is null"); return false; } try { RLock lock = redissonClient.getLock(lockName); // 锁15秒后自动释放,防止死锁 lock.lock(15, TimeUnit.SECONDS); // 加锁成功 return true; } catch (Exception e) { e.printStackTrace(); return false; } } // 释放锁 public Boolean unlock(String lockName) { if (redissonClient == null) { log.info("DistributedRedisLock redissonClient is null"); return false; } try { RLock lock = redissonClient.getLock(lockName); lock.unlock(); // 释放锁成功 return true; } catch (Exception e) { e.printStackTrace(); return false; } } }
@Autowired RedissonClient redisson; @Autowired RedisTemplate redisTemplate; @ResponseBody @GetMapping("/write") public String writeValue(){ RReadWriteLock lock = redisson.getReadWriteLock("rw-lock"); RLock rLock = lock.writeLock(); String s = ""; try { s = UUID.randomUUID().toString(); // 模拟业务时间 Thread.sleep(30000); } catch (Exception e){ e.printStackTrace(); }finally { rLock.unlock(); } redisTemplate.opsForValue().set("writeValue",s); return s; } @GetMapping(value = "/read") @ResponseBody public String readValue() { String s = ""; RReadWriteLock readWriteLock = redisson.getReadWriteLock("rw-lock"); //加读锁 RLock rLock = readWriteLock.readLock(); try { rLock.lock(); s = (String) redisTemplate.opsForValue().get("writeValue"); TimeUnit.SECONDS.sleep(10); } catch (Exception e) { e.printStackTrace(); } finally { rLock.unlock(); } return s; }
@GetMapping(value = "/lockDoor") @ResponseBody public String lockDoor() throws InterruptedException { RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor"); lockDoor.trySetCount(5); // 设置计数为5 lockDoor.await(); //等待闭锁完成 return "放假啦..."; } @GetMapping(value = "/go/{id}") public String go(@PathVariable("id") Integer id) { RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor"); lockDoor.countDown(); // 计数减1 return id+"班都走光了"; }
@GetMapping(value = "/park") @ResponseBody public String park() { RSemaphore park = redisson.getSemaphore("park"); try { park.acquire();// 获取一个信号量(redis中信号量值-1),如果redis中信号量为0了,则在这里阻塞住,直到信号量大于0,可以拿到信号量,才会继续执行。 } catch (InterruptedException e) { e.printStackTrace(); } return "ok"; } @GetMapping(value = "/go") @ResponseBody public String go() { RSemaphore park = redisson.getSemaphore("park"); park.release(); //释放一个信号量(redis中信号量值+1) return "ok"; }