本文主要介绍Spring Data Redis的实际使用。
1.Spring Data Redis 1.5新特性
增加了Redis HyperLogLog命令PFADD,PFCOUNT,PFMERGE
可以使用Jackson基于RedisSerializer对Java类型序列化
使用PropertySource配置Redis Sentinel连接,目前仅Jedis客户端支持
2.Spring Data Redis ?
Spring Data Redis使得在Spring应用中读写Redis数据库更加容易。
Spring Data Redis提供了四种Redis服务的Java客户端包的集成,分别是Jedis, JRedis, SRP and Lettuce
3.版本要求
Spring Data Redis1.2.x要求JDK1.6+,Spring Framwork3.2.8+
Key-Value存储服务Redis 2.6.x+
4.搭建环境
本文假设已经安装完成了Redis服务,并成功运行。
创建maven项目,添加依赖的Jar,本文主要使用jedis
1
2
3
4
5
|
< dependency >
< groupId >org.springframework.data</ groupId >
< artifactId >spring-data-redis</ artifactId >
< version >1.5.0.RELEASE</ version >
</ dependency >
|
1
2
3
4
5
|
< dependency >
< groupId >redis.clients</ groupId >
< artifactId >jedis</ artifactId >
< version >2.6.2</ version >
</ dependency >
|
5.连接Redis服务
在Spring Data Redis中通过org.springframework.data.redis.connection包中的RedisConnection和RedisConnectionFactory类来获取Redis连接。
5.1配置JedisConnectionFactory
1
2
3
4
5
6
7
8
9
|
<? xml version = "1.0" encoding = "UTF-8" ?>
< beans xmlns = "http://www.springframework.org/schema/beans"
xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance"
xmlns:p = "http://www.springframework.org/schema/p"
xsi:schemaLocation = "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >
< bean id = "jedisConnectionFactory" class = "org.springframework.data.redis.connection.jedis.JedisConnectionFactory" p:host-name = "server" p:port = "6379" />
</ beans >
|
5.2配置Jredis,SRP,Lettuce的配置和上面配置类似,只需要配置对应的RedisConnectionFactory接口实现接口,它门位于如下包中:
5.3关于RedisConnectionFactory注意问题
上面四种连接器并不是都支持Redis的所有特性,它们之间有差异性,如果调用方法在Connection API中不支持则抛出“UnsupportedOperationException”异常,具体情况需要了解对应的Redis Java客户端Jar的实现。
5.4关于Redis Sentinel(这里暂称为:哨兵)支持
Redis Sentinel监听主服务,再主服务发生故障时能够切换至从服务,将从服务升为主服务来保证故障恢复,使用该功能需要在JedisConnectionFactory设置RedisSentinelConfiguration属性,目前Jedis对Redis Sentinel提供支持。
编码方式如下:
1
2
3
4
5
|
public RedisConnectionFactory jedisConnectionFactory() {
RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration() .master( "mymaster" )
.sentinel( "127.0.0.1" , 26379 ) .sentinel( "127.0.0.1" , 26380 );
return new JedisConnectionFactory(sentinelConfig);
} |
在Spring容器中配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
< bean id = "sentinelConfig"
class = "org.springframework.data.redis.connection.RedisSentinelConfiguration" >
< constructor-arg name = "master" value = "mymaster" />
< constructor-arg name = "sentinelHostAndPorts" >
< set >
< value >192.168.88.153:26379</ value >
< value >192.168.88.153:26380</ value >
< value >192.168.88.153:26382</ value >
</ set >
</ constructor-arg >
</ bean >
< bean id = "jedisConnectionFactory"
class = "org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
< constructor-arg ref = "sentinelConfig" />
</ bean >
|
注意:在配置Redis的sentinel.conf文件时注意使用外部可以访问的ip地址,因为当redis-sentinel服务和redis-server在同一台机器的时候,主服务发生变化时配置文件中将主服务ip变为127.0.0.1,这样外部就无法访问了。如果应用程序,Redis服务在同一台机器则不存在这样的隐患,具体情况则更加实际的网络环境。
配置好之后,在实例化JedisConnectionFactory之后,可见如下日志:
1
2
3
4
5
6
|
2015-4-1 17:29:30 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Trying to find master from available Sentinels... 2015-4-1 17:29:30 redis.clients.jedis.JedisSentinelPool initSentinels 信息: Redis master running at 192.168.88.153:6384, starting Sentinel listeners... 2015-4-1 17:29:30 redis.clients.jedis.JedisSentinelPool initPool 信息: Created JedisPool to master at 192.168.88.153:6384 |
实验环境中192.168.88.153:6384的Redis实例是主服务。
5.5下面通过一组代码展示具体使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
@FixMethodOrder (MethodSorters.NAME_ASCENDING)
public class TestJedis {
public static ApplicationContext ctx;
public static JedisConnectionFactory jedisConnetionFactory;
public JedisConnection jedisConnection;
@SuppressWarnings ( "unchecked" )
@BeforeClass
public static void setBeforeClass() {
ctx = new ClassPathXmlApplicationContext( "spring-redis.xml" );
jedisConnetionFactory = (JedisConnectionFactory) ctx
.getBean( "jedisConnectionFactory" );
}
@Before
public void setBefore() {
jedisConnection = jedisConnetionFactory.getConnection();
}
@After
public void setAfter() {
jedisConnection.close();
}
private void print(Collection<RedisServer> c) {
for (Iterator<RedisServer> iter = c.iterator(); iter.hasNext();) {
RedisServer rs = (RedisServer) iter.next();
System.out.println(rs.getHost() + ":" + rs.getPort());
}
}
// 简单测试JedisConnection
@Ignore
@Test
public void test1() {
if (!jedisConnection.exists( new String( "zz" ).getBytes())) {
jedisConnection.set( new String( "zz" ).getBytes(),
new String( "zz" ).getBytes());
}
}
@Ignore
@Test
public void test2() {
Set< byte []> keys = jedisConnection.keys( new String( "*" ).getBytes());
for (Iterator< byte []> iter = keys.iterator(); iter.hasNext();) {
System.out.println( new String(iter.next()));
}
}
// 测试Sentinel
@Ignore
@Test
public void test3() throws InterruptedException {
if (jedisConnetionFactory.getSentinelConnection().isOpen()) {
Collection<RedisServer> c = jedisConnetionFactory
.getSentinelConnection().masters();
print(c);
RedisNode rn = new RedisNode( "192.168.88.153" , 6380 );
rn.setName( "mymaster" );
c = jedisConnetionFactory.getSentinelConnection().slaves(rn);
print(c);
}
for ( int i = 0 ; i < 1000 ; i++) {
jedisConnection.set( new String( "k" + i).getBytes(), new String( "v"
+ i).getBytes());
Thread.sleep( 1000 );
}
Set< byte []> keys = jedisConnection.keys( new String( "k*" ).getBytes());
Assert.assertEquals( 1000 , keys.size());
}
} |
6.RedisTemplate支持
熟悉Spring的JdbcTemplate对象的话,应该大概能猜出来RedisTemplate的作用了,RedisTemplate对象对RedisConnection进行了封装,它提供了连接管理,序列化等功能,它对Redis的交互进行了更高层次的抽象。另外还提供了Redis操作命令的操作视图,这极大的方便和简化了Redis的操作。
下表是具体的操作视图接口类介绍:
Key类型操作 | |
ValueOperations | Redis String/Value 操作 |
ListOperations | Redis List 操作 |
SetOperations | Redis Set 操作 |
ZSetOperations | Redis Sort Set 操作 |
HashOperations | Redis Hash 操作 |
Value约束操作 | |
BoundValueOperations | Redis String/Value key 约束 |
BoundListOperations | Redis List key 约束 |
BoundSetOperations | Redis Set key 约束 |
BoundZSetOperations | Redis Sort Set key 约束 |
BoundHashOperations | Redis Hash key 约束 |
在org.springframework.data.redis.core包中对表中的接口都提供了相应的默认实现。
6.1RedisSerializer
Spring Data Redis提供了对Key-Value的序列号,在使用RedisTemplate对象是默认使用JdkSerializationRedisSerializer实现。还提供了其它的序列化实现如:Jackson2JsonRedisSerializer,JacksonJsonRedisSerializer,GenericToStringSerializer,StringRedisSerializer,OxmSerializer。
另外用户可以提供自己的序列化实现
6.2配置RedisTemplate
1
2
3
4
5
|
< bean id = "redisTemplate" class = "org.springframework.data.redis.core.RedisTemplate"
p:connection-factory-ref = "jedisConnectionFactory" />
< bean id = "stringRedisTemplate" class = "org.springframework.data.redis.core.StringRedisTemplate" >
< property name = "connectionFactory" ref = "jedisConnectionFactory" />
</ bean >
|
这里配置了RedisTemplate和StringRedisTemplate,不同之处在于StringRedisTemplate的Key-Value序列化使用的是StringRedisSerializer。使用StringRedisTemplate操作Redis之后的结果是读友好的。
另外对Hash类型而言,还有对应的HashKey序列化(其对应于Hash类型的字段名)。
6.3RedisTemplate的使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
// 测试RedisTemplate,自主处理key的可读性(String序列号) @Ignore
@Test
public void test4() {
String key = "spring" ;
ListOperations<String, String> lop = redisTemplate.opsForList();
RedisSerializer<String> serializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(serializer);
redisTemplate.setValueSerializer(serializer);
// rt.setDefaultSerializer(serializer);
lop.leftPush(key, "aaa" );
lop.leftPush(key, "bbb" );
long size = lop.size(key); // rt.boundListOps(key).size();
Assert.assertEquals( 2 , size);
}
// 测试便捷对象StringRedisTemplate
@Ignore
@Test
public void test5() {
ValueOperations<String, String> vop = stringRedisTemplate.opsForValue();
String key = "string_redis_template" ;
String v = "use StringRedisTemplate set k v" ;
vop.set(key, v);
String value = vop.get(key);
Assert.assertEquals(v, value);
}
|
具体使用那种序列化策略则更加存储的Key-Value内容做权衡即可。
通过RedisTemplate中的方法的参数RedisCallback回调接口来获取RedisConnection,进一步操作Redis,比如事务控制。需要注意的是如果使用StringRedisTemplate则返回的是StringRedisConnection对象。
测试RedisCallback代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 测试Callback @Ignore
@Test
public void test61() {
Long dbsize = (Long) stringRedisTemplate
.execute( new RedisCallback<Object>() {
@Override
public Long doInRedis(RedisConnection connection)
throws DataAccessException {
StringRedisConnection stringRedisConnection=(StringRedisConnection)connection;
return stringRedisConnection.dbSize();
}
});
System.out.println( "dbsize:" + dbsize);
}
|
测试SessionCallback代码示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
@Test public void test62() {
List<Object> txresult = stringRedisTemplate
.execute( new SessionCallback<List<Object>>() {
@Override
public List<Object> execute(RedisOperations operations)
throws DataAccessException {
operations.multi();
operations.opsForHash().put( "hkey" , "multikey4" ,
"multivalue4" );
operations.opsForHash().get( "hkey" , "k1" );
return operations.exec();
}
});
for (Object o : txresult) {
System.out.println(o);
/**
* 0. false/true
* 1. v1
*/
}
}
|
说明:
-
在事务中的操作返回都是null,因此不能在execute中对操作的结果进行处理。比如这里get("hkey","k1")的结果。
-
Hash操作中如果字段存在则返回false(redis中是0),不存在返回true(redis中是1)与字段对应的值是否更新无关联。
-
exec()方法返回List<Object>中的对象对应事务中执行的每条命令的返回结果。
7.执行Lua脚本
Redis中执行Lua脚本开发思路:http://aiilive.blog.51cto.com/1925756/1626372
Spring Data Redis中执行Lua脚本更加便利,下面示例展示使用RedisTemplate对象执行Lua脚本。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
// 测试Lua脚本 @Ignore
@Test
public void test71() {
List<String> keys = new ArrayList<String>();
RedisScript<Long> script = new DefaultRedisScript<Long>(
"local size = redis.call('dbsize'); return size;" , Long. class );
Long dbsize = stringRedisTemplate
.execute(script, keys, new Object[] {});
System.out.println( "sha1:" + script.getSha1());
System.out.println( "Lua:" + script.getScriptAsString());
System.out.println( "dbsize:" + dbsize);
}
@Test
public void test72() {
DefaultRedisScript<Boolean> script = new DefaultRedisScript<Boolean>();
/**
* isexistskey.lua内容如下:
*
* return tonumber(redis.call("exists",KEYS[1])) == 1;
*/
script.setScriptSource( new ResourceScriptSource( new ClassPathResource(
"/isexistskey.lua" )));
script.setResultType(Boolean. class ); // Must Set
System.out.println( "script:" + script.getScriptAsString());
Boolean isExist = stringRedisTemplate.execute(script,
Collections.singletonList( "k2" ), new Object[] {});
Assert.assertTrue(isExist);
}
|
8.支持类操作
在org.springframework.data.redis.support包中提供了各种可重用组件,这些组件可以应用到Redis存储,如atomic计数,JDK集合,Redis的类型集合(RedisList,RedisSet等)
1
2
|
< redis:collection id = "springList" key = "springlist"
template = "stringRedisTemplate" type = "LIST" />
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
@Ignore @Test
public void test8() {
RedisAtomicInteger rai = new RedisAtomicInteger( "redis:atomic" ,
jedisConnetionFactory);
System.out.println(rai.get());
}
// 测试Redis Collection
@Ignore
@Test
public void test9() {
@SuppressWarnings ( "unchecked" )
RedisList<String> redisList = (RedisList<String>) ctx
.getBean( "springList" );
redisList.clear();
redisList.addFirst( "china" );
redisList.add( "in" );
redisList.add( "go" );
redisList.addLast( "made" );
System.out.println(redisList.getKey());
}
|
本文转自 secondriver 51CTO博客,原文链接:http://blog.51cto.com/aiilive/1627455,如需转载请自行联系原作者