package cn.dreampie.cache.redis; import cn.dreampie.cache.CacheEvent; import cn.dreampie.cache.CacheProvider; import cn.dreampie.common.Constant; import cn.dreampie.common.util.properties.Prop; import cn.dreampie.common.util.properties.Proper; import cn.dreampie.common.util.serialize.Serializer; import cn.dreampie.log.Logger; import org.apache.commons.pool2.impl.BaseObjectPoolConfig; import redis.clients.jedis.*; import redis.clients.util.Pool; import java.util.*; /** * Created by Dreampie on 15/4/24. */ public class RedisProvider extends CacheProvider { private static final Logger logger = Logger.getLogger(RedisProvider.class); private static final int PAGE_SIZE = 128; private static final Pool pool; private static final String host; private static final int timeout; private static final int expired; static { Prop config = null; try { config = Proper.use("redis.properties"); } catch (Exception ignore) { logger.warn(ignore.getMessage()); } if (config != null) { String shardHost = config.get("redis.shard.host"); String[] hp; host = config.get("redis.host"); timeout = config.getInt("redis.timeout", Protocol.DEFAULT_TIMEOUT); expired = config.getInt("redis.expired", -1); JedisPoolConfig poolConfig = new JedisPoolConfig(); poolConfig.setLifo(config.getBoolean("redis.pool.lifo", BaseObjectPoolConfig.DEFAULT_LIFO)); poolConfig.setMaxWaitMillis(config.getLong("redis.pool.maxWaitMillis", BaseObjectPoolConfig.DEFAULT_MAX_WAIT_MILLIS)); poolConfig.setMinEvictableIdleTimeMillis(config.getLong("redis.pool.minEvictableIdleTimeMillis", BaseObjectPoolConfig.DEFAULT_MIN_EVICTABLE_IDLE_TIME_MILLIS)); poolConfig.setSoftMinEvictableIdleTimeMillis(config.getLong("redis.pool.softMinEvictableIdleTimeMillis", BaseObjectPoolConfig.DEFAULT_SOFT_MIN_EVICTABLE_IDLE_TIME_MILLIS)); poolConfig.setNumTestsPerEvictionRun(config.getInt("redis.pool.numTestsPerEvictionRun", BaseObjectPoolConfig.DEFAULT_NUM_TESTS_PER_EVICTION_RUN)); poolConfig.setTestOnBorrow(config.getBoolean("redis.pool.testOnBorrow", BaseObjectPoolConfig.DEFAULT_TEST_ON_BORROW)); poolConfig.setTestOnReturn(config.getBoolean("redis.pool.testOnReturn", BaseObjectPoolConfig.DEFAULT_TEST_ON_RETURN)); poolConfig.setTestWhileIdle(config.getBoolean("redis.pool.testWhileIdle", BaseObjectPoolConfig.DEFAULT_TEST_WHILE_IDLE)); poolConfig.setTimeBetweenEvictionRunsMillis(config.getLong("redis.pool.timeBetweenEvictionRunsMillis", BaseObjectPoolConfig.DEFAULT_TIME_BETWEEN_EVICTION_RUNS_MILLIS)); poolConfig.setEvictionPolicyClassName(config.get("redis.pool.evictionPolicyClassName", BaseObjectPoolConfig.DEFAULT_EVICTION_POLICY_CLASS_NAME)); poolConfig.setBlockWhenExhausted(config.getBoolean("redis.pool.blockWhenExhausted", BaseObjectPoolConfig.DEFAULT_BLOCK_WHEN_EXHAUSTED)); poolConfig.setJmxEnabled(config.getBoolean("redis.pool.jmxEnabled", BaseObjectPoolConfig.DEFAULT_JMX_ENABLE)); poolConfig.setJmxNamePrefix(config.get("redis.pool.jmxNamePrefix", BaseObjectPoolConfig.DEFAULT_JMX_NAME_PREFIX)); //hp[0] is hostname,hp[1] is port,hp[2] is the password of redis if (shardHost != null) { String[] shards = shardHost.split(","); List<JedisShardInfo> shardInfos = new ArrayList<JedisShardInfo>(); JedisShardInfo shardInfo; for (String s : shards) { hp = s.split(":"); shardInfo = new JedisShardInfo(hp[0], Integer.parseInt(hp[1]), timeout); if (hp.length >= 3) { shardInfo.setPassword(hp[2]); } shardInfos.add(shardInfo); } pool = new ShardedJedisPool(poolConfig, shardInfos); } else { if (host != null) { hp = host.split(":"); if(hp.length >= 3){ pool = new JedisPool(poolConfig, hp[0], Integer.parseInt(hp[1]), timeout, hp[2]); } else { pool = new JedisPool(poolConfig, hp[0], Integer.parseInt(hp[1]), timeout); } } else { pool = null; logger.error("Could not found 'redis.host' or 'redis.shard.host'"); } } } else { host = "127.0.0.1:6379"; pool = null; timeout = Protocol.DEFAULT_TIMEOUT; expired = -1; } } private ShardedJedis getShardedJedis() { ShardedJedis shardedJedis = null; if (pool != null && pool instanceof ShardedJedisPool) { shardedJedis = (ShardedJedis) pool.getResource(); } return shardedJedis; } private Jedis getJedis() { Jedis jedis = null; if (pool != null && pool instanceof JedisPool) { jedis = (Jedis) pool.getResource(); } //hp[0] is hostname,hp[1] is port,hp[2] is the password of redis if (jedis == null) { String[] hp = host.split(":"); jedis = new Jedis(hp[0], Integer.parseInt(hp[1]), timeout); if (hp.length >= 3) { jedis.auth(hp[2]); } } return jedis; } private void returnResource(ShardedJedis shardedJedis, Jedis jedis) { if (pool != null) { if (shardedJedis != null) { pool.returnResource(shardedJedis); } if (jedis != null) { pool.returnResource(jedis); } } else { if (jedis != null) { jedis.disconnect(); } } } private String getCacheKey(String group, String key) { return group + Constant.CONNECTOR + key; } public <T> T getCache(String group, String key) { String cacheKey = getCacheKey(group, key); ShardedJedis shardedJedis = null; Jedis jedis = null; try { shardedJedis = getShardedJedis(); T cahe = null; if (shardedJedis != null) { cahe = (T) Serializer.unserialize(shardedJedis.get(cacheKey.getBytes())); } else { jedis = getJedis(); if (jedis != null) { cahe = (T) Serializer.unserialize(jedis.get(cacheKey.getBytes())); } } return cahe; } catch (Exception e) { logger.warn("%s", e, e); return null; } finally { returnResource(shardedJedis, jedis); } } public void addCache(String group, String key, Object cache, int expired) { ShardedJedis shardedJedis = null; Jedis jedis = null; try { byte[] cacheKey = getCacheKey(group, key).getBytes(); shardedJedis = getShardedJedis(); if (shardedJedis != null) { shardedJedis.set(cacheKey, Serializer.serialize(cache)); if (expired != -1) { shardedJedis.expire(cacheKey, expired); //添加group key addGroupKey(shardedJedis, group, key, expired); return; } else { if (RedisProvider.expired != -1) { shardedJedis.expire(cacheKey, RedisProvider.expired); //添加group key addGroupKey(shardedJedis, group, key, RedisProvider.expired); return; } } addGroupKey(shardedJedis, group, key); } else { jedis = getJedis(); if (jedis != null) { jedis.set(cacheKey, Serializer.serialize(cache)); if (expired != -1) { jedis.expire(cacheKey, expired); //添加到group key addGroupKey(jedis, group, key, expired); return; } else { if (RedisProvider.expired != -1) { jedis.expire(cacheKey, RedisProvider.expired); //添加到group key addGroupKey(jedis, group, key, RedisProvider.expired); return; } } addGroupKey(jedis, group, key); } } } catch (Exception e) { logger.warn("%s", e, e); } finally { returnResource(shardedJedis, jedis); } } public void removeCache(String group, String key) { String cacheKey = getCacheKey(group, key); ShardedJedis shardedJedis = null; Jedis jedis = null; try { shardedJedis = getShardedJedis(); if (shardedJedis != null) { shardedJedis.del(cacheKey.getBytes()); //删除 group key delGroupKey(shardedJedis, group, key); } else { jedis = getJedis(); if (jedis != null) { jedis.del(cacheKey.getBytes()); //删除 group key delGroupKey(jedis, group, key); } } } catch (Exception e) { logger.warn("%s", e, e); } finally { returnResource(shardedJedis, jedis); } } public void doFlush(CacheEvent event) { ShardedJedis shardedJedis = null; Jedis jedis = null; try { shardedJedis = getShardedJedis(); if (shardedJedis != null) { if (event.getType().equals(CacheEvent.CacheEventType.ALL)) { Collection<Jedis> shards = shardedJedis.getAllShards(); for (Jedis j : shards) { j.flushDB(); } } else if (event.getType().equals(CacheEvent.CacheEventType.GROUP)) { byte[] groupRawKey=event.getGroup().getBytes(); //从分组里取出所有的key String groupKeys = event.getGroup() + Constant.CONNECTOR + "keys"; byte[] groupRawKeys = groupKeys.getBytes(); Collection<Jedis> shards = shardedJedis.getAllShards(); int offset = 0; boolean finished = false; do { // need to paginate the keys Set<byte[]> rawKeys = shardedJedis.zrange(groupRawKeys, (offset) * PAGE_SIZE, (offset + 1) * PAGE_SIZE - 1); finished = rawKeys.size() < PAGE_SIZE; offset++; if (!rawKeys.isEmpty()) { List<byte[]> groupedKeys = new ArrayList<byte[]>(); for (byte[] rawKey : rawKeys) { groupedKeys.add(getGroupedKey(groupRawKey, rawKey)); } byte[][] groupedRawKeys = groupedKeys.toArray(new byte[groupedKeys.size()][]); for (Jedis j : shards) { j.del(groupedRawKeys); } } } while (!finished); shardedJedis.del(groupKeys); } } else { jedis = getJedis(); if (jedis != null) { if (event.getType().equals(CacheEvent.CacheEventType.ALL)) { jedis.flushDB(); } else if (event.getType().equals(CacheEvent.CacheEventType.GROUP)) { byte[] groupRawKey=event.getGroup().getBytes(); //从分组里取出所有的key String groupKeys = event.getGroup() + Constant.CONNECTOR + "keys"; byte[] groupRawKeys = groupKeys.getBytes(); int offset = 0; boolean finished = false; do { // need to paginate the keys Set<byte[]> rawKeys = jedis.zrange(groupRawKeys, (offset) * PAGE_SIZE, (offset + 1) * PAGE_SIZE - 1); finished = rawKeys.size() < PAGE_SIZE; offset++; if (!rawKeys.isEmpty()) { List<byte[]> groupedKeys = new ArrayList<byte[]>(); for (byte[] rawKey : rawKeys) { groupedKeys.add(getGroupedKey(groupRawKey, rawKey)); } byte[][] groupedRawKeys = groupedKeys.toArray(new byte[groupedKeys.size()][]); jedis.del(groupedRawKeys); } } while (!finished); jedis.del(groupKeys); } } } } catch (Exception e) { logger.warn("%s", e, e); } finally { returnResource(shardedJedis, jedis); } } private void addGroupKey(Object jedis, String group, String key){ addGroupKey(jedis,group,key,-1); } private void addGroupKey(Object jedis, String group, String key, int expired) { String groupKeys = group + Constant.CONNECTOR + "keys"; byte[] groupRawKeys = groupKeys.getBytes(); if (jedis instanceof ShardedJedis) { ((ShardedJedis) jedis).zadd(groupRawKeys, expired > 0 ? System.currentTimeMillis() + expired * 1000L : expired, Serializer.serialize(key)); } else if (jedis instanceof Jedis) { ((Jedis) jedis).zadd(groupRawKeys, expired > 0 ? System.currentTimeMillis() + expired * 1000L : expired, Serializer.serialize(key)); } } private void delGroupKey(Object jedis, String group, String key) { String groupKeys = group + Constant.CONNECTOR + "keys"; byte[] groupRawKeys = groupKeys.getBytes(); if (jedis instanceof ShardedJedis) { ((ShardedJedis) jedis).zrem(groupRawKeys, Serializer.serialize(key)); ((ShardedJedis) jedis).zremrangeByScore(groupRawKeys, 0, System.currentTimeMillis()); } else if (jedis instanceof Jedis) { ((Jedis) jedis).zrem(groupRawKeys, Serializer.serialize(key)); ((Jedis) jedis).zremrangeByScore(groupRawKeys, 0, System.currentTimeMillis()); } } /** * 获取添加了region的key * * @param groupRawKey * @param rawKey * @return */ private byte[] getGroupedKey(byte[] groupRawKey, byte[] rawKey) { byte[] connectorRawKey=Constant.CONNECTOR.getBytes(); byte[] connectedRawKey = Arrays.copyOf(groupRawKey, groupRawKey.length + connectorRawKey.length); System.arraycopy(connectorRawKey, 0, connectedRawKey, groupRawKey.length, connectorRawKey.length); byte[] regionedKey = Arrays.copyOf(groupRawKey, groupRawKey.length + rawKey.length); System.arraycopy(rawKey, 0, regionedKey, groupRawKey.length, rawKey.length); return regionedKey; } /** * 从regionedkey取出rawKey * * @param groupedKey * @param groupRawKey * @return */ private byte[] getRawKey(byte[] groupedKey, byte[] groupRawKey) { byte[] rawKey = new byte[groupedKey.length - groupRawKey.length]; System.arraycopy(groupedKey, groupRawKey.length, rawKey, 0, rawKey.length); return rawKey; } }