Redis Stream消息队列使用
Redis命令
生产消息
127.0.0.1:6379> XADD stream:queue * name Jobs
"1712041645839-0"
拉取消息
127.0.0.1:6379> XREAD COUNT 5 STREAMS stream:queue 0-0
1) 1) "stream:queue"
2) 1) 1) "1712046205751-0"
2) 1) "name"
2) "Jobs"
阻塞式拉取消息
127.0.0.1:6379> XREAD COUNT 5 BLOCK 0 STREAMS stream:queue 0-0
1) 1) "stream:queue"
2) 1) 1) "1712046321183-0"
2) 1) "name"
2) "Jobs"
发布订阅
127.0.0.1:6379> XGROUP CREATE stream:queue stream:queue:group 0-0
OK
消费
127.0.0.1:6379> XREADGROUP GROUP stream:queue:group C-1 COUNT 5 BLOCK 0 STREAMS stream:queue >
1) 1) "stream:queue"
2) 1) 1) "1712046452492-0"
2) 1) "name"
2) "Jobs"
SpringBoot集成
引入依赖
<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>
配置属性
spring.redis.database=0
spring.redis.host=localhost
spring.redis.password=
spring.redis.port=6379
spring.redis.connect-timeout=5s
spring.redis.timeout=5s
spring.redis.lettuce.pool.min-idle=0
spring.redis.lettuce.pool.max-idle=8
spring.redis.lettuce.pool.max-active=8
spring.redis.lettuce.pool.max-wait=-1ms
自定义RedisTemplate
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.GenericJackson2JsonRedisSerializer;
import static org.springframework.data.redis.serializer.StringRedisSerializer.UTF_8;
@Configuration
public class AppConfig {
@Bean
RedisTemplate<String, Object> provideRedisTemplate(final RedisConnectionFactory connectionFactory) {
final RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
final GenericJackson2JsonRedisSerializer serializer = new GenericJackson2JsonRedisSerializer();
serializer.configure(mapper -> {
// 将日期序列化为可读字符串
mapper.disable(com.fasterxml.jackson.databind.SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
// 注册模块
mapper.registerModule(new com.fasterxml.jackson.datatype.jsr310.JavaTimeModule());
});
template.setKeySerializer(UTF_8);
template.setValueSerializer(serializer);
template.setHashKeySerializer(UTF_8);
template.setHashValueSerializer(serializer);
template.afterPropertiesSet();
return template;
}
}
绑定队列、消费者
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.connection.stream.Consumer;
import org.springframework.data.redis.connection.stream.ObjectRecord;
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.core.StreamOperations;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.time.Duration;
@Component
public class RedisStreamMessageQueueRunner implements ApplicationRunner, DisposableBean {
private static final String STREAM_KEY = "stream:queue";
private static final String STREAM_QUEUE_GROUP = "stream:queue:group";
private final RedisTemplate<String, Object> redisTemplate;
private StreamMessageListenerContainer<String, ObjectRecord<String, String>> container;
public RedisStreamMessageQueueRunner(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void destroy() throws Exception {
this.container.stop();
}
@Override
public void run(ApplicationArguments args) throws Exception {
final StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> options = StreamMessageListenerContainer.StreamMessageListenerContainerOptions.builder()
.pollTimeout(Duration.ZERO)
.targetType(String.class)
.build();
final StreamMessageListenerContainer<String, ObjectRecord<String, String>> streamMessageListenerContainer = StreamMessageListenerContainer.create(
this.redisTemplate.getConnectionFactory(),
options);
streamMessageListenerContainer.receive(
Consumer.from(STREAM_QUEUE_GROUP, "消费者-1"),
StreamOffset.create(STREAM_KEY, ReadOffset.lastConsumed()),
new StreamMessageListener1(this.redisTemplate)
);
this.container = streamMessageListenerContainer;
this.container.start();
}
private static class StreamMessageListener1 implements StreamListener<String, ObjectRecord<String, String>> {
private final Logger log = LoggerFactory.getLogger(StreamMessageListener1.class);
private final RedisTemplate<String, Object> redisTemplate;
public StreamMessageListener1(RedisTemplate<String, Object> redisTemplate) {
this.redisTemplate = redisTemplate;
}
@Override
public void onMessage(ObjectRecord<String, String> message) {
log.info("来自STREAM的消息 message.getId() => {}", message.getId());
log.info("来自STREAM的消息 message.getStream() => {}", message.getStream());
log.info("来自STREAM的消息 message.getValue() => {}", message.getValue());
final StreamOperations<String, Object, Object> streamOperations = this.redisTemplate.opsForStream();
streamOperations.acknowledge(STREAM_KEY, STREAM_QUEUE_GROUP, message.getId());
streamOperations.delete(STREAM_KEY, message.getId());
}
}
}
Redis相关数据初始化
服务启动前先创建消费组,否则启动服务时会出错
127.0.0.1:6379> XADD stream:queue * name Jobs
"1712046994392-0"
127.0.0.1:6379> XDEL stream:queue 1712046994392-0
(integer) 1
127.0.0.1:6379> XGROUP CREATE stream:queue stream:queue:group 0-0
OK
生产消息放入队列
redisTemplate.opsForStream().add(ObjectRecord.create("stream:queue", UUID.randomUUID().toString()));
-----------------------------------------------------------------------------------------------------------
薔薇猛虎皆成個性,陽光雨露俱是天恩!
薔薇猛虎皆成個性,陽光雨露俱是天恩!
浙公网安备 33010602011771号