SpringBoot中如何实现Redis分库操作

  |   0 评论   |   140 浏览

概述

在Redis中默认有16个库,一些业务场景需要对Redis分库,每个库按业务需求存储不同数据。比如下面:

public enum RedisDBEnum {
    Default(0,"默认"),
    Banner(1,"Banner热数据"),
    Captcha_Token(2, "图片验证码/短信验证码/token"),
    ShoppingCart(3, "购物车"),
    Order(4,"订单"),
    Goods(5,"商品"),
    User(6, "用户"),
    District(7,"中国省份城市数据库");
    //TODO and so on
    
    private Integer index;
    private String desc;
    
    private RedisDBEnum(Integer index, String desc) {
    	this.index = index;
    	this.desc = desc;
    }
    
    public Integer getIndex() {
    	return index;
    }
    
    public String getDesc() {
    	return desc;
    }
}

使用原生的 spring-boot-starter-data-redis 默认只能构建出单例的 RedisTemplate 和 LettuceConnectionFactory,并且只能操作指定的一个Redis 库。所以,我们需要 改造LettuceConnectionFactory 让其能构建多个 RedisTemplate 。

实现过程

在 pom.xml 中引入相关依赖

<!-- 配置管理,里面有依赖FastJson -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>io.lettuce</groupId>
    <artifactId>lettuce-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

修改bootstrap配置文集

src/main/resources/bootstrap.properties

#配置中心
spring.cloud.nacos.config.server-addr=192.168.1.4:8848
spring.cloud.nacos.config.namespace=c51764ea-936b-42a9-a169-8b78ca9ea93e

spring.cloud.nacos.config.ext-config[0].data-id=redis_config.properties
spring.cloud.nacos.config.ext-config[0].group=DEFAULT_GROUP
spring.cloud.nacos.config.ext-config[0].refresh=false

在Nacos的DEV命名空间新增redis_config.properties 配置文件

redis.host=192.168.1.4
redis.port=6379
redis.password=
redis.timeout=10000

redis.lettuce.pool.max-idle=4
redis.lettuce.pool.min-idle=0
redis.lettuce.pool.max-active=8
redis.lettuce.pool.max-wait=10000
redis.lettuce.shutdown-timeout=4000

redisconfig.properties.jpg

编写配置类RedisConfig.java

import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import lombok.Data;

@Data
@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {
    private String host;
    private Integer port;
    private String password;
    private Duration timeout;
    private final Lettuce lettuce = new Lettuce();
    @Data
    public static class Pool {
    	private int maxIdle = 8;
    	private int minIdle = 0;
    	private int maxActive = 8;
    	private Duration maxWait = Duration.ofMillis(-1);
    	private Duration timeBetweenEvictionRuns;
    }
    
    @Data
    public static class Lettuce {
    	private Duration shutdownTimeout = Duration.ofMillis(100);
    
    	private Pool pool;
    }
}

构建多个RedisTemplate

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ProjectRedisTemplate {
    @Autowired
    private RedisConfig redisConfig;

    private LettuceConnectionFactory createLettuceConnectionFactory(int dbIndex) {
        // Redis配置
        RedisStandaloneConfiguration redisConfiguration = new RedisStandaloneConfiguration(redisConfig.getHost(), redisConfig.getPort());
        redisConfiguration.setDatabase(dbIndex);
        redisConfiguration.setPassword(redisConfig.getPassword());

        // 连接池配置
        GenericObjectPoolConfig<Object> genericObjectPoolConfig = new GenericObjectPoolConfig<Object>();
        RedisConfig.Pool pool = redisConfig.getLettuce().getPool();
        genericObjectPoolConfig.setMaxIdle(pool.getMaxIdle());
        genericObjectPoolConfig.setMinIdle(pool.getMinIdle());
        genericObjectPoolConfig.setMaxTotal(pool.getMaxActive());
        genericObjectPoolConfig.setMaxWaitMillis(pool.getMaxWait().toMillis());

        // Redis客户端配置
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration
                .builder().commandTimeout(redisConfig.getTimeout());

        builder.shutdownTimeout(redisConfig.getLettuce().getShutdownTimeout());
        builder.poolConfig(genericObjectPoolConfig);

        // 根据配置和客户端配置创建连接
        LettuceClientConfiguration lettuceClientConfiguration = builder.build();
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(redisConfiguration,
                lettuceClientConfiguration);
        lettuceConnectionFactory.afterPropertiesSet();

        return lettuceConnectionFactory;
    }

    /**
     * Banner 数据存储
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> bannerRedisTemplate() {
        LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(RedisDBEnum.Banner.getIndex());
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 验证码/token 数据存储
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> captchaTokenRedisTemplate() {
        LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(RedisDBEnum.Captcha_Token.getIndex());
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);

        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);

        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 默认
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(RedisDBEnum.Default.getIndex());
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(lettuceConnectionFactory);
        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringRedisSerializer);
        redisTemplate.setHashKeySerializer(stringRedisSerializer);
        FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.class);
        redisTemplate.setValueSerializer(fastJsonRedisSerializer);
        redisTemplate.setHashValueSerializer(fastJsonRedisSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

业务测试

@Slf4j
@RestController
public class DemoController {
    @Autowired
    @Qualifier("bannerRedisTemplate")
    private RedisTemplate<String, Object> bannerRedisTemplate;

    @Autowired
    @Qualifier("captchaTokenRedisTemplate")
    private RedisTemplate<String, Object> captchaTokenRedisTemplate;

    @GetMapping("/store")
    public String store() {
        captchaTokenRedisTemplate.opsForValue().set("captchaPureNumber", RandomStringUtils.randomNumeric(6), 30,
                TimeUnit.MINUTES);
        captchaTokenRedisTemplate.opsForValue().set("token", RandomStringUtils.randomAlphabetic(21), 30,
                TimeUnit.MINUTES);

        List<Money> moneyList = Arrays.asList(new Money(1L, "人民币", 100), new Money(2L, "越南盾", 100000));
        bannerRedisTemplate.opsForList().rightPushAll("homePageLeftBanner", moneyList);

        SetOperations<String, Object> moneySet = bannerRedisTemplate.opsForSet();
        moneySet.add("homePageRightBanner", new Money(11L, "日元", 10000));
        moneySet.add("homePageRightBanner", new Money(22L, "加元", 20000));
        moneySet.add("homePageRightBanner", new Money(33L, "泰铢", 30000));

        return "somewhere";
    }

    @GetMapping("/view")
    public String view(Model model) {
        model.addAttribute("captchaPureNumber", captchaTokenRedisTemplate.opsForValue().get("captchaPureNumber"));
        model.addAttribute("token", captchaTokenRedisTemplate.opsForValue().get("token"));

        Set<Object> homePageRightBanner = bannerRedisTemplate.opsForSet().members("homePageRightBanner");
        model.addAttribute("homePageRightBanner", homePageRightBanner);

        List<Object> homePageLeftBanner = (List<Object>) bannerRedisTemplate.opsForList().range("homePageLeftBanner", 0,-1);
        model.addAttribute("homePageLeftBanner", homePageLeftBanner);
        return "<pre>" + JSON.toJSONString(model, SerializerFeature.PrettyFormat,SerializerFeature.WriteMapNullValue) + "</pre>";
    }
}

@Builder
@Data
class Money {
    private Long id;
    private String currency;
    private Integer value;
}

访问 Controller中的 store 方法,不同业务的RedisTemplate操作不同Redis库。
访问 Controller view 方法,查看不同Redis库数据。

image.png


标题:SpringBoot中如何实现Redis分库操作
作者:shuikan95
地址:http://www.javadaily.cn/articles/2020/03/14/1584178764395.html


评论

发表评论