1. 程式人生 > >SpringBoot 2.0 | SpringBoot 整合 Redis + Cache + 分散式 Session

SpringBoot 2.0 | SpringBoot 整合 Redis + Cache + 分散式 Session

簡介

1.Redis

redis是一個key-value儲存系統。它支援儲存的value型別相對更多,包括string(字串)、list(連結串列)、set(集合) 和hash(雜湊型別)。這些資料型別都支援push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支援各種不同方式的排序。與memcached一樣,為了保證效率,資料都是快取在記憶體中的,它的效能極高 ,Redis能讀的速度是110000次/s,寫的速度是81000次/s 。

2.Cache

Spring Cache 是作用在方法上的,其核心思想是這樣的:當我們在呼叫一個快取方法時會把該方法引數和返回結果作為一個鍵值對存放在快取中,等到下次利用同樣的引數來呼叫該方法時將不再執行該方法,而是直接從快取中獲取結果進行返回。

與 Redis 的區別:

叢集環境下,每臺伺服器的 Spring Cache 是不同步的,這樣會出問題的,Spring Cache 只適合單機環境。Spring Cache + Redis 是設定單獨的快取伺服器,所有叢集伺服器統一訪問 Redis,不會出現快取不同步的情況。

Spring Cache + Redis 是基於註解的,對程式碼侵入比較小,但是支援的 api 太少了,不能滿足大部分業務需求。Redis 基於 api,優點是 api 型別多,缺點是業務程式碼入侵。

3.分散式 Session

對於微服務系統來說,每個服務都是獨立的系統,我們在這個服務中建立的 Session ,和在另一個服務中建立的 Session 是兩個不同的 Session ,這就難以對使用者的狀態進行管理,分散式 Session 是對 Session 進行統一管理,把 Session 儲存到 Redis 伺服器中,當用戶需要訪問各個不同的服務時,從 Redis 伺服器中對 Session 進行校驗,以訪問各個不同的服務,簡單來說,就是各個微服務共享一個 Session。

環境

1. 安裝 Redis

2.新增依賴

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

		<!--分散式session共享-->
		<dependency>
			<groupId>org.springframework.session</groupId>
			<artifactId>spring-session-data-redis</artifactId>
		</dependency>

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

3.application.yml 配置

spring:
  cache:
      redis:
        time-to-live: 100000 # #快取超時時間ms @Bean配置後,這裡的配置無效
        # cache-null-values: false #是否快取空值
      type: redis

  datasource:
      type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.jdbc.Driver
      username: root
      password: roof
      url: jdbc:mysql://localhost:3306/sirius?useSSL=false&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true

  redis:
    # Redis資料庫索引,預設為0
    database: 0
    # Redis伺服器地址
    host: 127.0.0.1
    # Redis伺服器連線埠
    port: 6379
    # Redis密碼,預設為空
    password:
    # Redis連線池最大連線數(負值表示沒有限制
    jedis:
      pool:
        max-active: 10
        # 連線池最大阻塞等待時間
        max-wait: -1
        # 連線池最大空閒連結
        max-idle: 10
        # 連線池最小空閒連結
        min-idle: 0
    # 連結超時使勁
    timeout: 10000

    # 配置前端Thymeleaf模板引擎
  thymeleaf:
  # 打包末尾無/
    prefix: classpath:/templates/
    check-template-location: true
    suffix: .html
    encoding: UTF-8
    servlet:
      content-type: text/html
    mode: HTML5
    # 禁止後實現前端熱部署
    cache: false

# 整合Mybatis
mybatis:
  # Mybatis對映
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.hly.sirius.domain

# 埠設定
server:
  port: 8080

4.Redis + Cache 配置

/**
 * @author :hly
 * @github :https://github.com/huangliangyun
 * @blog :blog.csdn.net/Sirius_hly
 * @date :2018/11/26
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    //設定快取的鍵值和引數,加上了包名和方法名,可以不配置
    /*@Bean
    public KeyGenerator keyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }*/

    //解決 Cache 存放到 Redis 資料庫序列化亂碼即型別無法轉化問題
    @Bean
    CacheManager cacheManager(RedisConnectionFactory connectionFactory) {
        //初始化一個RedisCacheWriter
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory);
        //設定CacheManager的值序列化方式
        ClassLoader loader = this.getClass().getClassLoader();
        JdkSerializationRedisSerializer jdkSerializer = new JdkSerializationRedisSerializer(loader);
        RedisSerializationContext.SerializationPair<Object> pair = RedisSerializationContext.SerializationPair.fromSerializer(jdkSerializer);
        RedisCacheConfiguration defaultCacheConfig=RedisCacheConfiguration.defaultCacheConfig().serializeValuesWith(pair).entryTtl(Duration.ofSeconds(30));
        //.disableCachingNullValues();//不儲存空值
        //初始化RedisCacheManager
        RedisCacheManager cacheManager = new RedisCacheManager(redisCacheWriter, defaultCacheConfig);
        return cacheManager;
    }
    //配置序列化,不然會出現 \xAC\xED\x00\x05t\x00\x06之類的
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory factory) {
        //// 配置連線工廠
        StringRedisTemplate template = new StringRedisTemplate(factory);
        ////使用Jackson2JsonRedisSerializer來序列化和反序列化redis的value值,效率高(預設使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        //// 指定要序列化的域,field,get和set,以及修飾符範圍,ANY是都有包括private和public
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //// 指定序列化輸入的型別,類必須是非final修飾的,final修飾的類,比如String,Integer等會跑出異常
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);
        //使用StringRedisSerializer來序列化和反序列化redis的key值
        template.setKeySerializer(new StringRedisSerializer());
        //// 值採用json序列化
        template.setValueSerializer(jackson2JsonRedisSerializer);
        // 設定hash key 和value序列化模式
        template.setHashKeySerializer(new StringRedisSerializer());
        template.setHashValueSerializer(jackson2JsonRedisSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

5.配置 Session

@SpringBootApplication
@EnableCaching//開啟全域性快取
@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1800)//開啟session共享,session有效期30分鐘
public class SpringBootRedisApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootRedisApplication.class, args);
	}
}

使用

1.Redis 的使用

啟動 Redis 資料庫
啟動專案訪問以下路徑
http://localhost:8080/redis/addUser

/**
 * @author :hly
 * @github :https://github.com/huangliangyun
 * @blog :blog.csdn.net/Sirius_hly
 * @date :2018/11/26
 */
@RestController
public class RedisController {

    //只針對鍵值對都是字元型的資料進行操作
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RedisTemplate redisTemplate;

    @RequestMapping("redis/addUser")
    public User addUserByRedis(){
        //儲存物件
        User  user = new User();
        user.setUsername("hly");
        user.setPassword("123");
        ValueOperations<String,User> operations = redisTemplate.opsForValue();
        operations.set("user",user);
        System.err.println(operations.get("user"));

        ValueOperations operations1 = stringRedisTemplate.opsForValue();
        operations1.set("hly","123");
        System.err.println(operations1.get("hly"));
        //operations.set("user",user,1, TimeUnit.SECONDS);
        return user;
    }
}

資料已經儲存到 Redis
在這裡插入圖片描述

2.Spring Cache + Redis 的使用

為了方便使用,我已經把路徑貼上了出來,直接複製訪問即可,每一個註解的含義也都註釋寫明瞭。

/**
 * @author :hly
 * @github :https://github.com/huangliangyun
 * @blog :blog.csdn.net/Sirius_hly
 * @date :2018/11/26
 */
@RestController
public class CacheController {

    /**
     * value:快取的名字
     * key 使用SpEL表示式自定義的快取Key,比如:#username是以引數username為key的快取
     * @return
     */

    //把集合寫入redis,放進快取
    //http://localhost:8080/cache/addUsers
    @GetMapping("/cache/addUsers")
    @Cacheable(value = "user")//讀取資料到方法上,先從快取中讀取,沒有再從資料庫中讀取
    public List<User> postAllUser() {
        User user1 = new User("hly","124");
        User user2 = new User("sirius","123");
        List<User> users = new ArrayList<User>();
        users.add(user1);
        users.add(user2);
        return users;
    }

    //空方法,拿上面方法的快取
    //http://localhost:8080/cache/all
    @GetMapping("/cache/all")
    @Cacheable(value = "user")//讀取資料到方法上,先從快取中讀取,沒有再從資料庫中讀取
    public List<User> getAllUser() {
        List<User> users = new ArrayList<User>();
        return users;
    }

    //增加,修改快取到空方法
    //http://localhost:8080/cache/update
    @CachePut(value = "user")
    @GetMapping("/cache/update")
    public List<User> updateUsers() {
        List<User> users = new ArrayList<User>();
        User user1 = new User("hly","125");
        users.add(user1);
        return users;
    }

    //刪除空方法設定的快取
    //http://localhost:8080/cache/del
    @GetMapping("/cache/del")
    @CacheEvict(value = "user")
    public String delAllCache() {
        //刪除後redis中還有
        return "以刪除所有快取";
    }

    //EL表示式來指定的key,有則取出,無則放入快取,返回到方法User返回引數,沒有則設定User user
    //http://localhost:8080/cache/object?username=hly
    @Cacheable(value = "user",key="#user.username")
    @GetMapping("/cache/object")
    public User getUserOfAdd(User user) {
        user.setUsername("hly");
        user.setPassword("129");
        return user;
    }

    //直接拿到
    //http://localhost:8080/cache/object/get/?username=hly
    @Cacheable(value = "user",key="#user.username")
    @GetMapping("/cache/object/get")
    public User getUser(User user) {
        return user;
    }

    //根據鍵值,增加,修改
    //http://localhost:8080/cache/update/object/?username=hly
    @CachePut(value = "user",key="#user.username")
    @GetMapping("/cache/update/object")
    public User updateUser(User user) {
        user.setUsername("hly");
        user.setPassword("128");
        return user;
    }

    //按名字刪除快取
    //http://localhost:8080/cache/del/object?username=hly
    @GetMapping("/cache/del/object")
    @CacheEvict(value = "user",key="#user.username")
    public String delCacheByName(User user) {
        //刪除後redis中還有
        return "按名字刪除快取";
    }
}

在這裡插入圖片描述

3.分散式 Session 的使用

@ResponseBody
    @RequestMapping("/uuid")
    public String sessionTest(HttpSession session){
        UUID uuid = (UUID)session.getAttribute("uuid");
        if(uuid == null){
            uuid = UUID.randomUUID();
        }
        session.setAttribute("uuid",uuid);
        return session.getId();
    }
//分散式Session
    @RequestMapping("/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, HttpSession session){
       if(session.getAttribute("uuid")!=null)
            return "redirect:/admin";
        return "redirect:/";
    }

執行專案,訪問 http://localhost:8080/uuid
會出現 一串 id,這是 Session 的 id。
在這裡插入圖片描述
這個 id 會儲存到 Redis 資料庫 。

在這裡插入圖片描述
到測試頁面進行測試 http://localhost:8080/

在沒有把這串 id 存到資料庫之前是無法進行登入的,如果換個瀏覽器,如同換了個使用者,我們必須重新獲取一個 id ,才能進行驗證。同理,我們可以在不同的專案進行相同的 Redis 伺服器配置,啟動多個專案,同一個 Session 可以訪問不同的專案,儘管他們的埠號,伺服器不同。

原始碼下載:https://github.com/huangliangyun/Spring-Boot-2.X
參考:http://www.ityouknow.com/springboot/2016/03/06/spring-boot-redis.html
QQ交流群:865061230