Spring与Jedis的集成

转自:http://www.cnblogs.com/yjmyzz/p/4113019.html


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

 

使用步骤:

一、pom添加依赖项

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.4.1.RELEASE</version>
        </dependency>

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

观察一下:

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

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

 

二、spring配置

<bean id="redisSentinelConfiguration"
        class="org.springframework.data.redis.connection.RedisSentinelConfiguration">
        <property name="master">
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <property name="name" value="mymaster"></property>
            </bean>
        </property>
        <property name="sentinels">
            <set>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg index="0" value="10.6.1**.**5" />
                    <constructor-arg index="1" value="7031" />                    
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">
                    <constructor-arg index="0" value="10.6.1**.**6" />
                    <constructor-arg index="1" value="7031" />                
                </bean>
                <bean class="org.springframework.data.redis.connection.RedisNode">                    
                    <constructor-arg index="0" value="10.6.1**.**1" />
                    <constructor-arg index="1" value="7031" />                
                </bean>
            </set>
        </property>
    </bean>

     <bean id="jedisConnFactory"
        class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <constructor-arg ref="redisSentinelConfiguration" />        
    </bean>

    <bean id="stringRedisTemplate" class="org.springframework.data.redis.core.StringRedisTemplate">
        <property name="connectionFactory" ref="jedisConnFactory" />
    </bean>


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

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

复制代码
    <bean id="jedisConnFactory"
          class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="10.6.53.xxx"/>
        <property name="port" value="8830"/>
        <property name="usePool" value="false"/>
        <constructor-arg ref="redisSentinelConfiguration"/>
    </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值建议设置成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写入缓存.

 

三、单元测试

@Test
    public void testSpringRedis() {
        ConfigurableApplicationContext ctx = null;
        try {
            ctx = new ClassPathXmlApplicationContext("spring.xml");

            StringRedisTemplate stringRedisTemplate = ctx.getBean("stringRedisTemplate", StringRedisTemplate.class);

            // String读写
            stringRedisTemplate.delete("myStr");
            stringRedisTemplate.opsForValue().set("myStr", "http://yjmyzz.cnblogs.com/");
            System.out.println(stringRedisTemplate.opsForValue().get("myStr"));
            System.out.println("---------------");

            // List读写
            stringRedisTemplate.delete("myList");
            stringRedisTemplate.opsForList().rightPush("myList", "A");
            stringRedisTemplate.opsForList().rightPush("myList", "B");
            stringRedisTemplate.opsForList().leftPush("myList", "0");
            List<String> listCache = stringRedisTemplate.opsForList().range(
                    "myList", 0, -1);
            for (String s : listCache) {
                System.out.println(s);
            }
            System.out.println("---------------");

            // Set读写
            stringRedisTemplate.delete("mySet");
            stringRedisTemplate.opsForSet().add("mySet", "A");
            stringRedisTemplate.opsForSet().add("mySet", "B");
            stringRedisTemplate.opsForSet().add("mySet", "C");
            Set<String> setCache = stringRedisTemplate.opsForSet().members(
                    "mySet");
            for (String s : setCache) {
                System.out.println(s);
            }
            System.out.println("---------------");

            // Hash读写
            stringRedisTemplate.delete("myHash");
            stringRedisTemplate.opsForHash().put("myHash", "PEK", "北京");
            stringRedisTemplate.opsForHash().put("myHash", "SHA", "上海虹桥");
            stringRedisTemplate.opsForHash().put("myHash", "PVG", "浦东");
            Map<Object, Object> hashCache = stringRedisTemplate.opsForHash()
                    .entries("myHash");
            for (Map.Entry<Object, Object> entry : hashCache.entrySet()) {
                System.out.println(entry.getKey() + " - " + entry.getValue());
            }

            System.out.println("---------------");

        } finally {
            if (ctx != null && ctx.isActive()) {
                ctx.close();
            }
        }

    }


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

...

信息: 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可以参考以下代码

@Test
    public void testGetAllMasterAndSlaves() {
        ApplicationContext ctx = new FileSystemXmlApplicationContext("D:/spring-redis.xml");
        StringRedisTemplate template = ctx.getBean(StringRedisTemplate.class);
        RedisSentinelConnection conn = template.getConnectionFactory().getSentinelConnection();
        for (RedisServer m : conn.masters()) {
            System.out.println("master => " + m);//打印master信息
            Collection<RedisServer> slaves = conn.slaves(m);
            //打印该master下的所有slave信息
            for (RedisServer s : slaves) {
                System.out.println("slaves of " + m + " => " + s);
            }
            System.out.println("--------------");
        }
        ((FileSystemXmlApplicationContext) ctx).close();
    }


输出类似下面的结果:

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

package org.springframework.data.redis.core;

import org.springframework.data.redis.connection.DefaultStringRedisConnection;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;

public class ObjectRedisTemplate<T> extends RedisTemplate<String, T> {

    public ObjectRedisTemplate(RedisConnectionFactory connectionFactory,
            Class<T> clazz) {

        RedisSerializer<T> objectSerializer = new Jackson2JsonRedisSerializer<T>(
                clazz);

        RedisSerializer<String> objectKeySerializer = new Jackson2JsonRedisSerializer<String>(
                String.class);

        setKeySerializer(objectKeySerializer);
        setValueSerializer(objectSerializer);
        setHashKeySerializer(objectSerializer);
        setHashValueSerializer(objectSerializer);

        setConnectionFactory(connectionFactory);
        afterPropertiesSet();
    }

    protected RedisConnection preProcessConnection(RedisConnection connection,
            boolean existingConnection) {
        return new DefaultStringRedisConnection(connection);
    }
}


然后就可以这样用了:

@Test
    public void testSpringRedis() {
        ConfigurableApplicationContext ctx = null;
        try {
            ctx = new ClassPathXmlApplicationContext("spring.xml");

            JedisConnectionFactory connFactory = ctx.getBean(
                    "jedisConnFactory", JedisConnectionFactory.class);

            ObjectRedisTemplate<SampleBean> template = new ObjectRedisTemplate<SampleBean>(
                    connFactory, SampleBean.class);

            template.delete("myBean");
            SampleBean bean = new SampleBean("菩提树下的杨过");
            template.opsForValue().set("myBean", bean);

            System.out.println(template.opsForValue().get("myBean"));
            
        } finally {
            if (ctx != null && ctx.isActive()) {
                ctx.close();
            }
        }
    }


其中SampleBean的定义如下:

package com.cnblogs.yjmyzz;

import java.io.Serializable;

public class SampleBean implements Serializable {

    private static final long serialVersionUID = -303232410998377570L;

    private String name;

    public SampleBean() {
    }

    public SampleBean(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String toString() {
        return "name:" + name;
    }

}


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

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

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

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

作者: 菩提树下的杨过
出处: http://yjmyzz.cnblogs.com 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值