cache.service.impl.JedisServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for cache.service.impl.JedisServiceImpl.java

Source

/*
 * Copyright 2002-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cache.service.impl;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import redis.client.jedis.CustomShardedJedisPool;
import redis.client.util.AssertUtils;
import redis.clients.jedis.Response;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPipeline;
import redis.clients.jedis.exceptions.JedisException;
import cache.service.RedisService;

/**
 * "?Jedis"?{@link RedisService}
 * 
 * @author huagang.li 20141212 ?4:59:38
 */
@Resource
public class JedisServiceImpl implements RedisService {

    private static final Logger logger = LoggerFactory.getLogger(JedisServiceImpl.class);

    /** Redis */
    @Autowired
    private CustomShardedJedisPool shardedJedisPool;

    /** Redis?? */
    private boolean enabled;

    /**  */
    private final ExecutorService executorService = new ThreadPoolExecutor(30, 10000, 60L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<Runnable>(50));

    /**
     * ?(UT, Unit Test)
     * 
     * @param shardedJedisPool
     */
    public void setShardedJedisPool(CustomShardedJedisPool shardedJedisPool) {
        this.shardedJedisPool = shardedJedisPool;
    }

    @Override
    public void setEnabled(boolean enabled) {
        this.enabled = enabled;
    }

    @Override
    public boolean getEnabled() {
        return enabled;
    }

    /**
     * Redis
     */
    @Override
    public void close() {
        if (shardedJedisPool != null) {
            shardedJedisPool.close();
        }
    }

    // ---------------- internal help method ----------------
    private static void notEmptyKey(String key) {
        AssertUtils.notEmpty(key, "'key' must not be null and empty");
    }

    /**
     * ?"Jedis"""
     * 
     * @param jedis
     */
    private static void close(ShardedJedis jedis) {
        if (jedis != null) {
            try {
                jedis.close();
            } catch (JedisException e) {
                logger.error("ShardedJedis close fail", e);
            }
        }
    }

    // ---------------- Key () ----------------
    @Override
    public int expire(String key, int seconds) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int ret = jedis.expire(key, seconds).intValue();
                return ret;
            } catch (JedisException e) {
                logger.error("'expire' key fail, key: {}, seconds: {}", key, seconds);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public long ttl(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                long liveTimeSeconds = jedis.ttl(key).longValue();
                return liveTimeSeconds;
            } catch (JedisException e) {
                logger.error("'ttl' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return -2L;
    }

    @Override
    public int del(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int removedKeyNum = jedis.del(key).intValue();
                return removedKeyNum;
            } catch (JedisException e) {
                logger.error("'del' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    // ---------------- String () ----------------
    @Override
    public String get(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                String value = jedis.get(key);
                return value;
            } catch (JedisException e) {
                logger.error("'get' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return null;
    }

    @Override
    public String set(String key, String value) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                String ret = jedis.set(key, value);
                return ret;
            } catch (JedisException e) {
                logger.error("'set' key fail, key: {}, value: {}", key, value);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return null;
    }

    @Override
    public String setex(String key, int seconds, String value) {
        if (enabled) {
            notEmptyKey(key);

            if (seconds > 0) {
                ShardedJedis jedis = null;
                try {
                    jedis = shardedJedisPool.getResource();
                    String ret = jedis.setex(key, seconds, value);
                    return ret;
                } catch (JedisException e) {
                    logger.error("'setex' key fail, key: {}, seconds: {}, value: {}", key, seconds, value);
                    logger.error(e.getMessage(), e);
                } finally {
                    close(jedis);
                }
            } // seconds???(<= 0)? ("JedisDataException: ERR invalid expire time in setex")??
        }
        return null;
    }

    // ---------------- List () ----------------
    @Override
    public int llen(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int listLength = jedis.llen(key).intValue();
                return listLength;
            } catch (JedisException e) {
                logger.error("'llen' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public int lpush(String key, String... values) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int pushedListLength = jedis.lpush(key, values).intValue();
                return pushedListLength;
            } catch (JedisException e) {
                logger.error("'lpush' key fail, key: {}, values: {}", key, Arrays.toString(values));
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public String rpop(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                String value = jedis.rpop(key);
                return value;
            } catch (JedisException e) {
                logger.error("'rpop' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return null;
    }

    @Override
    public List<String> lrange(String key, int start, int stop) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                List<String> list = jedis.lrange(key, start, stop);
                return list;
            } catch (JedisException e) {
                logger.error("'lrange' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptyList();
    }

    @Override
    public String ltrim(String key, int start, int stop) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                String ret = jedis.ltrim(key, start, stop);
                return ret;
            } catch (JedisException e) {
                logger.error("'ltrim' key fail, key: {}, start: {}, stop: {}", key, start, stop);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return null;
    }

    // ---------------- Sorted Set (??) ----------------
    @Override
    public int zadd(String key, double score, String member) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int newElementNum = jedis.zadd(key, score, member).intValue();
                return newElementNum;
            } catch (JedisException e) {
                logger.error("'zadd' key fail, key: {}, score: {}, member: {}", key, score, member);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public int zaddAndRem(String key, double score, String member) {
        return this.zaddAndRem(key, score, member, DEFAULT_MAX_LENGTH);
    }

    @Override
    public int zaddAndRem(String key, double score, String member, int maxLength) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                int newElementNum = 0;
                int elementNum = 0;

                jedis = shardedJedisPool.getResource();
                ShardedJedisPipeline pipeline = jedis.pipelined();
                Response<Long> zaddResponse = pipeline.zadd(key, score, member);
                Response<Long> zcardResponse = pipeline.zcard(key);
                pipeline.sync();

                Long zaddRes = zaddResponse.get();
                if (zaddRes != null) {
                    newElementNum = zaddRes.intValue();
                }
                Long zcardRes = zcardResponse.get();
                if (zcardRes != null) {
                    elementNum = zcardRes.intValue();
                }
                if (newElementNum > 0 && elementNum > maxLength) {
                    this.asynShrinkZset(key, elementNum, maxLength);
                }

                return newElementNum;
            } catch (JedisException e) {
                logger.error("'zaddAndRem' key fail, key: {}, score: {}, member: {}, maxLength: {}", key, score,
                        member, maxLength);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    // ?
    @Override
    public int zadd(String key, Map<String, Double> scoreMembers) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int newElementNum = jedis.zadd(key, scoreMembers).intValue();
                return newElementNum;
            } catch (JedisException e) {
                logger.error("'zadd' key fail, key: {}, scoreMembers: {}", key, scoreMembers);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public int zaddAndRem(String key, Map<String, Double> scoreMembers) {
        return this.zaddAndRem(key, scoreMembers, DEFAULT_MAX_LENGTH);
    }

    @Override
    public int zaddAndRem(String key, Map<String, Double> scoreMembers, int maxLength) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                int newElementNum = 0;
                int elementNum = 0;

                jedis = shardedJedisPool.getResource();
                ShardedJedisPipeline pipeline = jedis.pipelined();
                Response<Long> zaddResponse = pipeline.zadd(key, scoreMembers);
                Response<Long> zcardResponse = pipeline.zcard(key);
                pipeline.sync();

                Long zaddRes = zaddResponse.get();
                if (zaddRes != null) {
                    newElementNum = zaddRes.intValue();
                }
                Long zcardRes = zcardResponse.get();
                if (zcardRes != null) {
                    elementNum = zcardRes.intValue();
                }
                if (newElementNum > 0 && elementNum > maxLength) {
                    this.asynShrinkZset(key, elementNum, maxLength);
                }

                return newElementNum;
            } catch (JedisException e) {
                logger.error("'zaddAndRem' key fail, key: {}, scoreMembers: {}, maxLength: {}", key, scoreMembers,
                        maxLength);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    /**
     * ?"??"({@code maxLength} + {@link RedisService#LENGTH_THRESHOLD})?""?
     * 
     * @param key 
     * @param maxLength ??
     */
    private void asynShrinkZset(String key, int elementNum, int maxLength) {
        if (elementNum >= maxLength + LENGTH_THRESHOLD) {
            executorService.submit(new ZremrangeByRankRunnable(this, key, elementNum, maxLength));
        }
    }

    /**
     *  zremrangeByRank ""?
     */
    private class ZremrangeByRankRunnable implements Runnable {

        private final RedisService redisService;
        private final String key;
        private final int elementNum;
        private final int maxLength;

        public ZremrangeByRankRunnable(RedisService redisService, String key, int elementNum, int maxLength) {
            this.redisService = redisService;
            this.key = key;
            this.elementNum = elementNum;
            this.maxLength = maxLength;
        }

        /**
         * "??"""?
         */
        @Override
        public void run() {
            long startTime = System.currentTimeMillis();

            int stop = elementNum - maxLength - 1;
            int removedElementNum = redisService.zremrangeByRank(key, 0, stop);
            if (removedElementNum != elementNum - maxLength) {
                logger.warn("Failed to 'zremrangeByRank' of key: {}, elementNum: {}, maxLength: {}", key,
                        elementNum, maxLength);
            }

            long runTime = System.currentTimeMillis() - startTime;
            logger.debug("'zremrangeByRank' of Sorted Set key: {}, removed number: {}, time: {}", key,
                    removedElementNum, runTime);
        }

    }

    @Override
    public Set<String> zrange(String key, int start, int stop) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrange(key, start, stop);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrange' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public Set<String> zrevrange(String key, int start, int stop) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrevrange(key, start, stop);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrevrange' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public Set<String> zrangeByScore(String key, double min, double max) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrangeByScore(key, min, max);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrangeByScore' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public Set<String> zrangeByScore(String key, double min, double max, int offset, int count) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrangeByScore(key, min, max, offset, count);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrangeByScore' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public Set<String> zrevrangeByScore(String key, double max, double min) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrevrangeByScore(key, max, min);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrevrangeByScore' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public Set<String> zrevrangeByScore(String key, double max, double min, int offset, int count) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                Set<String> zset = jedis.zrevrangeByScore(key, max, min, offset, count);
                return zset;
            } catch (JedisException e) {
                logger.error("'zrevrangeByScore' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return Collections.emptySet();
    }

    @Override
    public int zcard(String key) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int zsetElementNum = jedis.zcard(key).intValue();
                return zsetElementNum;
            } catch (JedisException e) {
                logger.error("'zcard' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public int zremrangeByScore(String key, double min, double max) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int removedElementNum = jedis.zremrangeByScore(key, min, max).intValue();
                return removedElementNum;
            } catch (JedisException e) {
                logger.error("'zremrangeByScore' key fail, key: {}, min: {}, max: {}", key, min, max);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public int zremrangeByRank(String key, int start, int stop) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis jedis = null;
            try {
                jedis = shardedJedisPool.getResource();
                int removedElementNum = jedis.zremrangeByRank(key, start, stop).intValue();
                return removedElementNum;
            } catch (JedisException e) {
                logger.error("'zremrangeByRank' key fail, key: {}, start: {}, stop: {}", key, start, stop);
                logger.error(e.getMessage(), e);
            } finally {
                close(jedis);
            }
        }
        return 0;
    }

    @Override
    public String info(String key, String section) {
        if (enabled) {
            notEmptyKey(key);

            ShardedJedis shardedJedis = null;
            try {
                shardedJedis = shardedJedisPool.getResource();
                String info = shardedJedis.getShard(key).info(section);
                return info;
            } catch (JedisException e) {
                logger.error("'info' key fail, key: {}", key);
                logger.error(e.getMessage(), e);
            } finally {
                close(shardedJedis);
            }
        }
        return "";
    }

}