专业的编程技术博客社区

网站首页 > 博客文章 正文

Spring Boot 换成方案Redis,四种常用方案汇总

baijin 2024-08-21 11:25:13 博客文章 9 ℃ 0 评论

在基于Springboot框架下,常用的分布式缓存解决方案,当属Redis莫属了。 因此,日常开发中,需要缓存,通常采用Redis来解决。

在本文中,主要是介绍基于Redis的各种实现方式,让大家较为全面地了解各个实现方案的特点或优势。

一、Spring Cache方案

  • 添加依赖关系

Spring Cache,通常可以在新建Spring boot工程的时候,引入即可(web、cache、redis),具体依赖如下:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 增加配置参数

依赖引入后,需要在application.properites中配置如下信息(注意:根据Redis开发环境自行调整端口、ip地址):

spring.redis.port=6379
spring.redis.host=127.0.0.1
spring.cache.cache-names=common-cache
  • 开启缓存(@EnableCaching)
@EnableCaching
@SpringBootApplication
public class DemoApplication 
{
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
  • Redis在Spring boot配置类注入

在完成开启缓存后,需要将redis相关配置类注入到容器,进而完成使用前的初始化,具体代码如下:

@Configuration
@ConditionalOnClass(RedisConnectionFactory.class)
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory, 
                                                                ResourceLoader resLoader) {
        RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory)
                                                    .cacheDefaults(determineConfiguration(resourceLoader.getClassLoader()));
        List<String> cacheNames = this.cacheProperties.getCacheNames();
        if (!cacheNames.isEmpty()) {
            builder.initialCacheNames(new LinkedHashSet<>(cacheNames));
        }
        return this.customizerInvoker.customize(builder.build());
    }

}

Springboot 默认会初始化RedisCacheManager 这个Bean,Spring通过这个Bean实现Cache接口,因此有了这个Bean,就可以使用spring的缓存注解和接口了。

  • 缓存使用
// i1、类注解,标注该类下的所有方法使用的缓存名称
@Service
@CacheConfig(cacheNames="common-cache")
public class TaskService {

}

// i2、方法注解 (对查询方法返回值进行缓存, 默认为key对应方法入参)
@Cacheable(key = "#id")
public Task getTaskById(Integer id) {
    return queryTaskById(id);
}

// i3、更新方法上的注解, 伴随着数据的更新,缓存也跟着更新
@CachePut(key = "#task.id")
public Task updateTaskById(Task task) {
      return update(task);
}

// i4、 删除方法上的注解,当数据库删除后,随之删除缓存
@CacheEvict() 
public void deleteTaskById (Integer id) {
     deleteTaskById(id);
}

二、Spring Data Redis方案

使用该方案,需要按照如下步骤:

  • 引入依赖(引入Spring data redis + 连接池)
<dependencies>
    <dependency>
    <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-pool2</artifactId>
    </dependency>
</dependencies>
  • 参数配置
# 基础配置
spring.redis.database=0
spring.redis.password=abc@123
spring.redis.port=6379
spring.redis.host=127.0.0.1
# 连接池信息配置
spring.redis.lettuce.pool.min-idle=1
spring.redis.lettuce.pool.max-idle=10
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=1ms
spring.redis.lettuce.shutdown-timeout=100ms
  • 相关配置类
@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> tp = new RedisTemplate<>();
                tp.setConnectionFactory(redisConnectionFactory);
                tp..setKeySerializer(new StringRedisSerializer());
                return tp;
        }

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

}

上面中,主要实例化了两个不同的bean对象:RedisTemplate 和 StringRedisTemplate,主要区别为:RedisTemplate 意味着 key、value都可以为对象; StringRedisTemplate 的key、value只能是String字符串!

  • 具体使用
// RedisTemplate 使用示例
@Service
public class TaskService {

    @Autowired
    RedisTemplate redisTemplate;
    
    public void test() {
        ValueOperations ops = redisTemplate.opsForValue();
        ops.set("key", "test");
        Object key = ops.get("key");
        System.out.println(key);
    }

}

// StringRedisTemplate 使用示例
@Service
public class TaskService {

    @Autowired
    StringRedisTemplate stringRedisTemplate;
    
    public void test() {
        ValueOperations ops = stringRedisTemplate.opsForValue();
        ops.set("key", "test");
        String key = ops.get("key");
        System.out.println(key);
    }

}

注意,上面的示例主要是针对单机Redis,不适用集群redis

三、JRedis方案

  • 添加依赖关系
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.7.0</version>
</dependency>
  • 添加配置
spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=abc@123
spring.redis.timeout=10
spring.redis.jedis.pool.max-active=200
spring.redis.jedis.pool.max-idle=10
spring.redis.jedis.pool.min-idle=10
spring.redis.jedis.pool.max-wait=10000
  • 相关配置类
@Configuration
@Data
public class JedisPoolFactory {
	@Value("${spring.redis.host}")
	private String host;
	
	@Value("${spring.redis.port}")
	private int port;
	
	@Value("${spring.redis.password}")
	private String password;
	
	@Value("${spring.redis.timeout}")
	private int timeout;
	
	@Value("${spring.redis.jedis.pool.max-active}")
	private int maxActive;
	
	@Value("${spring.redis.jedis.pool.max-idle}")
	private int maxIdle;
	
	@Value("${spring.redis.jedis.pool.min-idle}")
	private int minIdle;
	
	@Value("${spring.redis.jedis.pool.max-wait}")
	private long maxWaitMillis;
	
	@Bean
	public JedisPool jedisPoolFactory() {
		JedisPoolConfig poolConfig = new JedisPoolConfig();
		poolConfig.setMaxTotal(maxActive);
		poolConfig.setMaxIdle(maxIdle);
		poolConfig.setMinIdle(minIdle);
		poolConfig.setMaxWaitMillis(maxWaitMillis);
		JedisPool jedisPool = new JedisPool(poolConfig, host, port, timeout, password);
		return jedisPool;
	}
}
  • 编写Util类
@Component
public class RedisUtil {

    @Autowired
    private JedisPool jedisPool;
    
    /**
     * 存储字符串键值对
     */
    public String set(String key, String value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key, value);
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
    
    /**
     * 得到对应键的字符串值
     */
    public String get(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.get(key);
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
    
    /**
     * 删除字符串键值对
     */
    public Long del(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.del(key);
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
    
    /**
     * 存储对象
     */
    public String setObject(String key, Object value) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.set(key.getBytes(), ObjectUtil.serialize(value));
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
    
    /**
     * 得到对应键的对象
     */
    public Object getObject(String key) {
        try (Jedis jedis = jedisPool.getResource()) {
            byte[] byteArr =  jedis.get(key.getBytes());
            return ObjectUtil.unSerialize(byteArr);
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
}

ObjectUtil类示例:

public class ObjectUtil {
    
    /**
     * 判断对象是否为空
     */
    public static boolean isEmpty(Object o) {
        if (o == null) {
            return true;
        } 
        
        if (o instanceof String) {
            String s = (String) o;
            return "".equals(s.trim()) ? true : false;
        } else if (o instanceof Collection) {
            return ((Collection) o).isEmpty();
        } else if (o instanceof Map) {
            return ((Map) o).isEmpty();
        } else if (o.getClass().isArray()) {
            return Array.getLength(o) == 0;
        } else {
            return false;
        }
    }
    
    /**
     * 序列化
     */
    public static byte[] serialize(Object o) {
        try (ObjectOutputStream oos = new ObjectOutputStream(baos); 
            ByteArrayOutputStream baos = new ByteArrayOutputStream();) {
            oos = new ObjectOutputStream(baos);
            oos.writeObject(o);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new RedisException(e.getMessage());
        }
    }
    
    /**
     * 反序列化
     */
    public static Object unSerialize(byte[] byteArr) {
        try (ObjectInputStream ois = new ObjectInputStream(bais);
             ByteArrayInputStream bais = new ByteArrayInputStream(byteArr);){
            ois = new ObjectInputStream(bais);
            o = ois.readObject();
            return o;
        } catch (Exception e) {
            throw new RedisException(e.getMessage());
        }
    }
}

四、Redission方案

redission是当前是否火的redis解决方案,github上,start 有18K!下面引用一段官方简介:

Redisson是一个在Redis的基础上实现的Java驻内存数据网格(In-Memory Data Grid)。它不仅提供了一系列的分布式的Java常用对象,还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离(Separation of Concern),从而让使用者能够将精力更集中地放在处理业务逻辑上。

Redisson底层采用的是Netty 框架。支持Redis 2.8以上版本,支持Java1.6+以上版本。

基于上述介绍,估计大家也会跃跃欲试了!下面开始介绍如何使用!

  • 引入依赖关系
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.16.4</version>
</dependency>
  • 添加配置
# 基础配置
spring.redis.database=0
spring.redis.password=abc@123
spring.redis.port=6379
spring.redis.host=127.0.0.1
  • 配置类编写
@Slf4j
@Configuration
public class RedissonConfig {

    @Autowired
    private RedisProperties redisProperties;

    @Bean
    public RedissonClient redissonClient() throws Exception {
        RedisProperties.Cluster cluster = redisProperties.getCluster();
        if (null == cluster) {
            Config cfg = new Config();
            String redisUrl = String.format("redis://%s:%s", redisProperties.getHost(), redisProperties.getPort());
            cfg.useSingleServer().setAddress(redisUrl).setPassword(redisProperties.getPassword());
            cfg.useSingleServer().setDatabase(redisProperties.getDatabase());
            return Redisson.create(cfg);
        }

        List<String> nodes = cluster.getNodes();
        if (CollectionUtils.isEmpty(nodes)) {
            throw new RedisException();
        }

        String[] nodeArr = new String[nodes.size()];
        for (int i = 0; i < nodes.size(); i++) {
            nodeArr[i] = "redis://" + nodes.get(i);
        }

        Config cfg = new Config();
        cfg.useClusterServers() 
                .addNodeAddress(nodeArr).setPassword(redisProperties.getPassword()).setRetryAttempts(5).setScanInterval(2000);
        cfg.useClusterServers().setPassword(redisProperties.getPassword());
        return Redisson.create(cfg);
    }
}
  • 具体使用示例
@RestController
public class TestController {

    @Autowired
    private RedissonClient redissonClient;

    @GetMapping(value = "/lock/{key}")
    public String lockTest(@PathVariable("key") String key) {
        RLock lock = redissonClient.getLock(key);
        try {
            lock.lock();
            Thread.sleep(10000);
        } catch (Exception e) {

        } finally {
            lock.unlock();
        }
        return "ok";
    }
}

其他使用示例十分丰富,这里就不一一列举了!

五、总结

本文中,总结了常用的四种方案,相信这些使用方案,或多或少,大家都在项目中在使用,这里进行归总,对于没有使用过的同学,也可以参看学习!

相关问题,欢迎留言提问;欢迎大家点赞、关注、收藏~

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表