首先不得不服Spring这个宇宙无敌的开源框架,几乎整合了所有流行的其它框架,http://projects.spring.io/spring-data/从这上面看,当下流行的redis、solr、hadoop、mongoDB、couchBase... 全都收入囊中。对于redis整合而言,主要用到的是spring-data-redis

使用步骤:

一、pom添加依赖项

  1. <dependency>
  2. <groupId>org.springframework.data</groupId>
  3. <artifactId>spring-data-redis</artifactId>
  4. <version>1.4.1.RELEASE</version>
  5. </dependency>

其它Spring必备组件,比如Core,Beans之类,大家自行添加吧

观察一下:

jedis、jredis等常用java的redis client已经支持了,不知道以后会不会集成Redisson,spring-data-redis提供了一个非常有用的类:StringRedisTemplate

对于大多数缓存应用场景而言,字符串是最常用的缓存项,用StringRedisTemplate可以轻松应付。

二、spring配置

  1. <bean id="redisSentinelConfiguration"
  2. class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
  3. <property name="master">
  4. <bean class="org.springframework.data.redis.connection.RedisNode">
  5. <property name="name" value="mymaster"></property>
  6. </bean>
  7. </property>
  8. <property name="sentinels">
  9. <set>
  10. <bean class="org.springframework.data.redis.connection.RedisNode">
  11. <constructor-arg index="0" value="10.6.1**.**5" />
  12. <constructor-arg index="1" value="7031" />
  13. </bean>
  14. <bean class="org.springframework.data.redis.connection.RedisNode">
  15. <constructor-arg index="0" value="10.6.1**.**6" />
  16. <constructor-arg index="1" value="7031" />
  17. </bean>
  18. <bean class="org.springframework.data.redis.connection.RedisNode">
  19. <constructor-arg index="0" value="10.6.1**.**1" />
  20. <constructor-arg index="1" value="7031" />
  21. </bean>
  22. </set>
  23. </property>
  24. </bean>
  25.  
  26. <bean id="jedisConnFactory"
  27. class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  28. <constructor-arg ref="redisSentinelConfiguration" />
  29. </bean>
  30.  
  31. <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
  32. <property name="connectionFactory" ref="jedisConnFactory" />
  33. </bean>

提示:上面配置中的端口为sentinel的端口,而非redis-server的端口。

这里我们使用Sentinel模式来配置redis连接,从上篇学习知道,sentinel是一种高可用架构,个人推荐在生产环境中使用sentinel模式。

注:26-28行,经试验,如果修改了默认端口,这里必须明细指定hostName及port,否则运行后,无法正确读写缓存,参考下面的配置:

(2016-4-2更新:最新1.6.4版的spring-data-redis 已经修正了这个问题,无需再指定端口和hostname)

  1. <bean id="jedisConnFactory"
  2. class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
  3. <property name="hostName" value="10.6.53.xxx"/>
  4. <property name="port" value="8830"/>
  5. <property name="usePool" value="false"/>
  6. <constructor-arg ref="redisSentinelConfiguration"/>
  7. </bean>

其中hostName为当前master的IP,port为redis-server的运行端口(非sentinel端口),此外还要设置usePool为false,由于sentinel可能会自行切换master节点,如果不清楚当前的master节点是哪台机器,可以用前面提到的命令./redis-cli -p <sentinal端口号> 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. }

另外<property name="usePool" value="false"/> 这里的value值如果改成true,经实际测试,发现偶尔会报如下错误(如果报错,换成false通常就可以了):

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.cnblogs.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<String> 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<String> 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<Object, Object> hashCache = stringRedisTemplate.opsForHash()
  45. .entries("myHash");
  46. for (Map.Entry<Object, Object> 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. }

运行一下,行云流水般的输出:

...

信息: Created JedisPool to master at 10.6.144.***:7030
http://yjmyzz.cnblogs.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<RedisServer> 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. }

输出类似下面的结果:

master => 172.20.16.19:6379
slaves of 172.20.16.19:6379 => 172.20.16.19: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<T> extends RedisTemplate<String, T> {
  10.  
  11. public ObjectRedisTemplate(RedisConnectionFactory connectionFactory,
  12. Class<T> clazz) {
  13.  
  14. RedisSerializer<T> objectSerializer = new Jackson2JsonRedisSerializer<T>(
  15. clazz);
  16.  
  17. RedisSerializer<String> objectKeySerializer = new Jackson2JsonRedisSerializer<String>(
  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. }

然后就可以这样用了:

  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<SampleBean> template = new ObjectRedisTemplate<SampleBean>(
  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. }

其中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. }

注:由于不是标准的String类型,所以在redis控制台,用./redis-cli get myBean是看不到缓存内容的,只能得到nil的输出,不要误以为set没成功!通过代码是可以正常get到缓存值的。

另外关于POJO对象的缓存,还有二个注意事项:

a) POJO类必须要有默认的无参构造函数,否则反序列化时会报错

b) ObjectRedisTemplate<T>中的T不能是接口,比如 DomainModelA继承自接口 IModelA,使用ObjectRedisTemplate时,要写成ObjectRedisTemplate<DomainModelA>而不是ObjectRedisTemplate<IModelA>,否则反序列化时也会出错

redis 学习笔记(5)-Spring与Jedis的集成的更多相关文章

  1. redis学习笔记(二)——java中jedis的简单使用

    redis怎么在java中使用,那就是要用到jedis了,jedis是redis的java版本的客户端实现,原本原本想上来就直接学spring整合redis的,但是一口吃个胖子,还是脚踏实地,从基础开 ...

  2. redis 学习笔记(7)-cluster 客户端(jedis)代码示例

    上节学习了cluster的搭建及redis-cli终端下如何操作,但是更常用的场景是在程序代码里对cluster读写,这需要redis-client对cluster模式的支持,目前spring-dat ...

  3. Redis学习笔记(4)—— Jedis入门

    一.Jedis介绍 Redis不仅是使用命令来操作,现在基本上主流的语言都有客户端支持,比如Java.C.C#.C++.php.Node.js.Go等. 在官方网站里列的一些Java客户端,有jedi ...

  4. Redis学习笔记7--Redis管道(pipeline)

    redis是一个cs模式的tcp server,使用和http类似的请求响应协议.一个client可以通过一个socket连接发起多个请求命令.每个请求命令发出后client通常会阻塞并等待redis ...

  5. redis学习笔记(详细)——高级篇

    redis学习笔记(详细)--初级篇 redis学习笔记(详细)--高级篇 redis配置文件介绍 linux环境下配置大于编程 redis 的配置文件位于 Redis 安装目录下,文件名为 redi ...

  6. redis 学习笔记(6)-cluster集群搭建

    上次写redis的学习笔记还是2014年,一转眼已经快2年过去了,在段时间里,redis最大的变化之一就是cluster功能的正式发布,以前要搞redis集群,得借助一致性hash来自己搞shardi ...

  7. Redis学习笔记~目录

    回到占占推荐博客索引 百度百科 redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合). ...

  8. Redis学习笔记4-Redis配置详解

    在Redis中直接启动redis-server服务时, 采用的是默认的配置文件.采用redis-server   xxx.conf 这样的方式可以按照指定的配置文件来运行Redis服务.按照本Redi ...

  9. Redis学习笔记一:数据结构与对象

    1. String(SDS) Redis使用自定义的一种字符串结构SDS来作为字符串的表示. 127.0.0.1:6379> set name liushijie OK 在如上操作中,name( ...

随机推荐

  1. php页面静态化

    如何优化页面响应时间: 动态页面静态化 优化数据库 使用负载均衡 使用缓存 如果页面中的一些内容不经常改动,可以使用动态页面静态化.好处是:减少服务器脚本的计算时间:降低服务器的响应时间. 1.动态U ...

  2. 转载文章----.NET 框架浅析

    转载地址:http://www.cnblogs.com/yangmingming/archive/2010/01/27/1657850.html .NET 框架概要: .NET框架,即.NET Fra ...

  3. [CMD]oracle数据库的导出导入

    除了推荐使用PL/SQL Developer 工具对oracle进行导出导入(http://www.cnblogs.com/whylaughing/p/5983490.html )之外,比较常用的还有 ...

  4. SSH新学

    流程:model-->dao-->service-->impService-->action 如果只是操作单个的一个表,比如user表,则都写到user的流程中 如果要操作俩个 ...

  5. ORACLE绑定变量隐式转换导致性能问题

    年后一次系统升级后,监控数据库的工具DPA发现数据库的Total Wait时间突然飙增,如下截图所示,数据库的总体等待时间对比升级前飙增了非常多 另外就是发现出现了较多的等待事件,主要有latch: ...

  6. SQLServer中给表增加组合唯一约束

    将两个或者多个字段一起约束成一个唯一约束 alter table 表名 add constraint 约束名 unique (列名1,列名2)

  7. 今天说一下Order by 这个常规东西~

    Order by 在我们日常的数据库开发生活中是出镜率灰常高的. order by 的作用就是用于对查询出来的结果进行排序~对啊~人家就是这么接地气~比如按发生时间啊,首字母啊之类的都是相当常见. 今 ...

  8. setTimeout()与setInterval()——走马灯效果

    JavaScript中的setTimeout()与setInterval()都是指延时执行某一操作. 但setInterval()指每隔指定时间执行某操作,会循环不断地执行该操作:setTimeout ...

  9. Linux rpmbuild命令

    一.简介 rpmbuild命令用于创建软件的二进制包和源代码包. 二.选项 参考:http://blog.sina.com.cn/s/blog_4ba5b45e0102e5r2.html http:/ ...

  10. Oracle Dataguard的原理与基本配置

    最近集团在做灾备方案,用于Oracle的高可用性,在不影响主库性能的前提下,我们选择使用DG的"最大性能"模式.   DG是Oracle数据库自带的数据同步功能,其基本原理是将日志 ...