redis连接拥塞。我不知道这样说对不对?

问题:

在实际项目中,我采用redis做资源锁。获取锁的线程才允许执行。然后再最后释放锁的时候。线程堵塞了16分钟后才能释放资源。

尝试解决:

我试着加大redispool的数量也无法解决。然后又查看了jedispool 的部分源码。查看默认属性中无法找到 16 分钟 或 960 秒 或 960000 毫秒 965965000的配置。

我释放资源的地方

try {
    xxxxxxxxxxx
}
} catch (Exception e) {
} finally {
    //释放锁
    log.info(targetPar + "." + methodPar + ":释放资源锁");
    if (StringUtil.isNotEmpty(userId)) {
        redisLock.unLock(userId);
    }
}

加锁的代码

/**
     * 
     * @Description 
     * @author lxlong
     * @param lockKey 键
     * @param expiretime 超时时间,默认为 30*1000 (单为毫秒)
     * @return
     * @throws LockOccupiedException 加锁失败
     */
    public boolean lockKey(String lockKey, Integer expiretime) throws LockOccupiedException {
        //添加锁前缀,避免相同key 被误删。
        key = LOCK_PREFIX.concat(lockKey);    
        //设置默认超时时间
        if(expiretime == null || expiretime <= 0){
            expiretime = defaultExpireTime;
        }
        String expiresStr = String.valueOf(System.currentTimeMillis() + expiretime);
        if(redisService.setNX(key, expiresStr)){
            log.info("---------------" + key + "加锁成功-------------------");
            myExpireTime = expiresStr;
            isLock = true;
            return true;
        }
        String currentValueStr = redisService.get(key); //redis里的时间
        if (currentValueStr != null && Long.parseLong(currentValueStr) < System.currentTimeMillis()) {
            log.info("===================前一锁已超时========================");
            //判断是否为空,不为空的情况下,如果被其他线程设置了值,则第二个条件判断是过不去的
            //锁超时,设置新锁
            String oldValueStr = redisService.getAndSet(key, expiresStr);
            //获取上一个锁到期时间,并设置现在的锁到期时间,
            //只有一个线程才能获取上一个线上的设置时间,因为jedis.getSet是同步的
            if (oldValueStr != null && oldValueStr.equals(currentValueStr)) {
                myExpireTime = expiresStr;
                isLock = true;
                log.info("---------------" + key + "加锁成功-------------------");
                return true;
            }
        }
        throw new LockOccupiedException();
    }

释放资源的代码如下

    /**
     * 释放锁
     * @Description 
     * @author lxlong
     * @param lockKey
     */
    public void unLock(String lockKey) {
        if(isLock){
            lockKey = LOCK_PREFIX.concat(lockKey);
            String expiresStr = redisService.get(lockKey);
            //判断是否超时,若时间值不一致说明锁已被其他线程获取,不用释放
            if(expiresStr.equals(myExpireTime)){
                redisService.delete(lockKey);
                log.info("---------------" + lockKey + "释放成功-------------------");
            } else {
                log.info("---------------" + lockKey + "已被其他线程获取/未获取到锁-------------------");
            }
        }
    }
    
    /**
     * 删除 (non-Javadoc)
     * 
     * @see com.leyizhu.dao.redis.RedisDao#delete(java.lang.String)
     */
    public boolean delete(final String key) {
        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                RedisSerializer<String> serializer = getRedisSerializer();
                byte[] jkey = serializer.serialize(key);
                if (connection.exists(jkey)) {
                    connection.del(jkey);
                    return true;
                } else {
                    return false;
                }
            }
        });
        return result;
    }    

实际中出现的问题如下:

[INFO] 2019-01-23 06:20:32 ---> ---------------LOCK_PREFIX_7170e8b3ed2144b48bf9ac8bebd29733加锁成功-------------------
[INFO] 2019-01-23 06:20:32 ---> =====================用户签到=====================
[INFO] 2019-01-23 06:20:32 ---> ------------------sign-----end
[INFO] 2019-01-23 06:20:32 ---> apiUserInfoTokenControl.sign:释放资源锁
[INFO] 2019-01-23 06:36:37 ---> ---------------LOCK_PREFIX_7170e8b3ed2144b48bf9ac8bebd29733释放成功-------------------

apiUserInfoTokenControl.sign:释放资源锁

-LOCK_PREFIX_7170e8b3ed2144b48bf9ac8bebd29733释放成功
之间时间差为 16分05秒965 millisecond。这段时间不知到发生了什么?
而且这段时间之内,对其他线程没有影响,可以正常操作redis。
这种问题基本上2-3天出现一次,没有规律可循。

环境配置

  1. 服务器配置
CPU: 2核
内存: 16 GB
实例类型: I/O优化
操作系统: Aliyun Linux 17.1 64位
  1. redis配置

redis 4.0

redis.host=127.0.0.1
redis.port=6379
redis.pass=123456
redis.minIdle=5
redis.maxIdle=10
redis.maxTotal=50
redis.timeout=2000
redis.maxWait=6000
redis.testOnBorrow=true

spring-redis.xml

<bean id="redisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">  
    <property name="connectionFactory"   ref="connectionFactory" />  
</bean>
<!-- Spring-redis连接池管理工厂 -->
<bean id="connectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redis.host}" />
    <property name="port" value="${redis.port}" />
    <property name="password" value="${redis.pass}" />
    <property name="timeout" value="${redis.timeout}" />
    <property name="poolConfig" ref="poolConfig" />
    <property name="usePool" value="true"/>
</bean>
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxTotal" value="${redis.maxTotal}" />
    <property name="minIdle" value="${redis.minIdle}" />
    <property name="maxIdle" value="${redis.maxIdle}" />
    <property name="maxWaitMillis" value="${redis.maxWait}" />
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

然后现在我删除key的方法改了一下下 然后效果还是一样。删除的时候会拥塞16分钟零4秒

/**
     * 删除 (non-Javadoc)
     * 
     * @see com.leyizhu.dao.redis.RedisDao#delete(java.lang.String)
     */
    public boolean delete(final String key) {
        redisTemplate.delete(key);
        return true;
//        boolean result = redisTemplate.execute(new RedisCallback<Boolean>() {
//            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
//                RedisSerializer<String> serializer = getRedisSerializer();
//                byte[] jkey = serializer.serialize(key);
//                if (connection.exists(jkey)) {
//                    connection.del(jkey);
//                    return true;
//                } else {
//                    return false;
//                }
//            }
//        });
//        return result;
    }

这是现在的日志,我在删除之前加了一段日志 准备释放 XXXXXXX 。用于确定是删除的时候拥塞的

[INFO] 2019-01-29 04:00:48 ---> ---------------LOCK_PREFIX_c3372edaae224b409ce87684d6abc125加锁成功-------------------
[INFO] 2019-01-29 04:00:48 ---> -----------------------------准备释放LOCK_PREFIX_c3372edaae224b409ce87684d6abc125
[INFO] 2019-01-29 04:16:52 ---> ---------------LOCK_PREFIX_c3372edaae224b409ce87684d6abc125释放成功-------------------

最近发现只要有调用redis删除key的地方都有可能出现拥塞。拥塞时间也是在16分钟左右。基本上可确定是 redis.delete(key) 这一块的问题。但是还是不知道具体原因,以及解决办法。

阅读 1.7k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题