《Redis应用实例》Java实现(14):消息队列

7 阅读1分钟

消息队列使用广泛,除了专门的消息队列组件,比如RocketMQ ,在新的redis中可以使用stream实现消息队列,支持消费者组。


package com.foxbill.redisinaction;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.StreamEntryID;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadGroupParams;
import redis.clients.jedis.resps.StreamEntry;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 消息队列,使用stream实现
 */
public class Chapter14 {
    static String streamKey = "chapter14:mystream";
    static String groupName = "mygroup";

    static public void start(Jedis jedis) {
        clean(jedis);
        createConsumeGroup(jedis);
        produceMessages(jedis);
        consumeReadMessage(jedis);
    }

    //清理数据,便于测试,实际场景不需要
    private static void clean(Jedis jedis) {
        jedis.del(streamKey);
    }
    /**
     * 创建消费组
     */
    private static void createConsumeGroup(Jedis jedis) {
        // 创建消费者组,从最新开始读
        try {
            jedis.xgroupCreate(streamKey, groupName, StreamEntryID.LAST_ENTRY, true); // true = 如果组已存在也不报错
            System.out.println("消费者组 '" + groupName + "' 创建成功");
            //创建完后,通过 XINFO GROUPS chapter14:mystream 可以查看消费组是否成功创建
        } catch (Exception e) {
            System.err.println("创建组失败:" + e.getMessage());
        }
    }

    /**
     * 生产者:向 Stream 添加消息
     */
    private static void produceMessages(Jedis jedis) {
        XAddParams params = new XAddParams();
        //long maxStreamLength = 3;
        //params.maxLen(maxStreamLength);
        for (int i = 1; i <= 5; i++) {
            Map<String, String> msg = new HashMap<>();
            msg.put("id", String.valueOf(i));
            msg.put("data", "message-" + i);
            // 调用xadd方法添加消息
            StreamEntryID xaddResult = jedis.xadd(streamKey, params, msg);
            System.out.println("已添加消息,ID: " + xaddResult +
                    " (时间戳: " + xaddResult.getTime() +
                    ", 序列号: " + xaddResult.getSequence() + "), 内容: " + msg);
        }
    }

    /*消费者读取消息*/
    private static void  consumeReadMessage(Jedis jedis) {
        // 1. 准备xreadGroup参数
        String consumerName = "consumer-1";
        XReadGroupParams params = XReadGroupParams.xReadGroupParams()
                .count(1)  // 每次读取1条消息
                .block(0); // 0表示无限阻塞,单位毫秒

        // 2. 准备streams参数:UNRECEIVED_ENTRY:获取当前消费者组中从未被任何消费者领取过的新消息
        Map<String, StreamEntryID> streams = new HashMap<>();
        streams.put(streamKey, StreamEntryID.UNRECEIVED_ENTRY);
        // 3. 读取消息
        while (true) {
            List<Map.Entry<String, List<StreamEntry>>> entries = jedis.xreadGroup(groupName, consumerName, params, streams);
            // 4. 处理读取到的消息
            if (entries != null && !entries.isEmpty()) {
                for (Map.Entry<String, List<StreamEntry>> streamEntry : entries) {
                    String streamName = streamEntry.getKey();
                    List<StreamEntry> messages = streamEntry.getValue();
                    System.out.println("从 stream " + streamName + " 读取到 " + messages.size() + " 条消息:");
                    for (StreamEntry msg : messages) {
                        System.out.println("  ID: " + msg.getID() + ", 内容: " + msg.getFields());
                        // 确认消息已处理
                        jedis.xack(streamKey, groupName, msg.getID());
                        System.out.println("  已确认消息: " + msg.getID());
                    }
                }
            } else {
                System.out.println("没有读取到新消息");
            }
        }
    }
}