Redis基于Stream实现消息队列
先上效果图
需要使用redis5.0以上版本,使用了redis5.0新增的数据类型Stream,使用block表示阻塞等待,直到有新的数据添加
这里不需要再redis新增Stream和消息组
pom文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | <?xml version= "1.0" encoding= "UTF-8" ?> <project xmlns= "http://maven.apache.org/POM/4.0.0" xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation= "http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion> 4.0 . 0 </modelVersion> <groupId>com.java</groupId> <artifactId>redis-study</artifactId> <version> 1.0 -SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version> 2.4 . 3 </version> <relativePath /> <!-- lookup parent from repository --> </parent> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> </project> |
配置文件
1 2 3 4 5 6 | server.port= 8080 spring.redis.database= 0 spring.redis.host= 192.168 . 0.101 spring.redis.port= 6379 spring.redis.password= 123 |
代码结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package com.java; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.annotation.PropertyAccessor; import com.fasterxml.jackson.databind.ObjectMapper; 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.StringRedisSerializer; /** * @Description: * @Author: qiuxie * @Create: 2023/6/15 18:38 */ @Configuration public class RedisConfig { @Bean public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) { RedisTemplate<String, Object> template = new RedisTemplate<String, Object>(); template.setConnectionFactory(factory); Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object. class ); ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jackson2JsonRedisSerializer.setObjectMapper(om); StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); template.setKeySerializer(stringRedisSerializer); // hash的key也采用String的序列化方式 template.setHashKeySerializer(stringRedisSerializer); // value序列化方式采用jackson template.setValueSerializer(jackson2JsonRedisSerializer); // hash的value序列化方式采用jackson template.setHashValueSerializer(jackson2JsonRedisSerializer); template.afterPropertiesSet(); return template; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package com.java.service.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.core.StreamOperations; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.stream.StreamListener; import org.springframework.stereotype.Component; import java.util.Map; /** * @Description: 消费者 * @Author: qiuxie * @Create: 2023/6/15 18:30 */ @Component public class RedisConsumer implements StreamListener<String, MapRecord<String,String,String>> { private final Logger log= LoggerFactory.getLogger(RedisConsumer. class ); public static final String streamName= "qiuxieStream" ; public static final String groupName= "qiuxieGroup" ; public final String consumerName= "emailConsumer" ; @Autowired private StringRedisTemplate stringRedisTemplate; @Override public void onMessage(MapRecord<String, String, String> message) { log.warn( "【消费者】Stream名称:{},消息内容:{}" ,streamName,message.getValue()); //获取生产者消息 Map<String, String> msgMap = message.getValue(); log.info( "msgMap:{}" ,msgMap); //业务逻辑 略 //ps:通过 msgMap.get("key") 拿到需要参数执行业务逻辑 StreamOperations<String, String, String> streamOperations = stringRedisTemplate.opsForStream(); //消息应答 streamOperations.acknowledge( streamName,groupName,message.getId() ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 | package com.java.service.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.stream.Consumer; import org.springframework.data.redis.connection.stream.MapRecord; import org.springframework.data.redis.connection.stream.ReadOffset; import org.springframework.data.redis.connection.stream.StreamOffset; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.serializer.StringRedisSerializer; import org.springframework.data.redis.stream.StreamMessageListenerContainer; import java.time.Duration; import java.util.Collections; /** * @Description: * @Author: qiuxie * @Create: 2023/6/15 18:34 */ @Configuration public class RedisStreamConfig { @Autowired private RedisConsumer emailConsumer; @Autowired private RedisTemplate<String,Object> redisTemplate; private final Logger log= LoggerFactory.getLogger(RedisStreamConfig. class ); @Bean public StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String,String,String>> emailListenerContainerOptions(){ StringRedisSerializer stringRedisSerializer = new StringRedisSerializer(); return StreamMessageListenerContainer.StreamMessageListenerContainerOptions .builder() //block读取超时时间 .pollTimeout(Duration.ofSeconds( 3 )) //count 数量(一次只获取一条消息) .batchSize( 1000 ) //序列化规则 .serializer( stringRedisSerializer ) .build(); } /** * 开启监听器接收消息 */ @Bean public StreamMessageListenerContainer<String, MapRecord<String,String,String>> emailListenerContainer(RedisConnectionFactory factory, StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, MapRecord<String,String,String>> streamMessageListenerContainerOptions){ StreamMessageListenerContainer<String,MapRecord<String,String,String>> listenerContainer = StreamMessageListenerContainer.create(factory, streamMessageListenerContainerOptions); //如果 流不存在 创建 stream 流 if ( !redisTemplate.hasKey(RedisConsumer.streamName)){ redisTemplate.opsForStream().add(RedisConsumer.streamName, Collections.singletonMap( "" , "" )); log.info( "初始化stream:{} success" , RedisConsumer.streamName); } //创建消费者组 try { redisTemplate.opsForStream().createGroup(RedisConsumer.streamName, RedisConsumer.groupName); } catch (Exception e) { log.info( "消费者组:{} 已存在" , RedisConsumer.groupName); } //注册消费者 消费者名称,从哪条消息开始消费,消费者类 // > 表示没消费过的消息 // $ 表示最新的消息 listenerContainer.receive( Consumer.from(RedisConsumer.groupName, emailConsumer.consumerName), StreamOffset.create(RedisConsumer.streamName, ReadOffset.lastConsumed()), emailConsumer ); listenerContainer.start(); return listenerContainer; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | package com.java.controller.front; import com.java.service.impl.RedisConsumer; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.connection.stream.ObjectRecord; import org.springframework.data.redis.connection.stream.RecordId; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StreamOperations; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; /** * @Description: 生产者 * @Author: qiuxie * @Create: 2023/6/15 17:02 */ @RestController public class RedisProducerController { @Autowired private RedisTemplate<String,Object> redisTemplate; @GetMapping ( "/generateData" ) public String generateData(String message) { StreamOperations<String, Object, Object> streamOperations = redisTemplate.opsForStream(); RecordId recordId = streamOperations.add(ObjectRecord.create(RedisConsumer.streamName, message)); System.out.println( "Published message with record ID: " + recordId); return "Success" ; } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package com.java; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Description: 设置年轻代和老年代的比例 1:4 * E:S0:S1 8:1:1 * @Author: Yourheart * @Create: 2023/4/21 11:27 */ @SpringBootApplication () public class MysqlServiceApplication { public static void main(String[] args) { SpringApplication.run(MysqlServiceApplication. class , args); } } |
使用postman测试,127.0.0.1:8080/generateData?message=aaaaa
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
2021-06-16 搭建eureka单机环境