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

 

posted @   不忘初心2021  阅读(385)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2021-06-16 搭建eureka单机环境
点击右上角即可分享
微信分享提示