第02课:Redis

第02课:Redis 通过 SpringData 的使用和配置

本课程我们来看看 Spring Boot 和 Spring Data 模式下面怎么玩?

第1部分:基于 Spring Boot 的配置方法

我们以 Spring Boot 为基础看一下 Redis 在里面是怎么兼容和配置的。

以 Spring Boot 为例分别介绍一下这四种配置方法:

  • 基本使用;
  • 连接池的使用;
  • 高可用连接(master/salve);
  • 客户端分片。

添加 Spring Data Redis 依赖

如果是 Spring Boot 项目直接添加 spring-boot-starter-data-redis 即可。

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

第1步:分析一下源码

一旦当我们使用 Spring Boot,其实任何一个 starter 都会引入 spring-boot-autoconfigure 的 jar 包,然后 autoconfigure 就会做很多事情。

SpringBootStarterRedis 源码分析

我们用 Spring Boot 都知道 starter 的原理(spring-boot-autoconfigure.jar 包里面的 spring.factories 定义了 Spring Boot 默认加载的 AutoConfiguration),因此,打开 spring.factories 文件可以找到 Spring 自动加载了。

``` org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration, org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,

这两个 Configuration 类,我们先打开 RedisAutoConfiguration 的源码 ,来一起看一下里面的关键代码片段。

(1)代码片段一:自动加载 JedisConnectionFactory。

@Bean @ConditionalOnMissingBean(RedisConnectionFactory.class) public JedisConnectionFactory redisConnectionFactory() throws UnknownHostException { return applyProperties(createJedisConnectionFactory()); } ```

通过这一段代码可以看到,JedisConnectionFactory 可以自己配置也可以直接用 Spring Boot 给我们提供的默认配置。

(2)代码片段二:查看 createJedisConnectionFactory() 的具体方法。

private JedisConnectionFactory createJedisConnectionFactory() {
    //这里会取我们配置文件里面的配置,如果没有配置,new 一个默认连接池
    JedisPoolConfig poolConfig = this.properties.getPool() != null
            ? jedisPoolConfig() : new JedisPoolConfig();

    //如果配置了Sentinel就取哨兵的配置直接返回
    if (getSentinelConfig() != null) {
        return new JedisConnectionFactory(getSentinelConfig(), poolConfig);
    }
    //如果没有配置中Sentinel,而配置了Cluster切片的配置方法,它就取Cluster的配置方法
    if (getClusterConfiguration() != null) {
        return new JedisConnectionFactory(getClusterConfiguration(), poolConfig);
    }
    //默认取连接pool的配置方法
    return new JedisConnectionFactory(poolConfig);
}
.......
//取配置文件里面的Pool的配置
private JedisPoolConfig jedisPoolConfig() {
    JedisPoolConfig config = new JedisPoolConfig();
    RedisProperties.Pool props = this.properties.getPool();
    config.setMaxTotal(props.getMaxActive());
    config.setMaxIdle(props.getMaxIdle());
    config.setMinIdle(props.getMinIdle());
    config.setMaxWaitMillis(props.getMaxWait());
    return config;
}
.......
//JedisPoolConfig的类默认构造函数
public class JedisPoolConfig extends GenericObjectPoolConfig {
    public JedisPoolConfig() {
        this.setTestWhileIdle(true);
        this.setMinEvictableIdleTimeMillis(60000L);
        this.setTimeBetweenEvictionRunsMillis(30000L);
        this.setNumTestsPerEvictionRun(-1);
    }
}

通过这段代码可以看出来,前面讲到的四种 Jedis 的配置方式,这里默认只支持了三种。而上一节我们讲的第一种单例的基本模式 Spring 不推荐使用,而 Spring 将 Pool 作为默认的配置方法。

可以看出:

其一,三种配置中 Pool(连接池)、Sentinel(哨兵,master/slave)和 Cluster(切片)只能选择一种来配置。

其二,只需要配置我们的配置文件就可以了,剩下的就交给 Spring 的 JedisConnectionFactory。

(3)代码片段三:查看 RedisAutoConfiguration 的关键源码。

@Configuration
@ConditionalOnClass({ JedisConnection.class, RedisOperations.class, Jedis.class })
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
......
}
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    /**
     * Database index used by the connection factory.
     */
    private int database = 0;
    /**
     * Redis url, which will overrule host, port and password if set.
     */
    private String url;
    /**
     * Redis server host.
     */
    private String host = "localhost";
    /**
     * Login password of the redis server.
     */
    private String password;
    /**
     * Redis server port.
     */
    private int port = 6379;
    /**
     * Enable SSL.
     */
    private boolean ssl;
    /**
     * Connection timeout in milliseconds.
     */
    private int timeout;

    private Pool pool;

    private Sentinel sentinel;

    private Cluster cluster;
    ......
}

这里省略了一些中间的代码,有兴趣的读者可以看一下源码,到这一步,其实已经发现了配置文件应该怎么配置了(PS:字段名字 +spring.redis 前缀就是 application.yml 里面的 key了)。如果使用 Intellij IDEA,当在 application.properties 输入 spring.redis 开头的 key 值的时候会给我们提示这类里面的属性值。

(4)代码片段四: RedisConfiguration 关键源码。

    /**
     * Standard Redis configuration.
     */
    @Configuration
    protected static class RedisConfiguration {

        @Bean
        @ConditionalOnMissingBean(name = "redisTemplate")
        public RedisTemplate<Object, Object> redisTemplate(
                RedisConnectionFactory redisConnectionFactory)
                        throws UnknownHostException {
            RedisTemplate<Object, Object> template = new RedisTemplate<Object, Object>();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }

        @Bean
        @ConditionalOnMissingBean(StringRedisTemplate.class)
        public StringRedisTemplate stringRedisTemplate(
                RedisConnectionFactory redisConnectionFactory)
                        throws UnknownHostException {
            StringRedisTemplate template = new StringRedisTemplate();
            template.setConnectionFactory(redisConnectionFactory);
            return template;
        }

    }

由以上代码可知,我们可以使用类 RedisTemplate 或者 StringRedisTemplate 来直接操作 Redis 的 client 的相关 API(PS:后面有介绍)。

第2步:配置方法

通过源码分析得出来了和第二节相对应的配置方法,具体如下。

基本使用配置方法 & 连接池的配置方法

只需要在我们的 application.properties 里面增加如下配置即可。

spring.redis.host=127.0.0.1 #redis服务器地址
spring.redis.port=6379 #端口
spring.redis.timeout=6000 #连接超时时间 毫秒
spring.redis.pool.max-active=8 # 连接池的配置,最大连接激活数
spring.redis.pool.max-idle=8 # 连接池配置,最大空闲数
spring.redis.pool.max-wait=-1 #  连接池配置,最大等待时间
spring.redis.pool.min-idle=0 # 连接池配置,最小空闲活动连接数

调用的地方就可以直接引用 redisTemplate 进行使用了。这时候启动的 pool 的很多设置,如果不配置 pool 的一些相关参数,我们看源码的话,也会发现启动 JedisPoolConfig 里面的默认配置(源码分析里面的代码片段二里面的内容)。

实例 DEMO1:

//例如某个Service里面只需要引用RedisTemplate类即可:
@Autowired
private static RedisTemplate redisTemplate;
//某个service方法中,直接调用redisTemplate操作redis的set集合,储存key和value
public Object cacheAround(String key,String value) throws Throwable {
    ....
    //直接调用redisTemplate操作redis的set集合,储存key和value
    redisTemplate.opsForSet().add(key,value);
    //这里不需要,关心redisTemplate里面配置的是连接池,还是哨兵,还是cluster。
    .....
}
sentinel 哨兵的高可用配置方法

我们可以看 RedisProperties 的 Sentinel 类得出如下配置方式:只需要在 application.properties 里面配置如下内容即可。

spring.redis.sentinel.master= redis_master_name #master 名字
spring.redis.sentinel.nodes= 127.0.0.1:63791,127.0.0.1:63792  #我们配置多个哨兵,用","分割即可

使用的地方保持不变,这就体现了 Spring 的大牛们超强的封装思想,当改变 Redis 的 Server 使用方式的时候,让 redisTemplate 使用的地方不受任何影响,这里体现了 Java 的封装思想(因为我们只需要改变配置文件即可,调用的地方不需要发生任何改变,如上面的实例 DEMO1)。

Cluster 分布式切片的配置方法

RedisProperties 的 Cluster 类得出如下配置方式:只需要在 application.properties 里面配置如下内容即可。

spring.redis.cluster.max-redirects=3 # Maximum number of redirects to follow when executing commands across the cluster.
spring.redis.cluster.nodes= 127.0.0.1:6379,127.0.0.1:6376,127.0.0.1:6378# Comma-separated list of "host:port" pairs to bootstrap from.

第3步:调用的地方

DEMO2:通过 RedisTemplate 直接操作 key/value。

```
//声明
@Resource(name = "redisTemplate") private RedisTemplate<String, String> template;

//调用方法 template.opsForValue().set("key","value");

DEMO3:通过注入 resdisTemplate 操作 ValueOperations。  

//RedisTemplate还提供了对应的*OperationsEditor,用来通过RedisTemplate直接注入对应的Operation。 //声明 @Resource(name = "redisTemplate") private ValueOperations<String, Object> vOps;

//调用方法 vOps.set("key","value");

DEMO4:通过注解 HashOperations 操作 Hash 数据结构。

//注入HashOperations对象 @Resource(name = "redisTemplate") private HashOperations<String,String,Object> hashOps;

//具体调用 Map<String,String> map = new HashMap<String, String>(); map.put("value","code"); map.put("key","keyValue"); hashOps.putAll("hashOps",map);

DEM5:通过 template 操作 Hash 数据结构。

//注入RedisTemplate对象 @Resource(name = "redisTemplate") private RedisTemplate<String, String> template;

//具体调用 Map<String,String> map = new HashMap<String, String>(); map.put("value","code"); map.put("key","keyValue"); template.opsForHash().putAll("hashOps",map);

#### 第4步:总结

我们总结一下 Spring Boot 的配置方法:

- 引入 spring-boot-starter-data-redis.jar 包的依赖。
- 修改 application.properties 文件的三种配置方法即可。
- 直接调用 RedisTemplate 进行 Redis 的相关操作。

### 第2部分:直接使用 spring-data-redis

当我们没有引入 Spring Boot 的时候怎么办?

添加 Spring Data Redis 的 jar 包依赖即可,maven 方式如下:

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

Bean 和配置文件的加载:写的优雅一点的话,就是将上面 Spring Boot Redis 的源码分析的部分重新实现一遍,定义自己的 RedisAutoConfiguration 和 RedisProperties 及其 Properties(prefix = "spring.redis")。这里不在重复叙述了。

### 第3部分:概况 & API

#### 主要的几个类 & 简单用法介绍

看一下我们要关心的几个重要的类图:

![redis class graph](http://images.gitbook.cn/04c29d90-e7cb-11e7-8d56-2d95710ec169)

![redis class graph](http://images.gitbook.cn/0a289aa0-e7cb-11e7-a510-4914b777019d)

1)JedisConnectionFactory 里面依赖了 JedicConnection 和 JedisPoolConfig、RedisSentineConfiguration、RedisCLusterConfiguration 的三种配置方法。    
2)RedisAutoConfiguration 自动加载了 RedisProperties 的配置文件。    
3)RedisTemplate 抽象保证了 Redis 相关的 Operations 方法。    
4)StringRedisTemplate 继承和扩展了 RedisTemplate,为我们提供了一种扩展思路。    

我们主要关心的类 RedisTemplate 的 API 简单介绍:

redisTemplate.opsForValue();//操作字符串 redisTemplate.opsForHash();//操作 hash redisTemplate.opsForList();//操作 list redisTemplate.opsForSet();//操作 set redisTemplate.opsForZSet();//操作有序 set ```

StringRedisTemplate 与 RedisTemplate 的封装的 Reids 操作要比我们第二节讲的自己调用 Jedis 的 API 的方式更优雅了一步。

而 StringRedisTemplate 与 RedisTemplate 对应 API 详细请查看此文档 Spring Data Redis 官方操作手册 ,这里不是本节的重点介绍对象了,不再赘述。

Jedis 实际工作中的正确使用场景

第 N 种做法:这个时候其实可以对上一节介绍的 RedisUtil 的使用用法做改进和优化了,我们不需要自己去管理 Jedis 的相关的东西了,相应的地方我们直接调用 RedisTemplate 就可以解决了很多问题。但是到目前为止还不是最优解,我们接着往后面看,会发现自定义注解这一块也可以不需要。

上一篇
下一篇
目录