1.Java类
package org.atm.dc.app.util;
import java.io.Closeable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.BinaryJedisCluster;
import redis.clients.jedis.Client;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisClusterConnectionHandler;
import redis.clients.jedis.JedisClusterInfoCache;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisSlotBasedConnectionHandler;
import redis.clients.jedis.PipelineBase;
import redis.clients.jedis.exceptions.JedisMovedDataException;
import redis.clients.jedis.exceptions.JedisRedirectionException;
import redis.clients.util.JedisClusterCRC16;
import redis.clients.util.SafeEncoder;
/**
* @program: message
* @description: JedisClusterPipeLine 因为pipeline是不支持在集群模式下使用的。所以继承PipelineBase接口,重写这个方法了。
* @author: Irving Wei
* @create: 2018-11-27 10:22
**/
public class JedisClusterPipeline extends PipelineBase implements Closeable {
private static final Logger LOGGER = LoggerFactory.getLogger(JedisClusterPipeline.class);
// 部分字段没有对应的获取方法,只能采用反射来做
// 你也可以去继承JedisCluster和JedisSlotBasedConnectionHandler来提供访问接口
private static final Field FIELD_CONNECTION_HANDLER;
private static final Field FIELD_CACHE;
static {
FIELD_CONNECTION_HANDLER = getField(BinaryJedisCluster.class, "connectionHandler");
FIELD_CACHE = getField(JedisClusterConnectionHandler.class, "cache");
}
private JedisSlotBasedConnectionHandler connectionHandler;
private JedisClusterInfoCache clusterInfoCache;
private Queue<Client> clients = new LinkedList<Client>();// 根据顺序存储每个命令对应的Client
private Map<JedisPool, Jedis> jedisMap = new HashMap<>();// 用于缓存连接
private boolean hasDataInBuf = false;// 是否有数据在缓存区
/*
* 根据jedisCluster实例生成对应的JedisClusterPipeline*
* @param
* @return
*/
public static JedisClusterPipeline pipelined(JedisCluster jedisCluster) {
JedisClusterPipeline pipeline = new JedisClusterPipeline();
pipeline.setJedisCluster(jedisCluster);
return pipeline;
}
public JedisClusterPipeline() {
}
public void setJedisCluster(JedisCluster jedis) {
connectionHandler = getValue(jedis, FIELD_CONNECTION_HANDLER);
clusterInfoCache = getValue(connectionHandler, FIELD_CACHE);
}
/**
* 刷新集群信息,当集群信息发生变更时调用
*
* @param
* @return
*/
public void refreshCluster() {
connectionHandler.renewSlotCache();
}
/**
* 同步读取所有数据. 与syncAndReturnAll()相比,sync()只是没有对数据做反序列化
*/
public void sync() {
innerSync(null);
}
/**
* 同步读取所有数据 并按命令顺序返回一个列表
*
* @return 按照命令的顺序返回所有的数据
*/
public List<Object> syncAndReturnAll() {
List<Object> responseList = new ArrayList<Object>();
innerSync(responseList);
return responseList;
}
private void innerSync(List<Object> formatted) {
HashSet<Client> clientSet = new HashSet<Client>();
try {
for (Client client : clients) {
// 在sync()调用时其实是不需要解析结果数据的,但是如果不调用get方法,发生了JedisMovedDataException这样的错误应用是不知道的,因此需要调用get()来触发错误。
// 其实如果Response的data属性可以直接获取,可以省掉解析数据的时间,然而它并没有提供对应方法,要获取data属性就得用反射,不想再反射了,所以就这样了
Object data = generateResponse(client.getOne()).get();
if (null != formatted) {
formatted.add(data);
}
// size相同说明所有的client都已经添加,就不用再调用add方法了
if (clientSet.size() != jedisMap.size()) {
clientSet.add(client);
}
}
} catch (JedisRedirectionException jre) {
if (jre instanceof JedisMovedDataException) {
// if MOVED redirection occurred, rebuilds cluster's slot cache,
// recommended by Redis cluster specification
refreshCluster();
}
throw jre;
} finally {
if (clientSet.size() != jedisMap.size()) {
// 所有还没有执行过的client要保证执行(flush),防止放回连接池后后面的命令被污染
for (Jedis jedis : jedisMap.values()) {
if (clientSet.contains(jedis.getClient())) {
continue;
}
flushCachedData(jedis);
}
}
hasDataInBuf = false;
close();
}
}
@Override
public void close() {
clean();
clients.clear();
for (Jedis jedis : jedisMap.values()) {
if (hasDataInBuf) {
flushCachedData(jedis);
}
jedis.close();
}
jedisMap.clear();
hasDataInBuf = false;
}
private void flushCachedData(Jedis jedis) {
try {
jedis.getClient().getAll();
} catch (RuntimeException ex) {
}
}
@Override
protected Client getClient(String key) {
byte[] bKey = SafeEncoder.encode(key);
return getClient(bKey);
}
@Override
protected Client getClient(byte[] key) {
Jedis jedis = getJedis(JedisClusterCRC16.getSlot(key));
Client client = jedis.getClient();
clients.add(client);
return client;
}
private Jedis getJedis(int slot) {
JedisPool pool = clusterInfoCache.getSlotPool(slot);
// 根据pool从缓存中获取Jedis
Jedis jedis = jedisMap.get(pool);
if (null == jedis) {
jedis = pool.getResource();
jedisMap.put(pool, jedis);
}
hasDataInBuf = true;
return jedis;
}
private static Field getField(Class<?> cls, String fieldName) {
try {
Field field = cls.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException | SecurityException e) {
throw new RuntimeException("cannot find or access field '" + fieldName + "' from " + cls.getName(), e);
}
}
@SuppressWarnings({ "unchecked" })
private static <T> T getValue(Object obj, Field field) {
try {
return (T) field.get(obj);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOGGER.error("get value fail", e);
throw new RuntimeException(e);
}
}
}
package org.atm.dc.app.config;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
@ConfigurationProperties(prefix = "spring.redis.cluster")
@Primary
public class RedisClusterProperties {
List<String> nodes;
public List<String> getNodes() {
return nodes;
}
public void setNodes(List<String> nodes) {
this.nodes = nodes;
}
}
package org.atm.dc.app.config;
import java.lang.reflect.Method;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisClusterConfiguration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
@Configuration
@EnableCaching
@EnableConfigurationProperties(RedisClusterProperties.class)
public class RedisConfig extends CachingConfigurerSupport {
@Autowired
RedisClusterProperties clusterProperties;
public @Bean
RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory(
new RedisClusterConfiguration(clusterProperties.getNodes()));
}
/**
* 生成key的策略
*
* @return
*/
@Bean
public KeyGenerator keyGenerator() {
return new KeyGenerator() {
@Override
public Object generate(Object target, Method method, Object... params) {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
}
};
}
@SuppressWarnings("rawtypes")
@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
// 设置缓存过期时间
// rcm.setDefaultExpiration(60);//秒
return rcm;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
@Bean
public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
StringRedisTemplate template = new StringRedisTemplate(factory);
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
// template.setValueSerializer(jackson2JsonRedisSerializer);
template.afterPropertiesSet();
return template;
}
}
package org.atm.dc.app.util;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.atm.dc.app.config.RedisClusterProperties;
import org.atm.dc.utils.ApplicationContants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisCluster;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.JedisShardInfo;
import redis.clients.jedis.Response;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;
@Service
public class RedisUtils {
private Logger logger = LoggerFactory.getLogger(RedisUtils.class);
@SuppressWarnings("rawtypes")
@Autowired
private RedisTemplate redisTemplate;
@Autowired
RedisClusterProperties clusterProperties;
/**
* 写入缓存
*
* @param key
* @param value
* @return
*/
@SuppressWarnings("unchecked")
public boolean set(final String key, Object value) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* 写入缓存设置时效时间
*
* @param key
* @param value
* @return
*/
@SuppressWarnings("unchecked")
public boolean set(final String key, Object value, Long expireTime, TimeUnit timeUnit) {
boolean result = false;
try {
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
operations.set(key, value);
redisTemplate.expire(key, expireTime, timeUnit);
result = true;
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
@SuppressWarnings("unchecked")
public Map<String, Object> hgetAll(String key) {
Map<String, Object> hgetAll = redisTemplate.opsForHash().entries(key);
return hgetAll;
}
/**
* 批量删除对应的value
*
* @param keys
*/
public void remove(final String... keys) {
for (String key : keys) {
remove(key);
}
}
/**
* 批量删除key
*
* @param pattern
*/
@SuppressWarnings("unchecked")
public void removePattern(final String pattern) {
Set<Serializable> keys = redisTemplate.keys(pattern);
if (keys.size() > 0) {
redisTemplate.delete(keys);
}
}
/**
* 删除对应的value
*
* @param key
*/
@SuppressWarnings("unchecked")
public void remove(final String key) {
if (exists(key)) {
redisTemplate.delete(key);
}
}
/**
* 判断缓存中是否有对应的value
*
* @param key
* @return
*/
@SuppressWarnings("unchecked")
public boolean exists(final String key) {
return redisTemplate.hasKey(key);
}
/**
* 读取缓存
*
* @param key
* @return
*/
@SuppressWarnings("unchecked")
public Object get(final String key) {
Object result = null;
ValueOperations<Serializable, Object> operations = redisTemplate.opsForValue();
result = operations.get(key);
return result;
}
/**
* 模糊匹配key,读取所有符合条件的数据
* @param key
* @return List<Object>
*/
@SuppressWarnings({ "unchecked" })
public List<Object> getRedis2(String key) {
List<Object> list = new ArrayList<Object>();
try {
Set<String> keys = redisTemplate.keys("*" + key + "*");
list = redisTemplate.opsForValue().multiGet(keys);
} catch (Exception e) {
logger.info("getRedis2,报错太频繁,所以简写。可能是这个错误:java.net.SocketTimeoutException: Read timed out");
}
return list;
}
/**
* 模糊匹配key,读取所有符合条件的数据【查询一次大约10ms,查询一万次需要10s,不建议使用此方法】
* @param key
* @return Map<String, Object>
*/
@SuppressWarnings("unchecked")
public Map<String, Object> getRedis(final String key) {
Map<String, Object> map = new HashMap<String, Object>();
try {
Set<String> keys = redisTemplate.keys("*" + key + "*");
for (String string : keys) {
Object value = redisTemplate.opsForValue().get(string);
map.put(string, value);
}
} catch (Exception e) {
logger.info("getRedis,报错太频繁,所以简写。可能是这个错误:java.net.SocketTimeoutException: Read timed out");
}
return map;
}
/**
* 模糊匹配key,读取所有符合条件的数据【查询一次大约10ms,查询一万次需要10s,不建议使用此方法】
* @param key
* @return hash类型
*/
@SuppressWarnings("unchecked")
public Map<String, Map<Object, Object>> getRedisHash(final String key) {
Map<String, Map<Object, Object>> map = new HashMap<String, Map<Object, Object>>();
Set<String> keys = redisTemplate.keys("*" + key + "*");
for (String string : keys) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
Map<Object, Object> value = hash.entries(string);
map.put(string, value);
}
return map;
}
/**
* 模糊匹配key,读取所有符合条件的数据【】
* @param key
* @return Map<String, List<Object>>
*/
@SuppressWarnings("unchecked")
public Map<String, List<Object>> getRedisList(final String key) {
Map<String, List<Object>> map = new HashMap<String, List<Object>>();
try {
Set<String> keys = redisTemplate.keys("*" + key + "*");
for (String string : keys) {
List<Object> list = lRange(string, 0, -1);
map.put(string, list);
}
} catch (Exception e) {
logger.info("getRedisList,报错太频繁,所以简写。可能是这个错误:java.net.SocketTimeoutException: Read timed out");
}
return map;
}
/**
* 哈希 添加
*
* @param key
* @param hashKey
* @param value
*/
@SuppressWarnings("unchecked")
public void hmSet(String key, Object hashKey, Object value) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
hash.put(key, hashKey, value);
}
/**
* 哈希获取数据
*
* @param key
* @param hashKey
* @return
*/
@SuppressWarnings("unchecked")
public Object hmGet(String key, Object hashKey) {
HashOperations<String, Object, Object> hash = redisTemplate.opsForHash();
return hash.get(key, hashKey);
}
@SuppressWarnings("unchecked")
public Map<String, Object> hmEntries(String key) {
HashOperations<String, String, Object> hash = redisTemplate.opsForHash();
return hash.entries(key);
}
/**
* 列表添加
*
* @param k
* @param v
*/
@SuppressWarnings("unchecked")
public void lPush(String k, Object v) {
ListOperations<String, Object> list = redisTemplate.opsForList();
list.rightPush(k, v);
}
/**
* 列表获取
*
* @param k
* @param l
* @param l1
* @return
*/
@SuppressWarnings("unchecked")
public List<Object> lRange(String k, long l, long l1) {
ListOperations<String, Object> list = redisTemplate.opsForList();
return list.range(k, l, l1);
}
/**
* 集合添加
*
* @param key
* @param value
*/
@SuppressWarnings("unchecked")
public void add(String key, Object value) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
set.add(key, value);
}
/**
* 集合获取
*
* @param key
* @return
*/
@SuppressWarnings("unchecked")
public Set<Object> setMembers(String key) {
SetOperations<String, Object> set = redisTemplate.opsForSet();
return set.members(key);
}
/**
* 有序集合添加
*
* @param key
* @param value
* @param scoure
*/
@SuppressWarnings("unchecked")
public void zAdd(String key, Object value, double scoure) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
zset.add(key, value, scoure);
}
/**
* 有序集合获取
*
* @param key
* @param scoure
* @param scoure1
* @return
*/
@SuppressWarnings("unchecked")
public Set<Object> rangeByScore(String key, double scoure, double scoure1) {
ZSetOperations<String, Object> zset = redisTemplate.opsForZSet();
return zset.rangeByScore(key, scoure, scoure1);
}
/**
* pipeline获取数据。Set集合
* @param str
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, Set<String>> usePipeline2(String str) {
Map<String, Set<String>> result = new HashMap<String, Set<String>>();
JedisCluster jedis = getJedisCluster();
JedisClusterPipeline pipeline = null;
try {
// long aa = System.currentTimeMillis();
Map<String, Response<Set<String>>> responses = new HashMap<String, Response<Set<String>>>();
Set<String> keys = redisTemplate.keys("*" + str + "*");
// long bb = System.currentTimeMillis();
pipeline = JedisClusterPipeline.pipelined(jedis);
for(String key : keys) {
responses.put(key, pipeline.smembers(key));
}
long cc = System.currentTimeMillis();
pipeline.sync();
long dd = System.currentTimeMillis();
logger.info("responses.size()-----" + responses.size() + ",pipeline.sync()------" + (dd - cc) + "ms");
for(String k : responses.keySet()) {
result.put(k, responses.get(k).get());
}
// long ee = System.currentTimeMillis();
} catch (Exception e) {
logger.info("usePipeline2,报错太频繁,所以简写。可能是这个错误:java.net.SocketTimeoutException: Read timed out");
}finally {
pipeline.close();
}
return result;
}
/**
* pipeline获取数据。List集合
* @param str
* @return
*/
@SuppressWarnings("unchecked")
public Map<String, List<String>> usePipeline(String str) {
Map<String, List<String>> result = new HashMap<String, List<String>>();
JedisCluster jedis = getJedisCluster();
JedisClusterPipeline pipeline = null;
try {
// long aa = System.currentTimeMillis();
Map<String, Response<List<String>>> responses = new HashMap<String, Response<List<String>>>();
Set<String> keys = redisTemplate.keys("*" + str + "*");
// long bb = System.currentTimeMillis();
// System.out.println("redisTemplate.keys------" + (bb - aa) + "ms");
pipeline = JedisClusterPipeline.pipelined(jedis);
for(String key : keys) {
responses.put(key, pipeline.lrange(key, 0, -1));
}
long cc = System.currentTimeMillis();
// System.out.println("pipeline.lrange------" + (cc - bb) + "ms");
pipeline.sync();
long dd = System.currentTimeMillis();
logger.info("responses.size()-----" + responses.size() + ",pipeline.sync()------" + (dd - cc) + "ms");
for(String k : responses.keySet()) {
result.put(k, responses.get(k).get());
}
// long ee = System.currentTimeMillis();
// System.out.println("result.put------" + (ee - dd) + "ms");
} catch (Exception e) {
logger.info("usePipeline,报错太频繁,所以简写。可能是这个错误:java.net.SocketTimeoutException: Read timed out");
}finally {
pipeline.close();
}
return result;
}
/**
* 集群
* @return
*/
private JedisCluster getJedisCluster() {
// 添加集群的服务节点Set集合
Set<HostAndPort> hostAndPortsSet = new HashSet<HostAndPort>();
// 添加节点
String[] arr = null;
List<String> list = clusterProperties.getNodes();
for (String string : list) {
arr = string.split(":");
hostAndPortsSet.add(new HostAndPort(arr[0], Integer.parseInt(arr[1])));
}
// Jedis连接池配置
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
// 最大空闲连接数, 默认8个
jedisPoolConfig.setMaxIdle(100);
// 最大连接数, 默认8个
jedisPoolConfig.setMaxTotal(500);
//最小空闲连接数, 默认0
jedisPoolConfig.setMinIdle(0);
// 获取连接时的最大等待毫秒数(如果设置为阻塞时BlockWhenExhausted),如果超时就抛异常, 小于零:阻塞不确定的时间, 默认-1
jedisPoolConfig.setMaxWaitMillis(2000); // 设置2秒
//对拿到的connection进行validateObject校验
jedisPoolConfig.setTestOnBorrow(true);
JedisCluster jedis = new JedisCluster(hostAndPortsSet, jedisPoolConfig);
return jedis;
}
/**
* 单节点、非集群
* @return
*/
@SuppressWarnings("resource")
public ShardedJedis getShardedJedis() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(2);
poolConfig.setMaxIdle(1);
poolConfig.setMaxWaitMillis(2000);
poolConfig.setTestOnBorrow(false);
poolConfig.setTestOnReturn(false);
JedisShardInfo info1 = new JedisShardInfo(ApplicationContants.redisHost, ApplicationContants.redisPort);
info1.setSoTimeout(3000);
info1.setPassword(ApplicationContants.redisPwd);//这里是密码
JedisShardInfo info2 = new JedisShardInfo(ApplicationContants.redisHost, ApplicationContants.redisPort);
info2.setSoTimeout(3000);
info2.setPassword(ApplicationContants.redisPwd);//这里是密码
ShardedJedisPool pool = new ShardedJedisPool(poolConfig, Arrays.asList(info1, info2));
return pool.getResource();
}
}
2.备注
<!--集成redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-redis</artifactId>
<version>1.4.1.RELEASE</version>
</dependency>
报错可以注释掉RedisConfig.java
// @SuppressWarnings("rawtypes")
// @Bean
// public CacheManager cacheManager(RedisTemplate redisTemplate) {
// RedisCacheManager rcm = new RedisCacheManager(redisTemplate);
// // 设置缓存过期时间
// // rcm.setDefaultExpiration(60);//秒
// return rcm;
// }
后缀为:.properties
spring.redis.cluster.nodes=IP1:6379,IP2:6379,IP3:6379
spring.redis.password=
后缀为:.yml
spring:
redis:
cluster:
nodes: IP1:6379,IP2:6379,IP3:6379
password: