• 一. 问题背景
  • 二. 版本说明
  • 三. 解决方案
  • 3.1 引入依赖
  • 3.2 配置Redis
  • 3.3 封装关于Redis的操作
  • 3.3.1 定义接口
  • 3.3.2 实现接口
  • 3.4 测试前期准备
  • 3.4.1 创建controller层
  • 3.4.2 创建Service层
  • 3.5 部署Redis
  • 3.5.1 安装Redis
  • 3.5.2 配置redis.conf
  • 3.5.3 创建并启动Redis容器
  • 3.6 测试


一. 问题背景

了解完了关于Redis的架构后,尝试动手写代码,SpringBoot整合Redis。Redis的客户端有Jedis以及Lettuce,SpringBoot2.x版本默认使用Lettuce了,可见Lettuce效果比较好。

参考自:

二. 版本说明

技术栈

版本

SpringBoot

2.x以上版本

Redis

6.0.10(以Docker方式运行)

三. 解决方案

3.1 引入依赖

<!--redis依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- lettuce pool 缓存连接池-->
<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-pool2</artifactId>
  <version>2.9.0</version>
</dependency>

3.2 配置Redis

在yml文件中,spring节点下,配置Redis的信息:

redis:
  host: xx.xx.xx.xx # Redis服务器地址
  database: 0 # Redis数据库索引(默认为0)
  port: 6379 # Redis服务器连接端口
  password: 'xxx' # Redis服务器连接密码(默认为空)
  lettuce:
    pool:
      max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
      max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
      max-idle: 8 # 连接池中的最大空闲连接
      min-idle: 0 # 连接池中的最小空闲连接
  timeout: 3000ms # 连接超时时间(毫秒)
  key:
    prefix:
      authCode: "portal:authCode:"
    expire:
      authCode: 20 # 验证码超期时间

注:上面代码中的prefix authCode以及expire authCode的值随便填

自定义RedisTemplate,否则中文的value存到redis里面会乱码,获取出来也会乱码:

package com.ganzalang.gmall.redis.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @Author Androidla
 * @Date 2021/2/14 13:40
 * @Description
 **/
@Configuration
@Slf4j
public class RedisConfig {

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        log.info("RedisConnectionFactory class: {}", redisConnectionFactory.getClass());
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisSerializer<Object> redisSerializer() {
        // 创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer =
                new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        // 必须设置,否则无法将json转化为对象,会转化为Map类型
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,
                ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }
}

3.3 封装关于Redis的操作

3.3.1 定义接口

package com.ganzalang.gmall.service;

/**
 * redis操作Service,
 * 对象和数组都以json形式进行存储
 */
public interface RedisService {

    /**
     * 存储数据
     * @param key
     * @param value
     */
    void set(String key, String value);

    /**
     * 获取数据
     * @param key
     * @return
     */
    String get(String key);

    /**
     * 设置超时时间
     * @param key
     * @param expire
     * @return
     */
    boolean expire(String key, long expire);

    /**
     * 删除数据
     * @param key
     */
    void remove(String key);

    /**
     * 自增操作
     * @param key
     * @param delta
     * @return
     */
    Long increment(String key, long delta);

}

3.3.2 实现接口

package com.ganzalang.gmall.service.impl;

import com.ganzalang.gmall.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * redis操作Service的实现类
 */
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public boolean expire(String key, long expire) {
        return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public void remove(String key) {
        stringRedisTemplate.delete(key);
    }

    @Override
    public Long increment(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key, delta);
    }
}

3.4 测试前期准备

3.4.1 创建controller层

package com.ganzalang.gmall.controller;

import com.ganzalang.gmall.service.impl.TestServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 测试redis是否整合成功
 */
@RestController
@RequestMapping("/test")
public class TestController {

    @Autowired
    private TestServiceImpl testService;

    @GetMapping("/redis")
    public String testRedis() {
        testService.testRedis();
        return "success";
    }

}

3.4.2 创建Service层

package com.ganzalang.gmall.service.impl;

import com.ganzalang.gmall.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class TestServiceImpl {

    @Autowired
    private RedisService redisService;

    @Value("${spring.redis.key.prefix.authCode}")
    private String REDIS_KEY_PREFIX_AUTH_CODE;

    @Value("${spring.redis.key.expire.authCode}")
    private Long AUTH_CODE_EXPIRE_SECONDS;

    private static String username = "10010";

    public void testRedis() {
        log.info("testRedis start......");
        log.info("REDIS_KEY_PREFIX_AUTH_CODE: {}", REDIS_KEY_PREFIX_AUTH_CODE);
        log.info("AUTH_CODE_EXPIRE_SECONDS: {}", AUTH_CODE_EXPIRE_SECONDS);

        // 设置缓存
        redisService.set(getAuthKey(), "管理员权限");
        // 设置过期时间
        redisService.expire(getAuthKey(), AUTH_CODE_EXPIRE_SECONDS);
        // 获取缓存
        String auth = redisService.get(getAuthKey());
        log.info("auth: {}", auth);
    }

    // 构造redis的key
    private String getAuthKey() {
        String key = REDIS_KEY_PREFIX_AUTH_CODE + username;
        return key;
    }
}

3.5 部署Redis

3.5.1 安装Redis

用Docker方式部署:

# 下载最新版的redis的docker镜像
docker pull redis

# 创建挂载数据的地方
mkdir /mydata/redis/data

3.5.2 配置redis.conf

必须以指定redis.conf方式启动redis容器,否则后面会有一堆报错

去网上下载redis.conf文件,并放到/mydata/redis/下,并修改redis.conf的某些内容,修改如下:

1. 注释掉 bind 127.0.0.1
2. 将protected-mode yes改为no,否则无法远程登陆redis
3. 将daemonize yes 改为no,否则启动redis容器会一直失败
4. 强烈建议设置redis密码,在requirepass处设置自己的密码

3.5.3 创建并启动Redis容器

docker run -p xxxx:6379 --name redis \
-v /mydata/redis/redis.conf:/etc/redis/redis.conf \
-v /mydata/redis/data:/data \
-d redis redis-server /etc/redis/redis.conf \
--appendonly yes

解释:

  1. -p xxxx:6379:前者是宿主机的端口,后者是容器的端口。强烈建议修改宿主机的端口
  2. --name:对创建出来的容器起别名
  3. -v:挂载容器数据到宿主机,实现宿主机与容器数据互通
  4. -d:后台运行
  5. --appendonly yes:数据持久化

3.6 测试

启动程序,可以看到如下:

data redis spring 不使用注解 spring data redis lettuce_Redis


整合成功