【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注解。

相关文章
相关标签/搜索