利用redis(spring-data-redis)锁的功能来实现定时器的分布式

看到这样一篇文章如下

以前为部门内部开发过一个定时器程序,这个定时器很简单,就是配置quartz,来实现定时调用配置的url功能。最近为了防止定时器所在的服务器由于特殊原因挂掉,需要对定时器做多机部署。那么如果按照原来的方式进行部署,就会遇到 在一定的间隔时间内,可能出现多次重复调用的问题。为了解决这个问题,我就借助了redis的分布式锁功能。

        redis分布式锁参考 : http://www.jeffkit.info/2011/07/1000/

        具体原理如下:

        定时器到时间被触发,程序开始先争取一个redis锁。

        如果获得锁,就设置锁的超时时间为到下次定时器触发的时间。

        然后执行定时器任务。后来的定时器也来尝试获得redis锁,当然,这个锁已不能获取了,而且超时时间在未来,所以就放弃这次任务调用。

        定时器到时间再次被触发,然后尝试获得锁,由于锁的超时时间为定时任务的时间间隔,当前时间正好大于或等于超时时间,所以,程序可以顺利的获得锁,并重置超时时间。

        。。。。。。。不断的循环调用,判断

 

        在此之间测试循环间隔时间最小单位为1s最好,如果小于1s的调用,由于使用redis会有10几毫秒的运算耗费,因此不能保证在1s以下的时间间隔比较均匀.

        为了能保证定时触发时,能获得redis锁,可以设置锁的超时时间为间隔时间-10ms。这样就判断超时时间 now > timeoutTime = true。

       补充:

       只要多个服务器时间差别不大,基本不会有重复的问题。唯一担心的就是redis,这个挂了,就全挂了。      

       因此,如果要考虑更全面,需要对redis点单再做集群。就看是否有必要了。


我也遇到了相同问题:

     公司两台服务器部署应用,然后使用redis锁,不过使用的是spring-data-redis的哈希表的操作。

//加锁
   public boolean addJobLock(final String jobName) {
        return getRedisTemplate().execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                String newJobName = JOB_PREFIX + jobName;
                final String key = "ALL" + ID_SPLIT + "ALL";
                if (((Jedis) connection.getNativeConnection()).hlen(newJobName) == 0L) {

                    final String keyValue = String.valueOf(System.currentTimeMillis());
                    return JedisUtil.isSuccess(((Jedis) connection.getNativeConnection()).hset(newJobName, key,
                            keyValue));
                } else {

                    // 如果存在key,则先取出来,然后看里面的值

                    long keyTime = getHKeyLong(newJobName, key);
                    /**
                     * 如果超时,并重新插入
                     */
                    if ((keyTime + DEFAULT_TIMEOUT) < System.currentTimeMillis()) {

                        final String keyValue = String.valueOf(System.currentTimeMillis());
                        ((Jedis) connection.getNativeConnection()).hset(newJobName, key, keyValue);
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        });
    }

<pre name="code" class="java">   /**
     * 获取Job的有效期
     */
    private long getHKeyLong(final String jobName, String key) {
        String keyValue = getRedisTemplate().execute(new RedisCallback<String>() {
            @Override
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                String newJobName = JOB_PREFIX + jobName;
                final String key = "ALL" + ID_SPLIT + "ALL";

                return ((Jedis) connection.getNativeConnection()).hget(newJobName, key);

            }
        });

        if (StringUtils.isNotBlank(keyValue)) {
            return Long.parseLong(keyValue);
        } else {
            return 0L;
        }
    }

//释放锁
   public boolean releaseJobLock(final String jobName) {
        return getRedisTemplate().execute(new RedisCallback<Boolean>() {
            String newJobName = JOB_PREFIX + jobName;
            final String key = "ALL" + ID_SPLIT + "ALL";

            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return JedisUtil.isSuccess(((Jedis) connection.getNativeConnection()).hdel(newJobName, key));
            }
        });

   }


 分别在业务开始加锁和释放锁就可以了。 


spring下spring-data-redis配置:

    <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <property name="maxActive" value="${redis.maxActive}"/>
    </bean>

    <bean id="jedisConnectionFactory"
          class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="usePool" value="true"/>
        <property name="hostName" value="${redis.hostName}"/>
        <property name="port" value="${redis.port}"/>
        <property name="database" value="${redis.dbIndex}"/>
        <property name="poolConfig" ref="jedisPoolConfig"/>
        <property name="timeout" value="${redis.timeout}"/>
    </bean>

    <!-- redis template definition -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
        <property name="connectionFactory" ref="jedisConnectionFactory"/>
    </bean>



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值