当前位置:

首页 > 编程开发 > Spring中怎么实现响应式Redis交互

Spring中怎么实现响应式Redis交互

Redis Assistant
Redis Assistant

Redis Assistant 是一款为Redis设计的高效可视化管理与监控工具,提供多种操作模式,高效处理服务器数据。日常使用里的持续防护和信息管理会更突出,适合把安全控制放进长期使用流程中的场景。

立即下载
¥19
Windows/macOS/Linux 2026-08-22
正版软件 Windows macOS软件 Linux 数据库管理工具

本文将模拟一个用户服务,并使用Redis作为数据存储服务器。涉及两个javabean,用户与权益publicclassUser{privatelongid;privateStringname;//标签privateStringlabel;//收货地址经度privateDoubledeliveryAddressLon;//收货地址维度privateDoubledeliveryAddressLat;//最新签到日privateStringlastSigninDay;//积分privateIntegerscor

本文目录

    本文将模拟一个用户服务,并使用Redis作为数据存储服务器。
    涉及两个java bean,用户与权益

    public class User {
        private long id;
        private String name;
        // 标签
        private String label;
        // 收货地址经度
        private Double deliveryAddressLon;
        // 收货地址维度
        private Double deliveryAddressLat;
        // 最新签到日
        private String lastSigninDay;
        // 积分
        private Integer score;
        // 权益
        private List rights;
        ...
    }
    
    public class Rights {
        private Long id;
        private Long userId;
        private String name;
        ...
    }

    启动

    引入依赖

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

    添加Redis配置

    spring.redis.host=192.168.56.102
    spring.redis.port=6379
    spring.redis.password=
    spring.redis.timeout=5000

    SpringBoot启动

    @SpringBootApplication
    public class UserServiceReactive {
        public static void main(String[] args) {
            new SpringApplicationBuilder(
                    UserServiceReactive.class)
                    .web(WebApplicationType.REACTIVE).run(args);
        }
    }

    应用启动后,Spring会自动生成ReactiveRedisTemplate(它的底层框架是Lettuce)。
    ReactiveRedisTemplate与RedisTemplate使用类似,但它提供的是异步的,响应式Redis交互方式。
    这里再强调一下,响应式编程是异步的,ReactiveRedisTemplate发送Redis请求后不会阻塞线程,当前线程可以去执行其他任务。
    等到Redis响应数据返回后,ReactiveRedisTemplate再调度线程处理响应数据。
    响应式编程可以通过优雅的方式实现异步调用以及处理异步结果,正是它的最大的意义。

    序列化

    ReactiveRedisTemplate默认使用的序列化是Jdk序列化,我们可以配置为json序列化

    @Bean
    public RedisSerializationContext redisSerializationContext() {
        RedisSerializationContext.RedisSerializationContextBuilder builder = RedisSerializationContext.newSerializationContext();
        builder.key(StringRedisSerializer.UTF_8);
        builder.value(RedisSerializer.json());
        builder.hashKey(StringRedisSerializer.UTF_8);
        builder.hashValue(StringRedisSerializer.UTF_8);
    
        return builder.build();
    }
    
    @Bean
    public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) {
        RedisSerializationContext serializationContext = redisSerializationContext();
        ReactiveRedisTemplate reactiveRedisTemplate = new ReactiveRedisTemplate(connectionFactory,serializationContext);
        return reactiveRedisTemplate;
    }

    builder.hashValue方法指定Redis列表值的序列化方式,由于本文Redis列表值只存放字符串,所以还是设置为StringRedisSerializer.UTF_8。

    基本数据类型

    ReactiveRedisTemplate支持Redis字符串,散列,列表,集合,有序集合等基本的数据类型。
    本文使用散列保存用户信息,列表保存用户权益,其他基本数据类型的使用本文不展开。

    public Mono  save(User user) {
        ReactiveHashOperations opsForHash = redisTemplate.opsForHash();
        Mono  userRs = opsForHash.putAll("user:" + user.getId(), beanToMap(user));
        if(user.getRights() != null) {
            ReactiveListOperations opsForRights = redisTemplate.opsForList();
            opsForRights.leftPushAll("user:rights:" + user.getId(), user.getRights()).subscribe(l -> {
                logger.info("add rights:{}", l);
            });
        }
        return userRs;
    }

    beanToMap方法负责将User类转化为map。

    HyperLogLog

    Redis HyperLogLog结构可以统计一个集合内不同元素的数量。
    使用HyperLogLog统计每天登录的用户量

    public Mono  login(User user) {
        ReactiveHyperLogLogOperations opsForHyperLogLog = redisTemplate.opsForHyperLogLog();
        return opsForHyperLogLog.add("user:login:number:" + LocalDateTime.now().toString().substring(0, 10), user.getId());
    }

    BitMap

    Redis BitMap(位图)通过一个Bit位表示某个元素对应的值或者状态。由于Bit是计算机存储中最小的单位,使用它进行储存将非常节省空间。
    使用BitMap记录用户本周是否有签到

    public void addSignInFlag(long userId) {
        String key = "user:signIn:" + LocalDateTime.now().getDayOfYear()/7 + (userId >> 16);
        redisTemplate.opsForValue().setBit(
                key, userId & 0xffff , true)
        .subscribe(b -> logger.info("set:{},result:{}", key, b));
    }

    userId高48位用于将用户划分到不同的key,低16位作为位图偏移参数offset。
    offset参数必须大于或等于0,小于2^32(bit 映射被限制在 512 MB 之内)。

    Geo

    Redis Geo可以存储地理位置信息,并对地理位置进行计算。
    如查找给定范围内的仓库信息

    public Flux getWarehouseInDist(User u, double dist) {
        ReactiveGeoOperations geo = redisTemplate.opsForGeo();
        Circle circle = new Circle(new Point(u.getDeliveryAddressLon(), u.getDeliveryAddressLat()), dist);
        RedisGeoCommands.GeoRadiusCommandArgs args =
                RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().sortAscending();
        return geo.radius("warehouse:address", circle, args);
    }

    warehouse:address这个集合中需要先保存好仓库地理位置信息。
    ReactiveGeoOperations#radius方法可以查找集合中地理位置在给定范围内的元素,它中还支持添加元素到集合,计算集合中两个元素地理位置距离等操作。

    Lua

    ReactiveRedisTemplate也可以执行Lua脚本。
    下面通过Lua脚本完成用户签到逻辑:如果用户今天未签到,允许签到,积分加1,如果用户今天已签到,则拒接操作。

    public Flux addScore(long userId) {
        DefaultRedisScript script = new DefaultRedisScript<>();
        script.setScriptSource(new ResourceScriptSource(new ClassPathResource("/signin.lua")));
        List keys = new ArrayList<>();
        keys.add(String.valueOf(userId));
        keys.add(LocalDateTime.now().toString().substring(0, 10));
        return redisTemplate.execute(script, keys);
    }

    signin.lua内容如下

    local score=redis.call('hget','user:'..KEYS[1],'score')
    local day=redis.call('hget','user:'..KEYS[1],'lastSigninDay')
    if(day==KEYS[2])
        then
        return '0'
    else
        redis.call('hset','user:'..KEYS[1],'score', score+1,'lastSigninDay',KEYS[2])
        return '1'
    end

    Stream

    Redis Stream 是 Redis 5.0 版本新增加的数据类型。该类型可以实现消息队列,并提供消息的持久化和主备复制功能,并且可以记住每一个客户端的访问位置,还能保证消息不丢失。

    Redis借鉴了kafka的设计,一个Stream内可以存在多个消费组,一个消费组内可以存在多个消费者。
    如果一个消费组内某个消费者消费了Stream中某条消息,则这消息不会被该消费组其他消费者消费到,当然,它还可以被其他消费组中某个消费者消费到。

    下面定义一个Stream消费者,负责处理接收到的权益数据

    @Component
    public class RightsStreamConsumer implements ApplicationRunner, DisposableBean {
        private static final Logger logger = LoggerFactory.getLogger(RightsStreamConsumer.class);
    
        @Autowired
        private RedisConnectionFactory redisConnectionFactory;
    
        private StreamMessageListenerContainer> container;
        // Stream队列
        private static final String STREAM_KEY = "stream:user:rights";
        // 消费组
        private static final String STREAM_GROUP = "user-service";
        // 消费者
        private static final String STREAM_CONSUMER = "consumer-1";
    
        @Autowired
        @Qualifier("reactiveRedisTemplate")
        private ReactiveRedisTemplate redisTemplate;
    
        public void run(ApplicationArguments args) throws Exception {
    
            StreamMessageListenerContainer.StreamMessageListenerContainerOptions> options =
                    StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
                            .batchSize(100) //一批次拉取的最大count数
                            .executor(Executors.newSingleThreadExecutor())  //线程池
                            .pollTimeout(Duration.ZERO) //阻塞式轮询
                            .targetType(Rights.class) //目标类型(消息内容的类型)
                            .build();
            // 创建一个消息监听容器
            container = StreamMessageListenerContainer.create(redisConnectionFactory, options);
    
            // prepareStreamAndGroup查找Stream信息,如果不存在,则创建Stream
            prepareStreamAndGroup(redisTemplate.opsForStream(), STREAM_KEY , STREAM_GROUP)
                    .subscribe(stream -> {
                // 为Stream创建一个消费者,并绑定处理类
                container.receive(Consumer.from(STREAM_GROUP, STREAM_CONSUMER),
                        StreamOffset.create(STREAM_KEY, ReadOffset.lastConsumed()),
                        new StreamMessageListener());
                container.start();
            });
        }
    
        @Override
        public void destroy() throws Exception {
            container.stop();
        }
    
        // 查找Stream信息,如果不存在,则创建Stream
        private Mono prepareStreamAndGroup(ReactiveStreamOperations ops, String stream, String group) {
            // info方法查询Stream信息,如果该Stream不存在,底层会报错,这时会调用onErrorResume方法。
            return ops.info(stream).onErrorResume(err -> {
                logger.warn("query stream err:{}", err.getMessage());
                // createGroup方法创建Stream
                return ops.createGroup(stream, group).flatMap(s -> ops.info(stream));
            });
        }
    
        // 消息处理对象
        class  StreamMessageListener implements StreamListener> {
            public void onMessage(ObjectRecord message) {
                // 处理消息
                RecordId id = message.getId();
                Rights rights = message.getValue();
                logger.info("receive id:{},rights:{}", id, rights);
                redisTemplate.opsForList().leftPush("user:rights:" + rights.getUserId(), rights).subscribe(l -> {
                    logger.info("add rights:{}", l);
                });
            }
        }
    }

    下面看一下如何发送信息

    public Mono addRights(Rights r) {
        String streamKey = "stream:user:rights";//stream key
        ObjectRecord record = ObjectRecord.create(streamKey, r);
        Mono mono = redisTemplate.opsForStream().add(record);
        return mono;
    }

    创建一个消息记录对象ObjectRecord,并通过ReactiveStreamOperations发送信息记录。

    Sentinel、Cluster

    ReactiveRedisTemplate也支持Redis Sentinel、Cluster集群模式,只需要调整配置即可。
    Sentinel配置如下

    spring.redis.sentinel.master=mymaster
    spring.redis.sentinel.nodes=172.17.0.4:26379,172.17.0.5:26379,172.17.0.6:26379
    spring.redis.sentinel.password=

    spring.redis.sentinel.nodes配置的是Sentinel节点IP地址和端口,不是Redis实例节点IP地址和端口。

    Cluster配置如下

    spring.redis.cluster.nodes=172.17.0.2:6379,172.17.0.3:6379,172.17.0.4:6379,172.17.0.5:6379,172.17.0.6:6379,172.17.0.7:6379
    spring.redis.lettuce.cluster.refresh.period=10000
    spring.redis.lettuce.cluster.refresh.adaptive=true

    如Redis Cluster中node2是node1的从节点,Lettuce中会缓存该信息,当node1宕机后,Redis Cluster会将node2升级为主节点。但Lettuce不会自动将请求切换到node2,因为它的缓冲没有刷新。
    开启spring.redis.lettuce.cluster.refresh.adaptive配置,Lettuce可以定时刷新Redis Cluster集群缓存信息,动态改变客户端的节点情况,完成故障转移。

    暂时未发现ReactiveRedisTemplate实现pipeline,事务的方案。

    本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系bd@zhengruan.com
    作者最新文章
    编程开发
    相关文章 更多
    spring hibernate 详细教程:新手也能快速学会
    spring hibernate 详细教程:新手也能快速学会

    Spring与Hibernate是Java企业开发的核心框架。Spring通过依赖注入管理对象生命周期,简化开发;Hibernate作为ORM工具,实现对象与数据库映射,避免繁琐SQL。两者结合能构建结构清晰、易于维护的应用。教程涵盖环境搭建、实体映射、数据操作及事务管理等实践内容,帮助新手快速掌握整合开发。

    Spring Boot 注册 Servlet 的五种方法(最新整理)
    Spring Boot 注册 Servlet 的五种方法(最新整理)

    在 Spring Boot 里注册 Servlet,其实有不止一种做法,而且每种方式适合的场景也不太一样:有的更偏配置化,有的更适合注解扫描,还有的更强调运行时动态注册。下面把常见的五种方式完整梳理一遍,方便直接对照使用。 一、Spring Boot 提供了 ServletRegistrationB

    从IDEA到Docker部署Spring Boot环境变量配置详解
    从IDEA到Docker部署Spring Boot环境变量配置详解

    一、问题背景 在 Spring Boot 项目里,配置文件用占位符引用环境变量可以说是家常便饭了,比如下面这种写法: url: jdbc:mysql://${datasource_addr:10.xxx.xxx.xxx:5236}/mydb 但有一个现象挺让人头疼的:同样的配置,在 IDEA 里跑得

    Spring MVC+ajax进行信息验证的方法
    Spring MVC+ajax进行信息验证的方法

    采用AJAX与SpringMVC的异步验证,通过XMLHttpRequest对象实现局部数据交换,利用@ResponseBody返回JSON或字符串,结合@RequestBody解析请求体,完成表单无刷新校验,包括用户名重复检测与密码一致性验证。该方法提升交互效率,减少带宽占用,有效确保数据准确性,提升用户体验。

    php72w redis,docker php7安装php-redis
    php72w redis,docker php7安装php-redis

    在Docker环境下为PHP安装Redis扩展:手动编译需下载源码、安装phpize、配置编译并安装,生成redis.so并在php.d目录新建redis.ini加载,重启PHP-FPM;也可直接yum安装php72w-pecl-redis,免手动编译。

    php 如何配置 redis,php 如何设置redis
    php 如何配置 redis,php 如何设置redis

    配置PHP的Redis扩展:先安装Redis服务,从GitHub下载phpredis源码,用phpize编译安装,修改php.ini添加extension=redis.so,重启php-fpm或Apache,通过phpinfo验证并连接Redis。

    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】
    c++如何将结构体序列化为Redis兼容的RESP协议格式【深度】

    C++结构体无法直接序列化为RESP协议,需手动将字段映射为*N数组嵌套$lenbulkstring形式。整数需转换为字符串并加:前缀,空字符串用$0\r\n,null用$-1\r\n。长度计算需精确到字节数,特别注意二进制安全,同时确保字段顺序与结构体定义一致。

    Spring Security 2026 构建安全、可靠的企业应用实践指南
    Spring Security 2026 构建安全、可靠的企业应用实践指南

    SpringSecurity2026是一款强大的安全框架,提供多因素认证、OAuth2.0与OpenIDConnect协议支持以及强密码管理功能;支持基于角色、权限和表达式的细粒度授权控制;同时涵盖跨站请求伪造与跨站脚本攻击等安全防护最佳实践,助力企业级应用安全构建。

    如何在Golang架构中部署Redict作为高能数据临时存储中转站
    如何在Golang架构中部署Redict作为高能数据临时存储中转站

    Redis作为跨服务临时状态枢纽,需按消息队列、缓存和原子状态三重角色设计。选型用List实现任务分发及延迟重试,Stream用于事件广播与位点回溯。go-redis/v8需配置MinIdleConns、MaxRetries及超时。数据写入应JSON序列化并带type标签、设合理TTL、将关键字段提至顶层。消费通过原子GET+DEL或processedkey

    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制
    TP8.0 高并发下库存超卖?Redis 原子递减结合数据库乐观锁控制

    在高并发秒杀场景下,利用Redis的减库存原子操作预扣减库存拦截超卖请求,再通过数据库乐观锁(版本号与库存校验)确保最终一致性,异常时回滚Redis库存并借助Lua脚本保证原子性,从而有效防止库存超卖。

    查看更多
    精品专题 更多
    装机必备
    装机必备

    正软商城装机必备专区,精选办公、浏览器、安全防护、影音播放、压缩解压、设计创作和系统工具等电脑常用正版软件,帮助用户快速完成新电脑软件配置。

    Windows
    Windows

    正软商城Windows软件专区,汇集适用于Windows电脑的办公、设计、安全防护、影音播放、开发工具和系统优化软件,提供软件介绍、系统要求、正版授权及购买下载服务。

    PDF教程
    PDF教程

    正软商城PDF教程频道提供PDF编辑、转换、合并、拆分、压缩及格式处理方法,同时介绍常用PDF软件和工具的使用技巧。

    Mac软件 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    Aperty macOS版
    Aperty macOS版
    macOS

    Aperty 是 Skylum 推出的 AI 人像修图软件,支持自然肤质修饰、瑕疵处理、面部与身体调整、预设及批量同步。

    WINDOWS 更多
    讯飞智作 网页版
    讯飞智作 网页版
    Windows/ macOS/ Linux

    讯飞智作是一款基于语音合成技术、虚拟人驱动技术与数字人形象声音构建能力的 AI 数字人音视频创作平台。

    火山引擎 网页版
    火山引擎 网页版
    Windows/ macOS/ Linux

    火山引擎是字节跳动旗下企业级云与AI服务平台。聚焦大模型核心能力,依托字节自研豆包多模态大模型,凭借火山方舟一站式平台,提供模型精调、推理部署、智能应用搭建等全链路服务,结合高性能AI算力,助力各行业快速落地大模型数字化与智能化应用。

    PixTV 网页版
    PixTV 网页版
    Windows

    PixTV 是一款在浏览器中使用的 AI 视觉内容创作平台,提供图片、视频、音频、文字生成、AI 分镜、局部重绘、提示词反推和无限画布等工具,适合短剧、漫剧、广告及社交媒体内容制作。

    即将离开本站
    您即将前往第三方网站,请确认是否继续?