redis学习记录(四)-SpringDataRedis分析

redis学习记录(四)-SpringDataRedis分析

标签(空格分隔): redis


个人独立博客: http://mrdear.cn

Redis学习记录(一)–入门知识
Redis学习记录(二)–使用Jedis连接
redis学习记录(三)-redis中的数据结构

1.简介

Spring Data Redis是对redis客户端(如jedis)的高度封装,支持多种客户端,因其高抽象,所以在某一个客户端不支持更新的时候可以容易切换到其他客户端.

本文是在Spring boot 1.5.2版本下测试.

需要引入架包


    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <!--spring boot start-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

2.配置

在Spring Boot下默认使用jedis作为客户端,并在包org.springframework.boot.autoconfigure.data.redis下,提供自动配置类RedisProperties,RedisAutoConfiguration等.

根据RedisProperties可以定位到可配置的属性,如:

# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址
spring.redis.host=115.159.185.14
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=8
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=2000

在application.properties中配置即可,另外还有SentinelCluster说明支持分布式和集群,博主研究不多就不瞎说这个了.

自动配置主要在RedisAutoConfiguration中,该类会提供三个bean:
1. JedisConnectionFactory : jedis连接控制工厂
2. RedisTemplate

3.RedisTemplate

3.1 Test case

那么具体操作过程是怎么样子的呢?写一个简单的测试去跟踪代码,如下代码,往redis中设置key为ping的字串.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
public class RedisConnectTest {
  @Resource
  private RedisTemplate<String,String> redisTemplate;

  @Test
  public void testSetAndGet() {
    redisTemplate.opsForValue().set("ping","pong");
    System.out.println(redisTemplate.opsForValue().get("ping"));
  }
}

运行之后查看redis数据库,你会发现很奇怪的事情,如下图,代码中存入的是ping,但是到redis中后却是一堆字符+ping,这个原因是什么呢?接着跟踪代码.

3.2 XXXOperations

3.3 XXXSerializer

那测试代码中第一步是获取了string类型的redis操作入口,然后执行set方法设置键和值,接着分析set方法.

    public void set(K key, V value) {
        final byte[] rawValue = rawValue(value);
        execute(new ValueDeserializingRedisCallback(key) {

            protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
                connection.set(rawKey, rawValue);
                return null;
            }
        }, true);
    }

可以发现rawKey()方法和rawValue()方法对key和value进行了一次序列化操作.该序列化使用的类为RedisTemplate中的XXXSerializer,那么回到RedisTemplate,在afterPropertiesSet()方法中有以下初始化方法,默认使用的序列化方式为JdkSerializationRedisSerializer,也就是ObjectInputStream和ObjectOutputStream写入和读取.这也是写入到redis中却在redis数据库通过”ping”访问不到的原因.

if (defaultSerializer == null) {

            defaultSerializer = new JdkSerializationRedisSerializer(
                    classLoader != null ? classLoader : this.getClass().getClassLoader());
        }
        if (enableDefaultSerializer) {
            if (keySerializer == null) {
                keySerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (valueSerializer == null) {
                valueSerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (hashKeySerializer == null) {
                hashKeySerializer = defaultSerializer;
                defaultUsed = true;
            }
            if (hashValueSerializer == null) {
                hashValueSerializer = defaultSerializer;
                defaultUsed = true;
            }
        }

那么SpringDataRedis支持哪些序列化呢?从官网可以看到:
StringRedisSerializer: string类型序列化,也是最常用的类型
JdkSerializationRedisSerializer: jdk默认序列化
OxmSerializer : xml格式
JacksonJsonRedisSerializer : json格式

通过手动注入RedisTemplate,更改所选择的序列化方式.另外Spring提供了最常使用的StringRedisTemplate,实现了StringRedisSerializer序列化方式.

    public StringRedisTemplate() {
        RedisSerializer<String> stringSerializer = new StringRedisSerializer();
        setKeySerializer(stringSerializer);
        setValueSerializer(stringSerializer);
        setHashKeySerializer(stringSerializer);
        setHashValueSerializer(stringSerializer);
    }

更改成StringRedisTemplate,再次执行,正常了.

3.4 总结过程

  1. 获取RedisTemplate
  2. 获取操作入口XXXOperations
  3. 使用RedisSerializer序列化key和value
  4. 获取conn连接
  5. 执行命令

4.发布与订阅

发布与订阅过程需要发布者,订阅者,以及把两者连在一起的桥梁.那么在SpringRedis中怎么实现呢?
订阅者:里面有一个处理方法即可.

@Component
public class Listen {

  private static Logger logger = LoggerFactory.getLogger(Listen.class);

  private CountDownLatch latch = new CountDownLatch(1);

  public void handleMsg(String message) {
    logger.info("reciver msg :" + message);
    latch.countDown();
  }

  public CountDownLatch getLatch() {
    return latch;
  }
}

发布者:XXXRedisTemplate.convertAndSend(chanel,msg)即作为发布者存在.

连接桥梁:RedisMessageListenerContainer,该container监听Redis的消息,分发给各自的监听者.关键代码为

@Configuration
public class PublishConfig {
  /**
   * 注入消息容器
   * @param jedisConnectionFactory jedis连接池
   * @param listenerAdapter 监听适配器
   * @return bean
   */
  @Bean
  public RedisMessageListenerContainer container(RedisConnectionFactory jedisConnectionFactory,
      MessageListenerAdapter listenerAdapter){
    RedisMessageListenerContainer container = new RedisMessageListenerContainer();
    container.setConnectionFactory(jedisConnectionFactory);
    //绑定监听者与信道的管理
    container.addMessageListener(listenerAdapter,new PatternTopic("java"));
    return container;
  }

  @Bean
  public MessageListenerAdapter adapter(Listen listen){
    //指定监听者和监听方法
    return new MessageListenerAdapter(listen,"handleMsg");
  }
}

测试:

  @Test
  public void testPublish() throws InterruptedException {
    stringRedisTemplate.convertAndSend("java","hello world");
    listen.getLatch().await();
  }

5.事务

对于事务的操作是通过SessionCallback实现,该接口保证其内部所有操作都是在同一个Session中的,在最后exec的时候执行全部操作.关键代码如下

    RedisConnectionUtils.bindConnection(factory, enableTransactionSupport);
    execute(this)
 @Test
  public void testMulti() {
    boolean isThrow = false;
    List<Object> result = null;
    try {
      result = stringRedisTemplate.execute(new SessionCallback<List<Object>>() {
        @Override
        public List<Object> execute(RedisOperations operations) throws
            DataAccessException {
          operations.multi();
          ValueOperations<String,String> ops = operations.opsForValue();
          ops.set("ping1","pong1");
          ops.set("ping2","pong2");
          if (isThrow){
            throw new IllegalArgumentException("测试异常");
          }
          return operations.exec();
        }
      });
    } catch (Exception e) {
      e.printStackTrace();
    }

    System.out.println(result);
  }

6.管道

直接引用官方案例

//pop a specified number of items from a queue
List<Object> results = stringRedisTemplate.executePipelined(
  new RedisCallback<Object>() {
    public Object doInRedis(RedisConnection connection) throws DataAccessException {
      StringRedisConnection stringRedisConn = (StringRedisConnection)connection;
      for(int i=0; i< batchSize; i++) {
        stringRedisConn.rPop("myqueue");
      }
    return null;
  }
});

还有脚本执行等,在官方文档中都有案例,这里就不复制粘贴了,如有错误请指出,不胜感激.

参考文档:

http://docs.spring.io/spring-data/redis/docs/1.8.1.RELEASE/reference/html/#redis:template

github:

https://github.com/nl101531/JavaWEB

猜你喜欢

转载自blog.csdn.net/u012706811/article/details/68950937
今日推荐