/*
* (C) Copyright 2015-2016 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.
*
* Contributors:
* ohun@live.cn (夜色)
*/
package com.mpush.cache.redis.manager;
import com.google.common.collect.Lists;
import com.mpush.api.spi.common.*;
import com.mpush.cache.redis.connection.RedisConnectionFactory;
import com.mpush.tools.Jsons;
import com.mpush.tools.config.CC;
import com.mpush.tools.log.Logs;
import com.mpush.tools.thread.pool.ThreadPoolManager;
import redis.clients.jedis.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* redis 对外封装接口
*/
public final class RedisManager implements CacheManager {
public static final RedisManager I = new RedisManager();
private final RedisConnectionFactory factory = new RedisConnectionFactory();
public void init() {
Logs.CACHE.info("begin init redis...");
factory.setPassword(CC.mp.redis.password);
factory.setPoolConfig(CC.mp.redis.getPoolConfig(JedisPoolConfig.class));
factory.setRedisServers(CC.mp.redis.nodes);
factory.setCluster(CC.mp.redis.isCluster());
factory.init();
test();
Logs.CACHE.info("init redis success...");
}
private <R> R call(Function<JedisCommands, R> function, R d) {
if (factory.isCluster()) {
try {
return function.apply(factory.getClusterConnection());
} catch (Exception e) {
Logs.CACHE.error("redis ex", e);
}
} else {
try (Jedis jedis = factory.getJedisConnection()) {
return function.apply(jedis);
} catch (Exception e) {
Logs.CACHE.error("redis ex", e);
}
}
return d;
}
private void call(Consumer<JedisCommands> consumer) {
if (factory.isCluster()) {
try {
consumer.accept(factory.getClusterConnection());
} catch (Exception e) {
Logs.CACHE.error("redis ex", e);
}
} else {
try (Jedis jedis = factory.getJedisConnection()) {
consumer.accept(jedis);
} catch (Exception e) {
Logs.CACHE.error("redis ex", e);
}
}
}
public long incr(String key) {
return call(jedis -> jedis.incr(key), 0L);
}
public long incrBy(String key, long delt) {
return call(jedis -> jedis.incrBy(key, delt), 0L);
}
/********************* k v redis start ********************************/
/**
* @param key
* @param clazz
* @return
*/
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<T> clazz) {
String value = call(jedis -> jedis.get(key), null);
if (value == null) return null;
if (clazz == String.class) return (T) value;
return Jsons.fromJson(value, clazz);
}
public void set(String key, String value) {
set(key, value, 0);
}
public void set(String key, Object value) {
set(key, value, 0);
}
public void set(String key, Object value, int time) {
set(key, Jsons.toJson(value), time);
}
/**
* @param key
* @param value
* @param time seconds
*/
public void set(String key, String value, int time) {
call(jedis -> {
jedis.set(key, value);
if (time > 0) {
jedis.expire(key, time);
}
});
}
public void del(String key) {
call(jedis -> jedis.del(key));
}
/********************* k v redis end ********************************/
/*********************
* hash redis start
********************************/
public void hset(String key, String field, String value) {
call(jedis -> jedis.hset(key, field, value));
}
public void hset(String key, String field, Object value) {
hset(key, field, Jsons.toJson(value));
}
@SuppressWarnings("unchecked")
public <T> T hget(String key, String field, Class<T> clazz) {
String value = call(jedis -> jedis.hget(key, field), null);
if (value == null) return null;
if (clazz == String.class) return (T) value;
return Jsons.fromJson(value, clazz);
}
public void hdel(String key, String field) {
call(jedis -> jedis.hdel(key, field));
}
public Map<String, String> hgetAll(String key) {
return call(jedis -> jedis.hgetAll(key), Collections.<String, String>emptyMap());
}
public <T> Map<String, T> hgetAll(String key, Class<T> clazz) {
Map<String, String> result = hgetAll(key);
if (result.isEmpty()) return Collections.emptyMap();
Map<String, T> newMap = new HashMap<>(result.size());
result.forEach((k, v) -> newMap.put(k, Jsons.fromJson(v, clazz)));
return newMap;
}
/**
* 返回 key 指定的哈希集中所有字段的名字。
*
* @param key
* @return
*/
public Set<String> hkeys(String key) {
return call(jedis -> jedis.hkeys(key), Collections.<String>emptySet());
}
/**
* 返回 key 指定的哈希集中指定字段的值
*
* @param fields
* @param clazz
* @return
*/
public <T> List<T> hmget(String key, Class<T> clazz, String... fields) {
return call(jedis -> jedis.hmget(key, fields), Collections.<String>emptyList())
.stream()
.map(s -> Jsons.fromJson(s, clazz))
.collect(Collectors.toList());
}
/**
* 设置 key 指定的哈希集中指定字段的值。该命令将重写所有在哈希集中存在的字段。如果 key 指定的哈希集不存在,会创建一个新的哈希集并与 key
* 关联
*
* @param hash
* @param time
*/
public void hmset(String key, Map<String, String> hash, int time) {
call(jedis -> {
jedis.hmset(key, hash);
if (time > 0) {
jedis.expire(key, time);
}
});
}
public void hmset(String key, Map<String, String> hash) {
hmset(key, hash, 0);
}
public long hincrBy(String key, String field, long value) {
return call(jedis -> jedis.hincrBy(key, field, value), 0L);
}
/********************* hash redis end ********************************/
/********************* list redis start ********************************/
/**
* 从队列的左边入队
*/
public void lpush(String key, String value) {
call(jedis -> jedis.lpush(key, value));
}
public void lpush(String key, Object value) {
lpush(key, Jsons.toJson(value));
}
/**
* 从队列的右边入队
*/
public void rpush(String key, String value) {
call(jedis -> jedis.lpush(key, value));
}
public void rpush(String key, Object value) {
rpush(key, Jsons.toJson(value));
}
/**
* 移除并且返回 key 对应的 list 的第一个元素
*/
@SuppressWarnings("unchecked")
public <T> T lpop(String key, Class<T> clazz) {
String value = call(jedis -> jedis.lpop(key), null);
if (value == null) return null;
if (clazz == String.class) return (T) value;
return Jsons.fromJson(value, clazz);
}
/**
* 从队列的右边出队一个元素
*/
@SuppressWarnings("unchecked")
public <T> T rpop(String key, Class<T> clazz) {
String value = call(jedis -> jedis.rpop(key), null);
if (value == null) return null;
if (clazz == String.class) return (T) value;
return Jsons.fromJson(value, clazz);
}
/**
* 从列表中获取指定返回的元素 start 和 end
* 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。
* 偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推。
*/
public <T> List<T> lrange(String key, int start, int end, Class<T> clazz) {
return call(jedis -> jedis.lrange(key, start, end), Collections.<String>emptyList())
.stream()
.map(s -> Jsons.fromJson(s, clazz))
.collect(Collectors.toList());
}
/**
* 返回存储在 key 里的list的长度。 如果 key 不存在,那么就被看作是空list,并且返回长度为 0。 当存储在 key
* 里的值不是一个list的话,会返回error。
*/
public long llen(String key) {
return call(jedis -> jedis.llen(key), 0L);
}
/**
* 移除表中所有与 value 相等的值
*
* @param key
* @param value
*/
public void lRem(String key, String value) {
call(jedis -> jedis.lrem(key, 0, value));
}
/********************* list redis end ********************************/
/*********************
* mq redis start
********************************/
public void publish(String channel, Object message) {
String msg = message instanceof String ? (String) message : Jsons.toJson(message);
call(jedis -> {
if (jedis instanceof MultiKeyCommands) {
((MultiKeyCommands) jedis).publish(channel, msg);
} else if (jedis instanceof MultiKeyJedisClusterCommands) {
((MultiKeyJedisClusterCommands) jedis).publish(channel, msg);
}
});
}
public void subscribe(final JedisPubSub pubsub, final String channel) {
ThreadPoolManager.I.newThread(channel,
() -> call(jedis -> {
if (jedis instanceof MultiKeyCommands) {
((MultiKeyCommands) jedis).subscribe(pubsub, channel);
} else if (jedis instanceof MultiKeyJedisClusterCommands) {
((MultiKeyJedisClusterCommands) jedis).subscribe(pubsub, channel);
}
})
).start();
}
/*********************
* set redis start
********************************/
/**
* @param key
* @param value
*/
public void sAdd(String key, String value) {
call(jedis -> jedis.sadd(key, value));
}
/**
* @param key
* @return
*/
public long sCard(String key) {
return call(jedis -> jedis.scard(key), 0L);
}
public void sRem(String key, String value) {
call(jedis -> jedis.srem(key, value));
}
/**
* 默认使用每页10个
*
* @param key
* @param clazz
* @return
*/
public <T> List<T> sScan(String key, Class<T> clazz, int start) {
List<String> list = call(jedis -> jedis.sscan(key, Integer.toString(start), new ScanParams().count(10)).getResult(), null);
return toList(list, clazz);
}
/*********************
* sorted set
********************************/
/**
* @param key
* @param value
*/
public void zAdd(String key, String value) {
call(jedis -> jedis.zadd(key, 0, value));
}
/**
* @param key
* @return
*/
public Long zCard(String key) {
return call(jedis -> jedis.zcard(key), 0L);
}
public void zRem(String key, String value) {
call(jedis -> jedis.zrem(key, value));
}
/**
* 从列表中获取指定返回的元素 start 和 end
* 偏移量都是基于0的下标,即list的第一个元素下标是0(list的表头),第二个元素下标是1,以此类推。
* 偏移量也可以是负数,表示偏移量是从list尾部开始计数。 例如, -1 表示列表的最后一个元素,-2 是倒数第二个,以此类推。
*/
public <T> List<T> zrange(String key, int start, int end, Class<T> clazz) {
Set<String> value = call(jedis -> jedis.zrange(key, start, end), null);
return toList(value, clazz);
}
@SuppressWarnings("unchecked")
private <T> List<T> toList(Collection<String> value, Class<T> clazz) {
if (value != null) {
if (clazz == String.class) {
return (List<T>) new ArrayList<>(value);
}
List<T> newValue = Lists.newArrayList();
for (String temp : value) {
newValue.add(Jsons.fromJson(temp, clazz));
}
return newValue;
}
return null;
}
public void destroy() {
if (factory != null) factory.destroy();
}
public void test() {
if (factory.isCluster()) {
JedisCluster cluster = factory.getClusterConnection();
if (cluster == null) throw new RuntimeException("init redis cluster error.");
} else {
Jedis jedis = factory.getJedisConnection();
if (jedis == null) throw new RuntimeException("init redis error, can not get connection.");
jedis.close();
}
}
}