您的位置:首页 > 数据库 > Redis

Spring客户端对Redis 缓存的使用(Java客户端等Redis3.X RedisCluster模式的支持)

2016-09-29 15:06 791 查看
最近项目中使用Spring并使用Redis作为缓存,参考网上的一些做法。发现Spring根本启动不了,先贴出网上的做法:

导入包

<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>{version}/version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>{version}</version>
</dependency>


配置核心类

@Bean
public JedisConnectionFactory redisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
redisConnectionFactory.setUsePool(false);
redisConnectionFactory.setHostName(xxx);
redisConnectionFactory.setPort(xxx);
return redisConnectionFactory;
}
@Bean
public RedisTemplate<String, String> redisTemplate(JedisConnectionFactory jf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(jf);
return redisTemplate;
}
@Bean(name="redisCacheManager")
public CacheManager cacheManager(RedisTemplate redisTemplate) {
RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);

// Number of seconds before expiration. Defaults to unlimited (0)
cacheManager.setDefaultExpiration(3000); // Sets the default expire time
// (in seconds)
return cacheManager;
}


完成如上配置后就是在业务逻辑层的注解配置:

@CachePut(key="#student.id",value="student")
@Override
public void add(Student student) {
//xxxx
}
@CacheEvict(value="student",key="#id")
public void delete(Integer id) {
//xxx
}
@Cacheable(key="#id",value="student")
@Override
public Student queryById(Integer id) {
//xxx
}


@Cacheable(key=”#id”,value=”student”),这个注释的意思是,当调用这个方法的时候,会从一个名叫 accountCache 的缓存中查询,如果没有,则执行实际的方法(即查询数据库),并将执行的结果存入缓存中,否则返回缓存中的对象。这里的缓存中的 key 就是参数 id

@CacheEvict 注释来标记要清空缓存的方法,当这个方法被调用后,即会清空缓存。注意其中一个

@CachePut 注释,这个注释可以确保方法被执行,同时方法的返回值也被记录到缓存中,实现缓存与数据库的同步更新。

我们不难看出,缓存的key为方法参数的一部分,为了避免两个数据其实不一样但方法参数有可能一致的情况我们需要自定义缓存的key来避免。

@Bean
public KeyGenerator wiselyKeyGenerator(){
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();
}
};

}

@Cacheable(value = "usercache",keyGenerator = "wiselyKeyGenerator")


如上是通过注解在方法级别来配置缓存。现在我们来自定义操作缓存,即手动管理缓存中的数据:

redis缓存工具类

ValueOperations   ——基本数据类型和实体类的缓存

ListOperations   ——list的缓存

SetOperations   ——set的缓存

HashOperations   ——Map的缓存

@Autowired @Qualifier("jedisTemplate")
public RedisTemplate redisTemplate;

/**
* 缓存基本的对象,Integer、String、实体类等
* @param key    缓存的键值
* @param value    缓存的值
* @return        缓存的对象
*/
public <T> ValueOperations<String,T> setCacheObject(String key,T value)
{

ValueOperations<String,T> operation = redisTemplate.opsForValue();
operation.set(key,value);
return operation;
}

/**
* 获得缓存的基本对象。
* @param key        缓存键值
* @param operation
* @return            缓存键值对应的数据
*/
public <T> T getCacheObject(String key/*,ValueOperations<String,T> operation*/)
{
ValueOperations<String,T> operation = redisTemplate.opsForValue();
return operation.get(key);
}

/**
* 缓存List数据
* @param key        缓存的键值
* @param dataList    待缓存的List数据
* @return            缓存的对象
*/
public <T> ListOperations<String, T> setCacheList(String key,List<T> dataList)
{
ListOperations listOperation = redisTemplate.opsForList();
if(null != dataList)
{
int size = dataList.size();
for(int i = 0; i < size ; i ++)
{

listOperation.rightPush(key,dataList.get(i));
}
}

return listOperation;
}

/**
* 获得缓存的list对象
* @param key    缓存的键值
* @return        缓存键值对应的数据
*/
public <T> List<T> getCacheList(String key)
{
List<T> dataList = new ArrayList<T>();
ListOperations<String,T> listOperation = redisTemplate.opsForList();
Long size = listOperation.size(key);

for(int i = 0 ; i < size ; i ++)
{
dataList.add((T) listOperation.leftPop(key));
}

return dataList;
}

/**
* 缓存Set
* @param key        缓存键值
* @param dataSet    缓存的数据
* @return            缓存数据的对象
*/
public <T> BoundSetOperations<String,T> setCacheSet(String key,Set<T> dataSet)
{
BoundSetOperations<String,T> setOperation = redisTemplate.boundSetOps(key);
/*T[] t = (T[]) dataSet.toArray();
setOperation.add(t);*/

Iterator<T> it = dataSet.iterator();
while(it.hasNext())
{
setOperation.add(it.next());
}

return setOperation;
}

/**
* 获得缓存的set
* @param key
* @param operation
* @return
*/
public Set<T> getCacheSet(String key/*,BoundSetOperations<String,T> operation*/)
{
Set<T> dataSet = new HashSet<T>();
BoundSetOperations<String,T> operation = redisTemplate.boundSetOps(key);

Long size = operation.size();
for(int i = 0 ; i < size ; i++)
{
dataSet.add(operation.pop());
}
return dataSet;
}

/**
* 缓存Map
* @param key
* @param dataMap
* @return
*/
public <T> HashOperations<String,String,T> setCacheMap(String key,Map<String,T> dataMap)
{

HashOperations hashOperations = redisTemplate.opsForHash();
if(null != dataMap)
{

for (Map.Entry<String, T> entry : dataMap.entrySet()) {

/*System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());  */
hashOperations.put(key,entry.getKey(),entry.getValue());
}

}

return hashOperations;
}

/**
* 获得缓存的Map
* @param key
* @param hashOperation
* @return
*/
public <T> Map<String,T> getCacheMap(String key/*,HashOperations<String,String,T> hashOperation*/)
{
Map<String, T> map = redisTemplate.opsForHash().entries(key);
/*Map<String, T> map = hashOperation.entries(key);*/
return map;
}

/**
* 缓存Map
* @param key
* @param dataMap
* @return
*/
public <T> HashOperations<String,Integer,T> setCacheIntegerMap(String key,Map<Integer,T> dataMap)
{
HashOperations hashOperations = redisTemplate.opsForHash();
if(null != dataMap)
{

for (Map.Entry<Integer, T> entry : dataMap.entrySet()) {

/*System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue());  */
hashOperations.put(key,entry.getKey(),entry.getValue());
}

}

return hashOperations;
}

/**
* 获得缓存的Map
* @param key
* @param hashOperation
* @return
*/
public <T> Map<Integer,T> getCacheIntegerMap(String key/*,HashOperations<String,String,T> hashOperation*/)
{
Map<Integer, T> map = redisTemplate.opsForHash().entries(key);
/*Map<String, T> map = hashOperation.entries(key);*/
return map;
}


Java对Redis Cluster模式的支持

查阅相关资料Spring-data-redis在1.7版本的时候才开始执行Redis Cluster模式。不知道是不是Redis3.X必须要求的,现在要配置Spring对Redis的操作必须配置RedisClusterConfiguration。

现在目前主要有二种方式:

一、以直接调用jedis来实现;

二、使用spring-data-redis 1.7X版本

下面分别对这二种方式如何操作Redis进行说明:

//   通过Jedis操作Redis Cluster的模型可以参考Redis官网,具体如下:

Set<HostAndPort>  jedisClusterNodes = new HashSet<HostAndPort>();

//Jedis Cluster will attempt to discover cluster nodes automatically

jedisClusterNodes.add(new HostAndPort("10.96.5.183",9001));

jedisClusterNodes.add(new HostAndPort("10.96.5.183",9002));

jedisClusterNodes.add(new HostAndPort("10.96.5.183",9003));

JedisCluster jc = new JedisCluster(jedisClusterNodes);


使用Spring-data-redis:

@Bean
public RedisClusterConfiguration getRedisCluster() {

Set<RedisNode> jedisClusterNodes = new HashSet<RedisNode>();

// Jedis Cluster will attempt to discover cluster nodes automatically

jedisClusterNodes.add(new RedisNode(redisHostName, Integer.valueOf(redisPort)));
//jedisClusterNodes.add( xxx );
RedisClusterConfiguration redisClusterConfiguration = new RedisClusterConfiguration();
redisClusterConfiguration.setClusterNodes( new HashSet<RedisNode>());
return redisClusterConfiguration;
}

@Bean
public JedisConnectionFactory jedisConnectionFactory() {
JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(getRedisCluster());
/*
* Defaults
redisConnectionFactory.setUsePool(false);
redisConnectionFactory.setHostName(redisHostName);
redisConnectionFactory.setPort(Integer.valueOf(redisPort));
*
*/
return redisConnectionFactory;
}

@Bean
public RedisTemplate<String, String> redisTemplate(JedisConnectionFactory cf) {
RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
redisTemplate.setConnectionFactory(cf);
return redisTemplate;
}


Redis Cluster调试中常见错误

(1) 按如上配置启动服务时,抛出ERR This instance has cluster support disabled.此时由于redis未开启Redis Cluster。在配置中设置cluster-enabled即可。

(1)当客户端与集群服务器不在同一台服务器上时,有如下错误Could not get a resource from the Cluster

一般当客户端与集群服务器在同一台服务器上时,操作Redis Cluster正常; 当二者不在同一台服务器上时报如上错误,可能是clusterTimeOut时间设置过小;


(2)操作Redis时报Too many cluster redirections

初始化JedisCluster时,设定JedisCluster的maxRedirections.


JedisCluster(Set jedisClusterNode, int timeout, int maxRedirections) ;

JedisCluster jc = new JedisCluster(jedisClusterNodes,5000,1000);

请参考:https://gitHub.com/xetorthio/jedis/issues/659

(3)Redis Cluster数据写入慢

检查在通过./redis-trib命令建立集群时,如果是通过127.0.0.1的方式建立的集群,那么在往Redis Cluster中写入数据时写入速度比较慢。可以通过配置真实的IP来规避此问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: