场景:用户登录状态存储到redis,2小时后过期。在过期前的30分钟如果用户进行操作,则对登录状态进行续期,续期后仍有2小时时限,并更换新的token。在微服务模式下,如果两个服务同时请求续期,则会返回两个不同的token,其中一个是无效的,这会导致用户登出。加锁以避免这个问题。
java"> private final StringRedisTemplate redisTemplate; private static final Long SUCCESS = 1L;/*** 锁的超时时间 10s*/private static final long TIMEOUT = 9999;/*** 加锁,无阻塞** @param* @param* @return*/public Boolean tryLock(String key, String value, long expireTime) {long start = System.currentTimeMillis();for (; ; ) {//SET命令返回OK ,则证明获取锁成功Boolean ret = redisTemplate.opsForValue().setIfAbsent(key, value, expireTime, TimeUnit.SECONDS);if (ret) {return true;}//防止执行过快try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}//否则循环等待,在timeout时间内仍未获取到锁,则获取失败long wait = System.currentTimeMillis() - start;if (wait >= TIMEOUT) {return false;}}}/*** 解锁(如果get(key)==value则删除key否则什么都不做)** @param* @param* @return*/public Boolean unlock(String key, String value) {String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";RedisScript<Long> redisScript = new DefaultRedisScript<>(script, Long.class);Long execute = redisTemplate.execute(redisScript, Collections.singletonList(key), value);if (SUCCESS.equals(execute)) {return true;}return false;}
java"> /*** 使用分布式锁续期token** @param response* @param userName 用户名 唯一* @param userDTO 业务参数* @param token* @param client 业务参数*/private void syncRenewalToken(HttpServletResponse response, String userName, UserDTO userDTO, String token, int client) {log.info(userDTO.getId() + " token续期");String uuid = UUID.randomUUID().toString().replaceAll("-", "");redisTemplate.tryLock("lock:" + userName, uuid, properties.getLockSurvive());//中间是对token进行续期的逻辑redisTemplate.unlock("lock:" + userName, uuid);}