SpringBoot中Redis缓存的应用

2023-07-10  本文已影响0人  liushiping

一.spring-data-redis整合

1.1 spring-data-redis简介

Spring Boot 提供了对 Redis 集成的组件包:spring-boot-starter-data-redis,它依赖于 spring-data-redis 和 lettuce。Spring Boot 1.0 默认使用的是 Jedis 客户端,2.0 替换成了 Lettuce,但如果你从 Spring Boot 1.5.X 切换过来,几乎感受不到差异,这是因为 spring-boot-starter-data-redis 为我们隔离了其中的差异性。

  1. Lettuce:是一个可伸缩线程安全的 Redis 客户端,多个线程可以共享同一个 RedisConnection,它利用优秀 Netty NIO 框架来高效地管理多个连接。
  2. Spring Data:是 Spring 框架中的一个主要项目,目的是为了简化构建基于 Spring 框架应用的数据访问,包括非关系数据库、Map-Reduce 框架、云数据服务等,另外也包含对关系数据库的访问支持。
  3. Spring Data Redis:是 Spring Data 项目中的一个主要模块,实现了对 Redis 客户端 API 的高度封装,使对 Redis 的操作更加便捷。

1.2 整合spring data redis

引入依赖包,引入 commons-pool 2 是因为 Lettuce 需要使用 commons-pool 2 创建 Redis 连接池。

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

1.3 redis连接配置

1.redis单例模式连接配置
redis的单节点实例,可以通过下面的配置连接redis单节点实例数据库:

spring:
  redis:
    database: 0 # Redis 数据库索引(默认为 0)
    host: 192.168.161.3 # Redis 服务器地址
    port: 6379 # Redis 服务器连接端口
    password: 123456 # Redis 服务器连接密码(默认为空)
    timeout:  5000  # 连接超时,单位ms
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制) 默认 8
        max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
        max-idle: 8 # 连接池中的最大空闲连接 默认 8
        min-idle: 0 # 连接池中的最小空闲连接 默认 0

2.redis哨兵模式连接配置
redis另外一种非常常用的部署模式是哨兵模式,如果你的公司使用的是这种部署模式,它相对于单实例模式更加的高可用。


哨兵连接模式

需要注意的是,当我们使用spring boot连接哨兵模式的redis集群,连接的是sentinel节点,而不是redis服务实例节点。注意上图的连接顺序。 Application Client是我们的应用程序,sentinel node是哨兵节点。

spring:
  redis:
    password: 123456
    timeout: 5000
    sentinel:     # 哨兵模式连接配置
      master: mymaster    #master节点名称,redis sentinel模式安装的时候会配置
      nodes: 192.168.1.201:26379,192.168.1.202:26379,192.168.1.203:26379      # 哨兵的IP:Port列表
    lettuce
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0

3.redis集群模式连接配置
Redis Cluster是Redis的分布式解决方案,在Redis 3.0版本正式推出的,有效解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster架构达到负载均衡的目的。分布式集群首要解决问题是:把整个数据集按照分区规则映射到多个节点上,即把数据集按照一定的规则划分到多个节点上,每个节点只保存整个数据集的一个子集

无论是单节点还是master-slave,其redis服务都保存了数据集的完整副本。cluster模式不是,其redis实例节点只包含完整数据集的子集。

集群连接模式
  1. 当程序客户端随意访问一个redis node节点时,可能会发现其操作的数据或者应该写入的数据位置,并不在当前node节点上。
  2. 此时,当前被访问的redis node节点会告知客户端,你应该去哪个节点访问数据或写入数据
  3. 然后客户端获取目标node节点的地址,重定向到该节点的地址,去访问或写入数据。

下面的配置,是针对redis集群模式连接访问的配置。

spring:
  redis:
    password: 123456
    timeout: 5000
    database: 0
    cluster:     #集群模式配置
      nodes: 192.168.1.11:6379,192.168.1.12:6379,192.168.1.13:6379,192.168.1.14:6379,192.168.1.15:6379,192.168.1.16:6379
      max-redirects: 3  # 重定向的最大次数
    lettuce:
      pool:
        max-active: 8
        max-wait: -1
        max-idle: 8
        min-idle: 0

二.使用redisTemplate操作数据

2.1 redis模板封装类

RedisTemplate 的封装使我们能够更方便的进行redis数据操作,比直接使用Jedis或者Lettuce的java SDK要方便很多。RedisTemplate作为java 操作redis数据库的API模板更通用,可以操作所有的redis数据类型。

// 注入RedisTemplate,更通用
@Resource
private RedisTemplate<String, Object> redisTemplate;

ValueOperations<String,Object> ValueOperations = redisTemplate.opsForValue();//操作字符串
HashOperations<String, String, Object> hashOperations = redisTemplate.opsForHash();//操作 hash
ListOperations<String, Object> listOperations = redisTemplate.opsForList();//操作 list
SetOperations<String, Object> setOperations = redisTemplate.opsForSet();//操作 set
ZSetOperations<String, Object> zSetOperations = redisTemplate.opsForZSet();//操作有序 set

ListOperations、ValueOperations、HashOperations、SetOperations、ZSetOperations等都是针对专有数据类型进行操作,使用起来更简洁。
除了RedisTemplate模板类,还有另一个模板类叫做StringRedisTemplate 。二者都提供了用来操作redis数据库的API。
二者的区别在于

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @GetMapping("/save")
    public void saveArticle(){
        Article article = Article.builder().author("William")
                .content("Content").createTime(new Date()).title("SpringBoot实战").build();
        redisTemplate.opsForValue().set("article:jdk", article);
        stringRedisTemplate.opsForValue().set("article:string", "SpringBoot实战");
    }

2.2 解决Redis客户端查看缓存结果人工无法阅读问题

因为RedisTemplate默认使用JDK的二进制序列化方式(JdkSerializationRedisSerializer),所以Redis序列化存储后人看不懂,但是程序是能看懂的。为了使人可以阅读,可以使用下面两种序列化器:

@Configuration
public class RedisConfig {

    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        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);

        //重点在这四行代码
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}

序列化方式对比:

三. 集群多节点应用session共享

3.1 spring session 共享的实现原理

集群应用的Session共享

3.2 集成Spring session

1.引入spring-session-redis的maven依赖

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

2.配置启用Redis的httpSession
在启动类上方加上注解,启动SpringSession管理应用的session,并设置session数据的有效期30分钟

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 30 * 60 * 1000)

3.配置redis链接信息(application.yml)
参考第一节配置

4.测试

@Controller
public class SessionController {

  @RequestMapping(value="/uid",method = RequestMethod.GET)
  public @ResponseBody
  String uid(HttpSession session) {
    return "sessionId:" + session.getId();
  }
}

通过不同端口启动两个服务:

-Dserver.port=8080 -Dserver.httpPort=80 -Dspring.profiles.active=dev -Ddebug

-Dserver.port=8081 -Dserver.httpPort=81 -Dspring.profiles.active=dev -Ddebug

依次访问,看看效果.通过返回值session.getId()即:sessionid来判断,如果sessionid一致,则证明session共享成功了。

四. RedisLockRegistry分布式锁

4.1 集成spring-integration-redis

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

4.1 注册RedisLockRegistry

@Configuration
public class RedisLockConfig {

     @Bean
     public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
         //第一个参数redisConnectionFactory
         //第二个参数registryKey,分布式锁前缀,设置为项目名称会好些
         //该构造方法对应的分布式锁,默认有效期是60秒.可以自定义
         return new RedisLockRegistry(redisConnectionFactory, "boot-launch");
         //return new RedisLockRegistry(redisConnectionFactory, "boot-launch",60);
     }
}

4.3 使用RedisLockRegistry

    @DeleteMapping(value = "/delete/{id}")
    public void updateUser(@PathVariable("id") Long id) {
        String lockKey = "lock:" + id;
        //获取锁资源
        Lock lock = redisLockRegistry.obtain(lockKey);
        try {
            //加锁
            lock.lock();

            //这里写需要处理业务的业务代码
            articleService.deleteArticle(id);
        } finally {
            lock.unlock();   //释放锁
        }
    }
上一篇 下一篇

猜你喜欢

热点阅读