前言

Redis在5.0后增加了Stream功能,在日常的项目中Redis用到的比较多,但是Stream这个功能用的却是比较少,今天学习了一下Stream的基本使用功能,可以方便在接下来项目中遇到合适的再场景使用。

接口代码

public interface IStreamServer {

    /**
     *  添加消息
     * @param key
     * @param streamEntryID
     * @param content
     * @return
     */
    StreamEntryID xadd(String key, StreamEntryID streamEntryID, Map<String, String> content);


    /**
     * 创建分组
     * @param stream
     * @param group
     * @param makeStream
     * @return
     */
    String xgroupCreate(String stream, String group, Boolean makeStream);


    /**
     * 倒序获取历史消息
     * @param key
     * @param end
     * @param start
     * @param count
     * @return
     */
    List<StreamEntry> xrevrange(String key, StreamEntryID end, StreamEntryID start, int count);

    /**
     * 正序获取历史消息
     * @param key
     * @param start
     * @param end
     * @param count
     * @return
     */
    List<StreamEntry> xrange(String key, StreamEntryID start, StreamEntryID end, int count);

    /**
     * 按分组获取消息
     * @param group
     * @param consumer
     * @param count
     * @param streams
     * @return
     */
    List<Map.Entry<String, List<StreamEntry>>> xreadGroup(String group, String consumer, int count, Map.Entry<String, StreamEntryID>... streams);


    /**
     * 获取消息
     * @param count  获取数据
     * @param streams 起始消息ID
     * @return
     */
    List<Map.Entry<String, List<StreamEntry>>> xread(int count, Map.Entry<String, StreamEntryID>... streams);

}
  • 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.

实现类代码

public class StreamServiceImpl implements IStreamServer {
    @Resource
    private Pool<Jedis> jedisPool;
    
    @Override
    public StreamEntryID xadd(String key, StreamEntryID streamEntryID, Map<String, String> content) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xadd(key, streamEntryID, content);
        }
    }
    
    @Override
    public String xgroupCreate(String stream, String group, Boolean makeStream) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xgroupCreate(stream, group, null, makeStream);
        }
    }
    
    @Override
    public List<StreamEntry> xrevrange(String key, StreamEntryID end, StreamEntryID start, int count) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xrevrange(key, end, start, count);
        }
    }
    
    @Override
    public List<StreamEntry> xrange(String key, StreamEntryID start, StreamEntryID end, int count) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xrange(key, start, end, count);
        }
    }
    
    @Override
    public List<Map.Entry<String, List<StreamEntry>>> xreadGroup(String group, String consumer, int count, Map.Entry<String, StreamEntryID>... streams) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xreadGroup(group, consumer, count, 0, false, streams);
        }
    }
    
    @Override
    public List<Map.Entry<String, List<StreamEntry>>> xread(int count, Map.Entry<String, StreamEntryID>... streams) {
        try (Jedis jedis = jedisPool.getResource()) {
            return jedis.xread(count, 0, streams);
        }
    }
}
  • 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.

测试代码

public class RedisStreamTest extends BaseTest {


    @Resource
    private IStreamServer iStreamServer;

    @Test
    public void setGroup() throws Exception {
    	 //创建 流名为 video 分组为 group1
         String res = iStreamServer.xgroupCreate("video", "group1", true);
        //创建 流名为 audio分组为 group1
        String res2 = iStreamServer.xgroupCreate("audio", "group1", true);
    
    }


    @Test
    public void xadd() throws Exception {
        Long time = System.currentTimeMillis();
        //向audio中插入100条消息
        for (int i = 0; i < 100; i++) {
            StreamEntryID streamEntryID = new StreamEntryID(String.format("%s-%s", time, i));
            StreamEntryID id = iStreamServer.xadd("audio", streamEntryID, ImmutableMap.of("id", String.valueOf(i)));
            System.out.println(id);
        }

		//向video中插入100条消息
        for (int i = 0; i < 100; i++) {
            StreamEntryID streamEntryID = new StreamEntryID(String.format("%s-%s", time, i));
            StreamEntryID id = iStreamServer.xadd("video", streamEntryID, ImmutableMap.of("id", String.valueOf(i)));
            System.out.println(id);
        }
    }

    @Test
    public void xreadGroup() throws Exception {

        Map<String, StreamEntryID> t = MapUtil.of("video", null);
        Map<String, StreamEntryID> t2 = MapUtil.of("audio", null);
        
        Map.Entry<String, StreamEntryID> video = t.entrySet().stream().findFirst().get();
        Map.Entry<String, StreamEntryID> audio = t2.entrySet().stream().findFirst().get();

		//client1 用 group1 分组 从 video/audio 流中获取两个消息 ,同一分组中不同客户端可共享消费消息(client1消费一条,client2消息偏移量增加1 ),不同分组中的客户端消息不影响(client1消费一条,不影响client2消息偏移量)
        List<Map.Entry<String, List<StreamEntry>>> list = iStreamServer.xreadGroup("group1", "client1", 2, video, audio);
        list.forEach(x -> {
            System.out.println(x.getKey() + "-->" + x.getValue());
        });
        //输出
        /**
			video-->[1610607445337-2 {id=2}, 1610607445337-3 {id=3}]
			audio-->[1610608368683-2 {id=2}, 1610608368683-3 {id=3}]
		**/


    }

    @Test
    public void xrange() throws Exception {
    	//video中按正序 从1610607445337-10 到1610607445337-20 中获取3条消息
        List<StreamEntry> streamEntries = iStreamServer.xrange("video", new StreamEntryID("1610607445337-10"), new StreamEntryID("1610607445337-20"), 3);
        streamEntries.forEach(x -> System.out.println(x));
        //输出
        /**
			1610607445337-10 {id=10}
			1610607445337-11 {id=11}
			1610607445337-12 {id=12}
		**/
    }


    @Test
    public void xrevrange() throws Exception {
    	//video中按倒序 从1610607445337-80 到1610607445337-0 中获取3条消息
        List<StreamEntry> streamEntries = iStreamServer.xrevrange("video", new StreamEntryID("1610607445337-80"), new StreamEntryID("1610607445337-0"), 3);
        streamEntries.forEach(x -> System.out.println(x));
         //输出
        /**
			1610607445337-80 {id=80}
			1610607445337-79 {id=79}
			1610607445337-78 {id=78}
		**/
    }

    @Test
    public void xread() throws Exception {
        //从1610607445337-2 开始获取
        Map<String, StreamEntryID> t = MapUtil.of("video", new StreamEntryID("1610607445337-2"));
        Map<String, StreamEntryID> t2 = MapUtil.of("audio", null);
        Map.Entry<String, StreamEntryID> video = t.entrySet().stream().findFirst().get();
        Map.Entry<String, StreamEntryID> audio = t2.entrySet().stream().findFirst().get();
		// 从video 1610607445337-2 开始,从audio 0开始获取 2条消息
        List<Map.Entry<String, List<StreamEntry>>> list = iStreamServer.xread(2, video, audio);
        list.forEach(x -> {
            System.out.println(x.getKey() + "-->" + x.getValue());
        });
         //输出
        /**
			audio-->[1610608368683-0 {id=0}, 1610608368683-1 {id=1}]
			video-->[1610607445337-3 {id=3}, 1610607445337-4 {id=4}]
		**/
    }
     
}
  • 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.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
  • 103.
  • 104.

总结

1.Redis Stream 消息支持持久化,可随意获取历史消息记录
2.Redis Stream 支持客户端分组功能,同一分组中的客户端可共同消费流中的消息