|
首先不得不服Spring这个宇宙无敌的开源框架,几乎整合了所有流行的其它框架,http://projects.spring.io/spring-data/ 从这上面看,当下流行的redis、solr、hadoop、mongoDB、couchBase... 全都收入囊中。对于redis整合而言,主要用到的是spring-data-redis
使用步骤:
一、pom添加依赖项
org.springframework.data
spring-data-redis
1.4.1.RELEASE
其它Spring必备组件,比如Core,Beans之类,大家自行添加吧
观察一下:
jedis、jredis等常用java的redis client已经支持了,不知道以后会不会集成Redisson,spring-data-redis提供了一个非常有用的类:StringRedisTemplate
对于大多数缓存应用场景而言,字符串是最常用的缓存项,用StringRedisTemplate可以轻松应付。
二、spring配置
1
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
28
29
30
31
32
33
View Code 这里我们使用Sentinel模式来配置redis连接,从上篇学习知道,sentinel是一种高可用架构,个人推荐在生产环境中使用sentinel模式。
注:26-28行,经试验,如果修改了默认端口,这里必须明细指定hostName及port,否则运行后,无法正确读写缓存,参考下面的配置:
其中hostName为当前master的IP,port为redis-server的运行端口(非sentinel端口),此外还要设置usePool为false,由于sentinel可能会自行切换master节点,如果不清楚当前的master节点是哪台机器,可以用前面提到的命令./redis-cli -p sentinel masters查看,或者用java代码输出,参考下面的代码:
1 ApplicationContext ctx = new FileSystemXmlApplicationContext("/opt/app/spring-redis.xml");
2 StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
3 for (RedisServer m : template.getConnectionFactory().getSentinelConnection().masters()) {
4 logger.debug(m);
5 }
另外 这里的value值建议设置成false,如果改成true,经实际测试,发现在有些环境下会报如下错误:
redis.clients.jedis.exceptions.JedisDataException: ERR unknown command 'SET'
其它注意事项:
配置文件中的sentinels属性的Set 中的节点,并非一定要在同一个master下,也可以是归属于多个master,即:如果这里配置了10个node信息,其中1-3归属于master1,剩下的4-10属于master2,这也是允许的。
这样调用时,通过StringRedisTemplate.getConnectionFactory().getSentinelConnection().masters()可以返回一个master的列表,然后代码中根据需要,向某一个需要的master写入缓存.
三、单元测试
1 @Test
2 public void testSpringRedis() {
3 ConfigurableApplicationContext ctx = null;
4 try {
5 ctx = new ClassPathXmlApplicationContext("spring.xml");
6
7 StringRedisTemplate stringRedisTemplate = ctx.getBean("stringRedisTemplate", StringRedisTemplate.class);
8
9 // String读写
10 stringRedisTemplate.delete("myStr");
11 stringRedisTemplate.opsForValue().set("myStr", "http://yjmyzz.iyunv.com/");
12 System.out.println(stringRedisTemplate.opsForValue().get("myStr"));
13 System.out.println("---------------");
14
15 // List读写
16 stringRedisTemplate.delete("myList");
17 stringRedisTemplate.opsForList().rightPush("myList", "A");
18 stringRedisTemplate.opsForList().rightPush("myList", "B");
19 stringRedisTemplate.opsForList().leftPush("myList", "0");
20 List listCache = stringRedisTemplate.opsForList().range(
21 "myList", 0, -1);
22 for (String s : listCache) {
23 System.out.println(s);
24 }
25 System.out.println("---------------");
26
27 // Set读写
28 stringRedisTemplate.delete("mySet");
29 stringRedisTemplate.opsForSet().add("mySet", "A");
30 stringRedisTemplate.opsForSet().add("mySet", "B");
31 stringRedisTemplate.opsForSet().add("mySet", "C");
32 Set setCache = stringRedisTemplate.opsForSet().members(
33 "mySet");
34 for (String s : setCache) {
35 System.out.println(s);
36 }
37 System.out.println("---------------");
38
39 // Hash读写
40 stringRedisTemplate.delete("myHash");
41 stringRedisTemplate.opsForHash().put("myHash", "PEK", "北京");
42 stringRedisTemplate.opsForHash().put("myHash", "SHA", "上海虹桥");
43 stringRedisTemplate.opsForHash().put("myHash", "PVG", "浦东");
44 Map hashCache = stringRedisTemplate.opsForHash()
45 .entries("myHash");
46 for (Map.Entry entry : hashCache.entrySet()) {
47 System.out.println(entry.getKey() + " - " + entry.getValue());
48 }
49
50 System.out.println("---------------");
51
52 } finally {
53 if (ctx != null && ctx.isActive()) {
54 ctx.close();
55 }
56 }
57
58 }
View Code 运行一下,行云流水般的输出:
...
信息: Created JedisPool to master at 10.6.144.***:7030
http://yjmyzz.iyunv.com/
---------------
0
A
B
---------------
C
B
A
---------------
SHA - 上海虹桥
PVG - 浦东
PEK - 北京
---------------
...
注意红色标出部分,从eclipse控制台的输出,还能看出当前的master是哪台服务器
这里再补充一点小技巧:如果想遍历所有master及slave可以参考以下代码
1 @Test
2 public void testGetAllMasterAndSlaves() {
3 ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/spring-redis.xml");
4 StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
5 RedisSentinelConnection conn = template.getConnectionFactory().getSentinelConnection();
6 for (RedisServer m : conn.masters()) {
7 System.out.println("master => " + m);//打印master信息
8 Collection slaves = conn.slaves(m);
9 //打印该master下的所有slave信息
10 for (RedisServer s : slaves) {
11 System.out.println("slaves of " + m + " => " + s);
12 }
13 System.out.println("--------------");
14 }
15 ((FileSystemXmlApplicationContext) ctx).close();
16 }
View Code 输出类似下面的结果:
master => 172.20.16.191:6379
slaves of 172.20.16.191:6379 => 172.20.16.192:6379
注:这里输出的slaves列表,经实际测试,发现只是根据redis server端的配置呆板的返回slave node列表,不管这些node是死是活,换句话说,就算某个slave已经down掉,这里依然会返回。
三、POJO对象的缓存
Spring提供的StringRedisTemplate只能对String操作,大多数情况下已经够用,但如果真需要向redis中存放POJO对象也不难,我们可以参考StringRedisTemplate的源码,扩展出ObjectRedisTemplate
1 package org.springframework.data.redis.core;
2
3 import org.springframework.data.redis.connection.DefaultStringRedisConnection;
4 import org.springframework.data.redis.connection.RedisConnection;
5 import org.springframework.data.redis.connection.RedisConnectionFactory;
6 import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
7 import org.springframework.data.redis.serializer.RedisSerializer;
8
9 public class ObjectRedisTemplate extends RedisTemplate {
10
11 public ObjectRedisTemplate(RedisConnectionFactory connectionFactory,
12 Class clazz) {
13
14 RedisSerializer objectSerializer = new Jackson2JsonRedisSerializer(
15 clazz);
16
17 RedisSerializer objectKeySerializer = new Jackson2JsonRedisSerializer(
18 String.class);
19
20 setKeySerializer(objectKeySerializer);
21 setValueSerializer(objectSerializer);
22 setHashKeySerializer(objectSerializer);
23 setHashValueSerializer(objectSerializer);
24
25 setConnectionFactory(connectionFactory);
26 afterPropertiesSet();
27 }
28
29 protected RedisConnection preProcessConnection(RedisConnection connection,
30 boolean existingConnection) {
31 return new DefaultStringRedisConnection(connection);
32 }
33 }
View Code 然后就可以这样用了:
1 @Test
2 public void testSpringRedis() {
3 ConfigurableApplicationContext ctx = null;
4 try {
5 ctx = new ClassPathXmlApplicationContext("spring.xml");
6
7 JedisConnectionFactory connFactory = ctx.getBean(
8 "jedisConnFactory", JedisConnectionFactory.class);
9
10 ObjectRedisTemplate template = new ObjectRedisTemplate(
11 connFactory, SampleBean.class);
12
13 template.delete("myBean");
14 SampleBean bean = new SampleBean("菩提树下的杨过");
15 template.opsForValue().set("myBean", bean);
16
17 System.out.println(template.opsForValue().get("myBean"));
18
19 } finally {
20 if (ctx != null && ctx.isActive()) {
21 ctx.close();
22 }
23 }
24 }
View Code 其中SampleBean的定义如下:
1 package com.cnblogs.yjmyzz;
2
3 import java.io.Serializable;
4
5 public class SampleBean implements Serializable {
6
7 private static final long serialVersionUID = -303232410998377570L;
8
9 private String name;
10
11 public SampleBean() {
12 }
13
14 public SampleBean(String name) {
15 this.name = name;
16 }
17
18 public String getName() {
19 return name;
20 }
21
22 public void setName(String name) {
23 this.name = name;
24 }
25
26 public String toString() {
27 return "name:" + name;
28 }
29
30 }
View Code 注:由于不是标准的String类型,所以在redis控制台,用./redis-cli get myBean是看不到缓存内容的,只能得到nil的输出,不要误以为set没成功!通过代码是可以正常get到缓存值的。
另外关于POJO对象的缓存,还有二个注意事项:
a) POJO类必须要有默认的无参构造函数,否则反序列化时会报错
b) ObjectRedisTemplate中的T不能是接口,比如 DomainModelA继承自接口 IModelA,使用ObjectRedisTemplate时,要写成ObjectRedisTemplate而不是ObjectRedisTemplate,否则反序列化时也会出错 |
|
|