1. 程式人生 > >【Spring Boot學習總結】14.Spring Boot整合Redis-與傳統方式對比

【Spring Boot學習總結】14.Spring Boot整合Redis-與傳統方式對比

前面我們講解了如何使用Spring Boot來控制事務,下面我們來講解一下如何使用Spring Boot來整合Redis
為了對比傳統工程與Spring Boot整合的不同,以及彰顯Spring Boot整合的優勢,我們會逐一剖析傳統整合方式與Spring Boot整合方式。

一、傳統方式整合Redis

在不使用Spring Boot的傳統工程中,我們使用XML配置檔案來整合Redis。首先在POM檔案中引入Redis的相關依賴:

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
    <version>1.6.0.RELEASE</version>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.7.3</version>
</dependency>

然後建立一個redis.properties檔案,用於配置redis的連線資訊:

# Redis伺服器地址
redis.host=127.0.0.1
# Redis伺服器連線埠
redis.port=6379  
# Redis伺服器連線密碼(預設為空)
redis.pass=password
redis.dbIndex=0  
# Redis中Key的過期時間
redis.expiration=3000  
# 連線池中的最大空閒連線
redis.maxIdle=300  
# 連線池最大連線數(使用負值表示沒有限制)
redis.maxActive=600  
# 連線池最大阻塞等待時間(使用負值表示沒有限制)
redis.maxWait=1000  
redis.testOnBorrow=true

然後在Spring的配置檔案中(含有資料來源、sessionFactory等Bean配置的檔案),新增Redis的配置:

<!-- redis config start -->
<!-- 配置JedisPoolConfig例項 -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
    <property name="maxIdle" value="${redis.maxIdle}" />
    <property name="maxTotal" value="${redis.maxActive}" />
    <property name="maxWaitMillis" value="${redis.maxWait}" />
    <property name="testOnBorrow" value="${redis.testOnBorrow}" />
</bean>

<!-- 配置JedisConnectionFactory -->
<bean id="jedisConnectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <property name="hostName" value="${redis.host}" />
    <property name="port" value="${redis.port}" />
    <property name="password" value="${redis.pass}" />
    <property name="database" value="${redis.dbIndex}" />
    <property name="poolConfig" ref="poolConfig" />
</bean>

<!-- 配置RedisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
    <property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>

<!-- 配置RedisCacheManager -->
<bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
    <constructor-arg name="redisOperations" ref="redisTemplate" />
    <property name="defaultExpiration" value="${redis.expiration}" />
</bean>

<!-- 配置RedisCacheConfig -->
<bean id="redisCacheConfig" class="com.ssm.utils.RedisCacheConfig">
    <constructor-arg ref="jedisConnectionFactory" />
    <constructor-arg ref="redisTemplate" />
    <constructor-arg ref="redisCacheManager" />
</bean>
<!-- redis config end -->

其中分別配置了JedisPoolConfig、JedisConnectionFactory、RedisTemplate、RedisCacheManager、RedisCacheConfig,其中:
(1)JedisPoolConfig
JedisPoolConfig類中設定了redis連線池的配置資訊,用於通過配置資訊來控制redis資料庫連線池的一些連線操作。

(2)JedisConnectionFactory
JedisConnectionFactory類為redis資料庫的連線工廠類,類似引入了DataSource的SqlSessionFactory資料庫會話工廠。
該類通過配置的redis資料庫的連線資訊,來與redis資料庫進行連線操作,並返回相應的連線例項物件。

(3)RedisTemplate
RedisTemplate類是redis操作類,通過引入JedisConnectionFactory連線工廠獲取連線,對外提供redis的各種操作API,
在專案中可以直接使用RedisTemplate類來進行redis資料庫的操作。

(4)RedisCacheManager
RedisCacheManager為redis自定義的工具類,在構造方法中引入了RedisTemplate,旨在對redis資料庫中的key-value資料進行管理,例如設定了key的失效時間等。

(5)RedisCacheConfig
RedisCacheConfig類是需要開發者自定義的類,用於為redis做統一的排程和管理。該類需繼承CachingConfigurerSupport父類,分別注入了JedisConnectionFactory、RedisTemplate以及RedisCacheManager。
下面是一個自定義RedisCacheConfig:

package com.ssm.utils;
import java.lang.reflect.Method;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

/**
 * 通過spring管理redis快取配置
 * @author Administrator
 */
@Configuration  
@EnableCaching 
public class RedisCacheConfig extends CachingConfigurerSupport {
    private volatile JedisConnectionFactory jedisConnectionFactory;
    private volatile RedisTemplate<String, String> redisTemplate;
    private volatile RedisCacheManager redisCacheManager;

    public RedisCacheConfig() {
        super();
    }

    /**
     * 帶引數的構造方法 初始化所有的成員變數
     * 
     * @param jedisConnectionFactory
     * @param redisTemplate
     * @param redisCacheManager
     */
    public RedisCacheConfig(JedisConnectionFactory jedisConnectionFactory, RedisTemplate<String, String> redisTemplate,
            RedisCacheManager redisCacheManager) {
        this.jedisConnectionFactory = jedisConnectionFactory;
        this.redisTemplate = redisTemplate;
        this.redisCacheManager = redisCacheManager;
    }

    public JedisConnectionFactory getJedisConnecionFactory() {
        return jedisConnectionFactory;
    }

    public RedisTemplate<String, String> getRedisTemplate() {
        return redisTemplate;
    }

    public RedisCacheManager getRedisCacheManager() {
        return redisCacheManager;
    }

    @Bean
    public KeyGenerator customKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... objects) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : objects) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }
}

該類繼承CachingConfigurerSupport,用於手動修改redis相關設定。這裡重寫customKeyGenerator方法,提供了key的預設生成方式(類名+方法名)。下面就是在我們的Service中使用快取,有兩種方式,第一種就是開發一個工具類xxxRedisUtils,將RedisTemplate類注入進該類,然後提供各種操作,如下類:

@Service
public class RedisCacheUtil<T>{

     @Autowired @Qualifier("jedisTemplate")
     public RedisTemplate redisTemplate;

     /**
      * 快取基本的物件,Integer、String、實體類等
      * @param key 快取的鍵值
      * @param value 快取的值
      * @return  快取的物件
      */
     public <T> ValueOperations<String,T> setCacheObject(String key,T value){
          ValueOperations<String,T> operation = redisTemplate.opsForValue(); 
          operation.set(key,value);
          return operation;
     }
     
     //其它各種操作省略...
}

然後在Service中注入RedisCacheUtil類進行快取的操作即可:

@Autowired
private RedisCacheUtil<User> redisCache;

第二種就是在Service直接使用註解的方式來進行快取的操作,這種也是比較方便的方式:
有兩種常用註解,分別是@Cacheable和@CacheEvict,用於快取的設定和清理:
(1)@Cacheable("a")
該註解的意義就是把該方法的查詢結果放到redis中去,下一次再發起查詢就去redis中去取,存在redis中的資料的key就是a;
(2)@CacheEvict(value={"a","b"},allEntries=true) 
該註解的意思就是執行該方法後要清除redis中key名稱為a,b的資料;使用樣例:

@Cacheable("getUserById")//標註該方法查詢的結果進入快取,再次訪問時直接讀取快取中的資料
@Override
public User getUserById(int userId) {
    return this.iUserDao.selectByPrimaryKey(userId);
}

@CacheEvict(value= {"getAllUser","getUserById","findUsers"},allEntries=true)//清空快取,allEntries變量表示所有物件的快取都清除
@Override
public void insertUser(User user) {
    this.iUserDao.insertUser(user);
}

以上就是使用傳統方式整合redis的操作方式。可以看出配置步驟還是比較繁瑣的。


二、使用Spring Boot整合Redis原理剖析

說白了,Spring Boot針對Redis的整合,有一部分依賴了Spring Boot的自動配置機制。
我們先來了解一下Spring Boot針對Spring Boot的自動化配置原理:
一般我們在使用Spring Boot的時候要編寫一個啟動類:

package cn.com.springboot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//Sprnig Boot專案的核心註解,主要目的是開啟自動配置
public class MainApplication {
    //該main方法作為專案啟動的入口
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

因為Spring Boot可以開啟自動配置(@SpringBootApplication中的@EnableAutoConfiguration),SpringBootApplication物件例項化時會載入spring-boot的jar中的META-INF/spring.factories檔案,
在該檔案中設定了需要進行自動配置載入的實體類,其中就包含了redis的相關自動配置類:

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

其中RedisAutoConfiguration類會讀取RedisProperties類中的引數作為配置項,而RedisProperties中的連線引數,是讀取使用者在properties中配置的,以“spring.redis”開頭的配置項,通過原始碼可以看到:

package org.springframework.boot.autoconfigure.data.redis;
import java.util.List;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    //內容原始碼省略...
}

RedisAutoConfiguration就是幫我們自動做了一些列的配置,我們在開發中只需要修改少量配置指向自己的redis服務地址即可。

那我們其實很清晰了,我們只需要在Spring Boot的properties檔案中,使用“spring.redis”字首進行redis連線資訊的配置,就可以使用redis了!是不是很方便!

下面就是Spring Boot連整合redis的方式。

三、使用Spring Boot整合Redis步驟

首先在POM檔案中引入redis的啟動依賴:

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

然後在修改專案啟動類,增加註解@EnableCaching,開啟快取功能,如下:

package cn.com.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
//Sprnig Boot專案的核心註解,主要目的是開啟自動配置
@EnableScheduling
@EnableCaching
public class MainApplication {
    //該main方法作為專案啟動的入口
    public static void main(String[] args) {
        SpringApplication.run(MainApplication.class, args);
    }
}

然後在application.properties中配置Redis連線資訊,如下:

# Redis資料庫索引(預設為0)
spring.redis.database=0
# Redis伺服器地址
spring.redis.host=172.31.19.222
# 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=0

然後編寫一個Redis快取配置類RedisConfig,用於定義快取key生成策略、快取管理器、RedisTemplate等。如下:

package springboot.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;


/**
 * Redis快取配置類
 * @author szekinwin
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport{

    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    
    //自定義快取key生成策略
    @Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator(){
            @Override
                public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
                    StringBuffer sb = new StringBuffer();
                    sb.append(target.getClass().getName());
                    sb.append(method.getName());
                    for(Object obj:params){
                        sb.append(obj.toString());
                    }
                    return sb.toString();
                }
            };
    }
    //快取管理器
    @Bean 
    public CacheManager cacheManager(@SuppressWarnings("rawtypes") RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //設定快取過期時間 
        cacheManager.setDefaultExpiration(10000);
        return cacheManager;
    }
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory){
        StringRedisTemplate template = new StringRedisTemplate(factory);
        setSerializer(template);//設定序列化工具
        template.afterPropertiesSet();
        return template;
    }
     private void setSerializer(StringRedisTemplate template){
            @SuppressWarnings({ "rawtypes", "unchecked" })
            Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            template.setValueSerializer(jackson2JsonRedisSerializer);
     }
}

然後在Service中使用redisTemplate或者直接使用@Cacheable和@CacheEvict來進行快取操作即可。

當然你也可以像第一小節講的那樣,使用傳統配置來進行redis的設定,不同的就是我們使用Java配置來代替XML配置,適合那些需要自己控制redis核心操作的專案。
使用Java方式配置,只需要新建一個類,然後使用@Configuration註解即可,然後使用配置的RedisTemplate類進行redis資料庫的操作。這裡不再贅述。

四、補充:redis叢集操作

上面講的都是針對redis單機版進行的操作,而目前我們的企業級專案都是搭建了redis叢集來進行redis管理的,這裡我們來補充一下傳統模式和Spring Boot模式對
redis叢集的操作。

(1)傳統方式操作redis叢集
修改redis.propertis,放置叢集配置:

redis.host1=192.168.1.235
redis.port1=7001
redis.host2=192.168.1.235
redis.port2=7002
redis.host3=192.168.1.235
redis.port3=7003
redis.host4=192.168.1.235
redis.port4=7004
redis.host5=192.168.1.235
redis.port5=7005
redis.host6=192.168.1.235
redis.port6=7006
 
redis.maxRedirects=3
redis.maxIdle=30
redis.maxTotal=100
redis.minIdle=5
redis.maxWaitMillis=30000 
redis.testOnBorrow=true
redis.testOnReturn=true 
redis.testWhileIdle=true
redis.timeout=3000

然後在spring配置檔案中,配置RedisClusterConfiguration類,將叢集資訊放置進去:

<bean id="redisClusterConfiguration" class="org.springframework.data.redis.connection.RedisClusterConfiguration">  
                <property name="maxRedirects" value="${redis.maxRedirects}"></property>  
            <property name="clusterNodes">  
                <set>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host1}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port1}"></constructor-arg>  
                    </bean>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host2}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port2}"></constructor-arg>  
                    </bean>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host3}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port3}"></constructor-arg>  
                    </bean>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host4}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port4}"></constructor-arg>  
                    </bean>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host5}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port5}"></constructor-arg>  
                    </bean>  
                    <bean class="org.springframework.data.redis.connection.RedisNode">  
                        <constructor-arg name="host" value="${redis.host6}"></constructor-arg>  
                        <constructor-arg name="port" value="${redis.port6}"></constructor-arg>  
                    </bean>  
            </set>  
    </property>  
</bean> 

然後將配置好的RedisClusterConfiguration作為引數,引入到之前配置好的jeidsConnectionFactory中即可:

<bean id="jeidsConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"  >  
    <property name="poolConfig" ref="jedisPoolConfig" />
    <constructor-arg name="clusterConfig" ref="redisClusterConfiguration"/>  
    <constructor-arg name="poolConfig" ref="jedisPoolConfig"/>   
</bean> 

其它配置就無需修改(redisTemplate等配置),在後面使用redis的操作也類似之前。

如果redis配置了主從,操作如下:
配置檔案:

#sentinel1的IP和埠  
sentinel1.host=192.168.1.233
sentinel1.port=26379  
#sentinel2的IP和埠  
sentinel2.host=192.168.1.233
sentinel2.port=26378 
 
im.hs.server.redis.maxIdle=500  
#最大連線數,超過此連線時操作redis會報錯  
im.hs.server.redis.maxTotal=5000  
im.hs.server.redis.maxWaitTime=1000  
im.hs.server.redis.testOnBorrow=true  
#最小閒置連線數,spring啟動的時候自動建立該數目的連線供應用程式使用,不夠的時候會申請。  
im.hs.server.redis.minIdle=300
im.hs.server.redis.sentinel.masterName=mymaster

然後在XML配置檔案中配置redisSentinelConfiguration類,將redis主從資訊放入其中:

<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="${im.hs.server.redis.sentinel.masterName}"/>
        </bean>
    </property>
    <property name="sentinels">
        <set>
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <constructor-arg name="host" value="${sentinel1.host}"></constructor-arg>
                <constructor-arg name="port" value="${sentinel1.port}"></constructor-arg>
            </bean>
            <bean class="org.springframework.data.redis.connection.RedisNode">
                <constructor-arg name="host" value="${sentinel2.host}"></constructor-arg>
                <constructor-arg name="port" value="${sentinel2.port}"></constructor-arg>
            </bean>
        </set>
    </property>
</bean>
 
<bean id="jeidsConnectionFactory"
    class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
    <constructor-arg name="sentinelConfig" ref="redisSentinelConfiguration"></constructor-arg>  
    <constructor-arg name="poolConfig" ref="poolConfig"></constructor-arg>      
</bean>

解釋:sentinel意思是哨兵,用於監視主從伺服器的執行狀況,如果主伺服器掛掉,會在從伺服器中選舉一個作為主伺服器。 
其它配置就無需修改(redisTemplate等配置),在後面使用redis的操作也類似之前。

(2)Spring Boot操作redis叢集
Spring Boot的叢集配置就很簡單了,修改原來的配置如下:

spring.redis.cluster.nodes=127.0.0.1:26379,127.0.0.1:26479,127.0.0.1:26579
spring.redis.password=password

餘下的操作如之前。

主從配置:

# database name
spring.redis.database=0
# server host1 單機使用,對應伺服器ip
#spring.redis.host=127.0.0.1  
# server password 密碼,如果沒有設定可不配
#spring.redis.password=
#connection port  單機使用,對應埠號
#spring.redis.port=6379
# pool settings ...池配置
spring.redis.pool.max-idle=8
spring.redis.pool.min-idle=0
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
# name of Redis server  哨兵監聽的Redis server的名稱
spring.redis.sentinel.master=mymaster
# comma-separated list of host:port pairs  哨兵的配置列表
spring.redis.sentinel.nodes=127.0.0.1:26379,127.0.0.1:26479,127.0.0.1:26579

以上就是Spring 以及Spring Boot針對redis的整合。


參考:
Spring整合redis:
https://www.cnblogs.com/hello-daocaoren/p/7891907.html
SpringBoot使用Redis快取:
https://www.cnblogs.com/gdpuzxs/p/7222309.html
spring整合redis(叢集、主從):
https://blog.csdn.net/sunqingzhong44/article/details/70976038?locationNum=6&fps=1
spring boot整合redis主從sentinel:
https://blog.csdn.net/liuchuanhong1/article/details/54601037

轉載請註明出處:https://blog.csdn.net/acmman/article/details/83035604