栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

springboot + redis

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

springboot + redis

本篇简述springboot集成redis,基于redis哨兵模式。

sproingboot 集成组件三步曲(参见springboot+JPA介绍:依赖、配置、封装)

(假定springboot脚手架已搭建完成并成功运行,可参考历史分享springboot+mybatis)

1. maven添加redis依赖

    org.springframework.boot
    spring-boot-starter-data-redis


     org.springframework.data
     spring-data-redis
2. redis 配置

2.1 yml

#spring
spring:

#redis
  redis:
    host: 192.168.2.9
    port: 6379
    password: CacheDB123
    jedis:
      pool:
        max-active: 8
        max-wait: 6000
        max-idle: 5
        min-idle: 1
    # 哨兵配置
    sentinel:
      master: mymaster
      nodes: 192.168.2.9:6379,192.168.2.10:6379

2.2 RedisTemplate config

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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 org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory connectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(connectionFactory);

        // 使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式)
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 使用StringRedisSerializer来序列化和反序列化redis的key值
        RedisSerializer redisSerializer = new StringRedisSerializer();
        // key
        redisTemplate.setKeySerializer(redisSerializer);
        redisTemplate.setHashKeySerializer(redisSerializer);
        // value
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

} 
3. redis 功能封装 

3.1 RedisService

import java.util.concurrent.TimeUnit;

public interface RedisService {

    
    long TIME_ONE_SECOND = 1; // 1秒
    long TIME_ONE_MINUTE = 60 * TIME_ONE_SECOND; // 1分
    long TIME_ONE_HOUR = 60 * TIME_ONE_MINUTE; // 1小时
    long TIME_ONE_DAY = 24 * TIME_ONE_HOUR; // 1天
    long TIME_ONE_MonTH = 30 * TIME_ONE_DAY; // 1个月

    
    long getExpire(String... keys);

    
     boolean setByKey(V value, long exp, TimeUnit unit, String... keys);

    
     boolean setByKey(V value, long exp, String... keys);

    
     boolean setByKey(V value, String... keys);

    
    long increment(String key, long value);

    
    long increment(String key, long value, long exp);

    
     boolean rpush(String key, V value, long exp);

    
     boolean lpush(String key, V value, long exp);

    
     V lrange(String key);

    
     boolean sadd(String key, V value, long exp);

    
     V smembers(String key);

    
     boolean zadd(String key, V value, long exp);

    
     V zrange(String key);

    
     V getByKey(String... keys);

    
    boolean delByKey(String... keys);

    
    void convertAndSend(String channel, Object message);

}

3.2 RedisServiceImpl

import com.alibaba.fastjson.JSON;
import com.example.demo.service.RedisService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class RedisServiceImpl implements RedisService {

    @Autowired
    private RedisTemplate redisTemplate;

    
    private String getMemKey(String... keys){
        if(keys == null || keys.length == 0){
            return null;
        }

        if(keys.length == 1){
            return keys[0];
        }

        StringBuffer memKey = new StringBuffer();
        Arrays.stream(keys).forEach(key -> {
            memKey.append(":");
            memKey.append(key);
        });

        return memKey.toString().replaceFirst(":", "");
    }

    
    private boolean expireByKey(String key, long exp, TimeUnit unit){
        if (exp == 0) {
            exp = RedisService.TIME_ONE_MONTH;
        }
        if (unit == null) {
            unit = TimeUnit.SECONDS;
        }

        return redisTemplate.expire(key, exp, unit);
    }

    @Override
    public long getExpire(String... keys) {
        String memKey = this.getMemKey(keys);
        Long expire = redisTemplate.getExpire(memKey);

        log.info("获取缓存数据剩余有效期:key-{}, value-{}", memKey, expire);

        return expire == null ? 0 : expire;
    }

    @Override
    public  boolean setByKey(V value, long exp, TimeUnit unit, String... keys) {
        String memKey = this.getMemKey(keys);
        redisTemplate.opsForValue().set(memKey, value);
        return this.expireByKey(memKey, exp, unit);
    }

    @Override
    public  boolean setByKey(V value, long exp, String... keys) {
        return setByKey(value, exp, TimeUnit.SECONDS, keys);
    }

    @Override
    public  boolean setByKey(V value, String... keys) {
        return setByKey(value, 0, keys);
    }

    @Override
    public long increment(String key, long value) {
        // 永不过期
        return redisTemplate.opsForValue().increment(key, value);
    }

    @Override
    public long increment(String key, long value, long exp) {
        long afterIncrementValue = redisTemplate.opsForValue().increment(key, value);
        this.expireByKey(key, exp, TimeUnit.SECONDS);
        return afterIncrementValue;
    }

    @Override
    public  boolean rpush(String key, V value, long exp) {
        redisTemplate.opsForList().rightPush(key, value);
        return this.expireByKey(key, exp, TimeUnit.SECONDS);
    }

    @Override
    public  boolean lpush(String key, V value, long exp) {
        redisTemplate.opsForList().leftPush(key, value);
        return this.expireByKey(key, exp, TimeUnit.SECONDS);
    }

    @Override
    public  V lrange(String key) {
        log.debug("获取缓存数据:key-{}", key);
        return (V) redisTemplate.opsForList().range(key, 0, -1);
    }

    @Override
    public  boolean sadd(String key, V value, long exp) {
        redisTemplate.opsForSet().add(key, value);
        return this.expireByKey(key, exp, TimeUnit.SECONDS);
    }

    @Override
    public  V smembers(String key) {
        log.debug("获取缓存数据:key-{}", key);
        return (V) redisTemplate.opsForSet().members(key);
    }

    @Override
    public  boolean zadd(String key, V value, long exp) {
        redisTemplate.opsForZSet().add(key, value, Math.random());
        return this.expireByKey(key, exp, TimeUnit.SECONDS);
    }

    @Override
    public  V zrange(String key) {
        log.debug("获取缓存数据:key-{}", key);
        return (V) redisTemplate.opsForZSet().range(key, 0, -1);
    }

    @Override
    @SuppressWarnings("unchecked")
    public  V getByKey(String... keys) {
        String memKey = this.getMemKey(keys);
        V value = (V) redisTemplate.opsForValue().get(memKey);

        log.info("获取缓存数据:key-{}, value-{}", memKey, JSON.toJSonString(value));

        return value;
    }

    @Override
    public boolean delByKey(String... keys) {
        return redisTemplate.opsForValue().getOperations().delete(this.getMemKey(keys));
    }

    @Override
    public void convertAndSend(String channel, Object message) {
        redisTemplate.convertAndSend(channel, message);
    }

}

写在最后:

简单提下缓存三点问题:

  1. 缓存穿透:Redis及DB中都未查到数据,击穿Redis穿透DB,频繁高频请求会影响DB。可以缓存一定时长有效期的空值来拦截恶意攻击请求。
  2. 缓存击穿:某一热点数据过期,短时间内请求击穿Redis,直达DB,DB峰值飙升。可以设置热点数据永不过期。
  3. 缓存雪崩:大量热点数据在短时间内几乎同时过期了,DB瞬时压力陡升爆掉。同上可以设置热点数据永不过期或有效期随机错开。

缓存中间件Redis的生产应用模式,一般有主从模式,sentinel哨兵模式,cluster集群模式。后面有空了会在服务运维专栏聊聊各种模式的搭建及测试应用。

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号