【Spring Boot】18.集成redis

簡介

redis是什麼相信你們都有具體的瞭解,不瞭解的同窗最好先去官方網站查閱學習。java

咱們以前講到的緩存系統,默認使用的是ConcurrentMapCacheManager==ConcurrentMapCache,默認開啓的是SimpleCacheConfiguration配置,咱們以前分析過,他還支持其餘的不少緩存配置,包括咱們要講到的redis。mysql

經過將redis集成到咱們的緩存系統,咱們就能夠輕鬆的經過reids的客戶端查詢對應的緩存信息,實現了緩存信息的可視化。web

印象筆記 —— redis是一個比傳統數據庫更輕量,但卻擁有強大功能的「數據庫」,通常在應用中充當緩存組件,例如做爲數據庫和應用程序上層的中間件、提供session會話信息保存等。redis

1 reids基礎環境搭建

  1. 安裝redis 傳統安裝redis的方法,咱們就很少說了,咱們使用以前用的docker安裝redis.
  • 拉取redis
docker pull redis
  • 啓動reids
docker run -d -p 6379:6379 --name redis redis
  • 查看結果
docker ps

輸出spring

439e18fd2ea6        rabbitmq:3-management                                 "docker-entrypoint..."   5 days ago          Up 5 days           4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp   rabbit
3d9ee1d941b0        redis                                                 "docker-entrypoint..."   5 days ago          Up 5 days           0.0.0.0:6379->6379/tcp                                                                       redis
402fbb3778ad        docker.elastic.co/elasticsearch/elasticsearch:6.5.3   "/usr/local/bin/do..."   5 days ago          Up 5 days           0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp                                               loving_carson
e3ef44911869        mariadb                                               "docker-entrypoint..."   5 days ago          Up 5 days           0.0.0.0:3306->3306/tcp                                                                       mysql
59f9c80d269b        tomcat                                                "catalina.sh run"        5 days ago          Up 5 days           0.0.0.0:8080->8080/tcp                                                                       goofy_swartz

以前我已經裝好了redis,咱們能夠看到redis已經在咱們端口6379上映射好了,接下來就可使用redis客戶端鏈接並查看咱們本身的redis了。sql

  1. 安裝redis客戶端 redis客戶端有不少,不過比較經常使用的是 RedisDesktopManager ,您能夠輕鬆的下載安裝。經過點擊Connect to Redis Server按鈕(若是您的版本和個人差很少的話,應該在右下方往右數的第二個按鈕,有一個綠色的+),按照咱們的redis環境對其進行配置,若是成功的話,雙擊生成的redis服務,就能夠看到出現16個數據倉庫,說明您整個過程已經成功了。通常都是以下的配置
Name : 隨意寫
Host : 你的Redis服務器id
Port: 6379
Auth: 無需填寫
  1. 嘗試一下redis的使用 redis的使用您能夠參考官網的文檔嘗試或者參考一些關於redis的一些博客,官方文檔列出了許多相關的命令,能夠一一嘗試下,很是有意思。(先了解redis的特性以後在開始穩當一些。)

2 整合redis

  1. 引入redis的場景啓動器:
pom.xml
<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
  1. 配置redis 能夠經過sprig.redis相關參數配置redis。

application.yml

spring:
  redis:
    host: 10.21.1.47
    port: 6379

port默認6379,若是是默認端口的話咱們也能夠不予指定。docker

  1. 查看redis自動配置類源碼
/*
 * Copyright 2012-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot.autoconfigure.data.redis;

import java.net.UnknownHostException;

import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;

/**
 * {@link EnableAutoConfiguration Auto-configuration} for Spring Data's Redis support.
 *
 * @author Dave Syer
 * @author Andy Wilkinson
 * @author Christian Dupuis
 * @author Christoph Strobl
 * @author Phillip Webb
 * @author Eddú Meléndez
 * @author Stephane Nicoll
 * @author Marco Aust
 * @author Mark Paluch
 */
@Configuration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

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

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

}

能夠看出,他爲咱們提供了兩個組件類,一個是RedisTemplate(k-v都是對象),一個是StringRedisTemplate(k-v都是字符串)。因爲咱們常用字符串操做,因此redis配置類中爲提供了stringRedisTemplate這個組件。shell

咱們能夠直接在應用中直接注入就可使用了。數據庫

3 測試redis操做

redis支持的五大數據類型:String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合),咱們均可以使用springboot提供的組件類進行操做。express

  1. StringRedisTemplate

經過redisTemplate.opsXXX.redis提供的Command來操做相應的數據。例如redisTemplate.opsForHash.

test/AwebApplicationTests
package com.zhaoyi.aweb;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class AwebApplicationTests {

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    RedisTemplate redisTemplate;
    @Test
    public void test01() {
        stringRedisTemplate.opsForValue().append("name", "a");
        stringRedisTemplate.opsForValue().append("name","kuya");
        System.out.println(stringRedisTemplate.opsForValue().get("name"));
    }

    @Test
    public void contextLoads() {

    }

}

運行test01測試方法後,咱們的redis存儲庫中存儲了一個String類型的name數據,其值爲akuya,其餘的操做你們均可以一一逐個測試,固然不一樣的數據類型設置和獲取由些許差別。

接下來咱們使用RedisTemplate類型的template來測試對象的保存。

  1. RedisTemplate
test/AwebApplicationTests
@Autowired
    RedisTemplate redisTemplate;
    @Test
    public void test02() {
        Integer id = 1;
        String key = "user_" + id;
        User user = userService.getUser(id);
        redisTemplate.opsForValue().set(key, user);
        System.out.println(redisTemplate.opsForValue().get(key));
    }

咱們查詢id=2的用戶信息,並以user_{id}爲key的方式存儲一個對象user的信息,運行測試用例test02咱們會發現報錯:

...
java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.zhaoyi.aweb.bean.User]
...

提示咱們須要提供的應該是一個可序列化的有效載荷(patload)類型,所以,咱們須要將User標誌爲可序列化的對象類型。

bean/User.class
public class User implements Serializable {
    private static final long serialVersionUID = 4125096758372084309L;

咱們再運行以後,查看redis客戶端的存儲信息發現

key=\xAC\xED\x00\x05t\x00\x06user_1

value = \xAC\xED\x00\x05sr\x00\x19com.zhaoyi.aweb.bean.User\xC7\xD7\xAC\x00\x0F\xDB\x0A\x97\x02\x00\x04L\x00\x0CdepartmentIdt\x00\x13Ljava/lang/Integer;L\x00\x02idq\x00~\x00\x01L\x00\x09loginNamet\x00\x12Ljava/lang/String;L\x00\x08userNameq\x00~\x00\x02xpsr\x00\x11java.lang.Integer\x12\xE2\xA0\xA4\xF7\x81\x878\x02\x00\x01I\x00\x05valuexr\x00\x10java.lang.Number\x86\xAC\x95\x1D\x0B\x94\xE0\x8B\x02\x00\x00xp\x00\x00\x00\x01q\x00~\x00\x06t\x00\x05akuyat\x00\x09\xE9\x98\xBF\xE5\xBA\x93\xE5\xA8\x85

也就是說,保存對象時,都是以jdk序列化機制,不論是key仍是value都是序列化的字符串了。

一般,咱們仍是習慣本身轉化爲json方式來存儲:

  • 將對象轉化爲json字符串存儲 這個很簡單,咱們通常使用這一種
  • 修改默認序列化器爲json序列化器。

咱們先查看默認狀況下用的是什麼序列化器:

org.springframework.data.redis.core.RedisTemplate
if (defaultSerializer == null) {

    defaultSerializer = new JdkSerializationRedisSerializer(
            classLoader != null ? classLoader : this.getClass().getClassLoader());
}

也就是JdkSerializationRedisSerializer,接下來,咱們修改成本身的序列化器。

config/MyConfig.class
package com.zhaoyi.aweb.config;

import com.zhaoyi.aweb.bean.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;

@Configuration
public class MyConfig {

    @Bean
    public RedisTemplate<String, User> redisTemplateUser(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<String, User> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        // 設置默認的序列化器
        Jackson2JsonRedisSerializer<User> userJackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<User>(User.class);
        template.setDefaultSerializer(userJackson2JsonRedisSerializer);
        return template;
    }
}

而後,咱們使用該template來進行redis的存儲操做

test/AwebApplicationTests
@Autowired
    RedisTemplate<String, User> redisTemplate;
    @Test
    public void test02() {
        Integer id = 1;
        String key = "user_" + id;
        User user = userService.getUser(id);
        redisTemplate.opsForValue().set(key, user);
        //System.out.println(redisTemplate.opsForValue().get(key));
    }

查看咱們的redis客戶端,就能夠看到不論是key仍是value都是易讀的的形式了:

key = "user_1"
value = 
{
  "id": 1,
  "loginName": "akuya",
  "userName": "阿庫婭",
  "departmentId": 1
}

4 測試緩存

咱們引入redis的starter以後,咱們的容器中的緩存管理器變成了RedisCacheManager,他的做用是建立了RedisCache來做爲緩存組件,RedisCachle經過咱們配置的redis來進行緩存數據。

前面咱們使用查詢user的時候,會發現redis生成了一個名叫user命名空間,緩存了咱們查詢的用戶信息。

默認建立的RedisCacheManager使用的是RedisTemplate<Object,Object>,他默認使用的序列化器仍是咱們默認的JDK序列化機制。要改變這種行爲,咱們一樣須要建立本身的自定義CacheManager,注意2.0和1.x版本仍是有很大的區別,請當心版本帶來的問題。

但在尋找其區別的時候,我發現一個問題,那就是咱們應用中不可能沒有一個類就寫一個template,所以,咱們須要寫一個通用的redis底層存儲配置,也就是說遵循:String類型的數據做爲key,以json字符串做爲結果進行存儲,所以,編寫了以下的配置器,若是嫌麻煩的同窗能夠直接複製過去使用,兼容任何的類與底層緩存:

config/JoyRedisConfig.class
package com.zhaoyi.aweb.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
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.serializer.*;

import java.net.UnknownHostException;

@Configuration
public class JoyRedisConfig {

    /**
     * 配置RedisTemplate<String,Object>
     * @param redisConnectionFactory
     * @return
     * @throws UnknownHostException
     */
    @Bean
    public RedisTemplate<String,Object> redisTemplateUser(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<String, Object> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        // 設置序列化器
        template.setDefaultSerializer(valueSerializer());
        return template;
    }

    @Bean
    public RedisCacheManager redisCacheManagerUser(RedisConnectionFactory redisConnectionFactory){
        // 這裏能夠配置超時時間等
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration
                // Default { using the following:
                // key expiration === eternal
                // cache null values === yes
                // prefix cache keys===yes
                // default prefix===the actual cache name
                // key serializer === org.springframework.data.redis.serializer.StringRedisSerializer
                // value serializer === org.springframework.data.redis.serializer.JdkSerializationRedisSerializer
                // conversion service === DefaultFormattingConversionService with #registerDefaultConverters(ConverterRegistry) default
                .defaultCacheConfig()
                // Define the {@link SerializationPair} used for de-/serializing cache keys.
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(keySerializer()))
                // Define the {@link SerializationPair} used for de-/serializing cache values.
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(valueSerializer()))
                // Disable caching
                .disableCachingNullValues();
        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(redisCacheConfiguration)
                .transactionAware()
                .build();
    }

    // key序列化器
    private RedisSerializer<String> keySerializer() {
        return new StringRedisSerializer();
    }

    // Value序列化器
    private RedisSerializer<Object> valueSerializer() {
        return new GenericJackson2JsonRedisSerializer();
    }
}

咱們能夠看到經過配置@Cacheable(value = "user", key = "#id")生成的緩存key的樣子爲user::1,也就是說cacheName指定的值,生成了user::這樣的前綴,在redis中以名稱空間的形式存在;另外,若是咱們以@Cacheable(value = {"user","saber"}, key = "#id"),這樣存儲的話,結果會在user和saber兩個名稱空間中,存儲兩份結果,即user::1saber::1

注意這是2.x版本,簡化配置。基本知足須要,若是您有詳細的配置,例如超時時間、針對具體的容器進行配置,能夠在redisCacheConfiguration配置。

還記得@CacheManager註解嗎,他所指定的就是咱們如今定義的這些CacheManager了。

若是你配置了多個緩存管理器,別忘了爲默認的緩存管理器添加一個@Primary註解。

相關文章
相關標籤/搜索