基于spring boot+redis Stream 实现一个消息队列

消息队列添加消息和消费确认以及删除消息

    /**
     * 消息队列添加消息
     * @param message 队列存储消息
     * @param queueKey 队列
     */
    public void addMessageBlockingQueue(String message,String queueKey){
        Record<String,String> record = StreamRecords.objectBacked(message).withStreamKey(queueKey);
        redisTemplate.opsForStream().add(record);
    }

    /**
     * 消息队列消费确认
     * @param queueKey 消息队列key
     * @param group 分组名称
     * @param recordId 消息id
     * @return 成功或者失败
     */
    public boolean messageQueueConsumptionAck(String queueKey,String group,RecordId recordId){
        try {
            Long result = redisTemplate.opsForStream().acknowledge(queueKey, group, recordId);
            if (SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    /**
     * 消息队列删除消息
     * @param queueKey 消息队列key
     * @param recordId 消息id
     * @return
     */
    public boolean messageQueueConsumptionDelField(String queueKey, RecordId recordId){
        try {
            Long result = redisTemplate.opsForStream().delete(queueKey, recordId);
            if (SUCCESS.equals(result)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

创建消费者监听处理类:

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.stream.ObjectRecord;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.stereotype.Component;

@Slf4j
@Component()
public class RedisStreamListener implements StreamListener<String, ObjectRecord<String,String>> {

    @Override
    public void onMessage(ObjectRecord<String, String> message) {
        log.info(message.toString());
		// 消息消费ack确认
        redisService.messageQueueConsumptionAck("key", "group", "recordId");
		// 消费完成消息直接删除
		redisService.messageQueueConsumptionDelField("key", "recordId");
    }
}

创建消费者监听类的订阅配置:

import io.lettuce.core.RedisException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.redis.RedisSystemException;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.stream.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.stream.StreamListener;
import org.springframework.data.redis.stream.StreamMessageListenerContainer;
import org.springframework.data.redis.stream.Subscription;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

@Slf4j
@RequiredArgsConstructor
@Configuration
public class Config {

    private final StringRedisTemplate redisTemplate;

    private final StreamListener<String, ObjectRecord<String, String>> streamListener;

    @Bean
    public Subscription subscription(RedisConnectionFactory factory) {
        checkGroup();
        // 创建Stream消息监听容器配置
        StreamMessageListenerContainer.StreamMessageListenerContainerOptions<String, ObjectRecord<String, String>> options = StreamMessageListenerContainer
                .StreamMessageListenerContainerOptions
                .builder()
                // 设置阻塞时间
                .pollTimeout(Duration.ofSeconds(1))
                // 配置消息类型
                .targetType(String.class)
                .build();
        // 创建Stream消息监听容器
        StreamMessageListenerContainer<String, ObjectRecord<String, String>> listenerContainer = StreamMessageListenerContainer.create(factory, options);
        // 设置消费手动提交配置
        Subscription subscription = listenerContainer.receive(
                // 设置消费者分组和名称
                Consumer.from("group", "consumer-1"),
                // 设置订阅Stream的key和获取偏移量,以及消费处理类
                StreamOffset.create("key", ReadOffset.lastConsumed()),
                streamListener);
        // 监听容器启动
        listenerContainer.start();
        return subscription;
    }

    /**
     * 由于订阅需要先有stream,先做下检查
     */
    private void checkGroup() {
        // 创建需要校验的分组List
        List<String> consumers = new ArrayList<>();
        consumers.add("group");
        StreamInfo.XInfoGroups infoGroups = null;
        try {
            // 获取Stream的所有组信息
            infoGroups = redisTemplate.opsForStream().groups(BaseConstant.SEND_MESSAGE_QUEUE_KEY);
        } catch (RedisSystemException | RedisException | InvalidDataAccessApiUsageException ex) {
            log.error("group key not exist or commend error", ex);
        }

        // 遍历校验分组是否存在
        for (String consumer : consumers) {
            boolean consumerExist = false;
            if (Objects.nonNull(infoGroups)) {
                if (infoGroups.stream().anyMatch(t -> Objects.equals(consumer, t.groupName()))) {
                    consumerExist = true;
                }
            }
            // 创建不存在的分组
            if (!consumerExist) {
                redisTemplate.opsForStream().createGroup("key", consumer);
            }
        }

    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章