利用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>



發佈了17 篇原創文章 · 獲贊 8 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章