栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

使用Jedis监听Redis Stream 实现消息队列功能

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

使用Jedis监听Redis Stream 实现消息队列功能

简介

之前使用SpringBoot去监听Redis Stream实现了消息队列的功能,本次分享的是使用Jedis来实现同样的功能,而且还可以继续拓展功能,因为Jedis我觉得还是比之前那种方式要灵活。本次实现的监听可以使用多线程去监听。

之前通过SpringBoot实现文章链接:
SpringBoot 中使用Redis Stream 实现消息监听

视频演示

使用Jedis自己实现监听Redis Stream的功能达到消息队列的效果Demo

实现原理

这次实现监听我分为了通过群组和消费者监听和模式使用xread的原生监听,它们的区别就是如果使用的是通过群组和消费者这样的监听可以确保消息只会被同一个消费者消费一次,不会对消息进行重复消费,适合要求数据唯一性的场景,比如入库或者其他的操作。默认的xread实现方式监听有几个线程那么这几个线程会同时收到相同的插入的消息,可以理解为广播的方式去接受消息。

这里主要基于的是Redis Stream中的一下几个命令对应Jedis的方法:

  • xadd:创建群组
  • xread:读取数据
  • xgroup: 创建群组
  • xreadgroup:读取群组消息

读取时主要用到了它们的block属性,将block属性设置为0则表示一直阻塞直到收到新的消息,然后我将这一步骤放到一个轮询中,实现了阻塞收到消息后进入到下一次阻塞,从而实现了监听到效果。

实现代码

本次demo代码很简单,都放到了一个类中,并且只要有redis就可以在修改代码中的配置后直接进行运行,不需要手动创建stream或者群组等操作。

pom.xml文件


    4.0.0

    vip.huhailong
    JRedisMQ
    1.0-SNAPSHOT

    
        8
        8
        4.2.3
    

    
    
        
            redis.clients
            jedis
            ${jedis.version}
        
        
        
            org.slf4j
            slf4j-api
            1.7.36
        
        
        
            ch.qos.logback
            logback-classic
            1.2.11
        
        
            com.alibaba
            fastjson
            2.0.7
        
    


实现代码
package jredismq.test;

import com.alibaba.fastjson.JSONObject;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.StreamEntryID;
import redis.clients.jedis.params.XAddParams;
import redis.clients.jedis.params.XReadGroupParams;
import redis.clients.jedis.params.XReadParams;
import redis.clients.jedis.resps.StreamEntry;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;


public class JedisStreamMQTest {

    private static final Logger logger = LoggerFactory.getLogger(JedisStreamMQTest.class);

    public static void main(String[] args) {
		//以下内容根据自己的情况进行修改
        String host = "192.168.1.110";
        int port = 6379;
        int timeout = 1000;
        String password = "huhailong";
        int database = 0;

        String streamKeyName = "streamtest";
        String groupName = "testgroup";

        String[]consumerNames = {"huhailong", "xiaohu"};

        String listenerType = "DEFAULT";  //GROUP or DEFAULT

        //创建 redis 连接池实例
        JedisPool pool = new JedisPool(new GenericObjectPoolConfig<>(),host,port,timeout,password,database);

        JedisStreamMQTest test = new JedisStreamMQTest();
        test.createGroup(pool,streamKeyName,groupName); //创建群组

        if("GROUP".equals(listenerType)){
            test.listenerByGroup(pool,streamKeyName,groupName,consumerNames);   //使用群组和消费者监听
        }else{
            test.listenerDefault(pool,streamKeyName);
        }

        new Thread(()->{    //线程3:用于写入stream数据
            Jedis jedis = pool.getResource();
            while(true) {
                try {
                    Thread.sleep(500L);
                    Map map = new HashMap<>();
                    map.put("currentTime", LocalDateTime.now().toString());
                    jedis.xadd(streamKeyName,map, XAddParams.xAddParams());
                } catch (Exception e) {
                    logger.error(e.getMessage());
                }
            }
        }).start();
    }

    
    private void listenerByGroup(JedisPool pool, String keyName, String groupName, String...consumerNames){
        Map entryIDMap = new HashMap<>();
        entryIDMap.put(keyName,StreamEntryID.UNRECEIVED_ENTRY);
        //以下为了演示简单就不用线程池了,直接创建两个线程说明问题
        IntStream.range(0,2).forEach(i->{
            Jedis jedis = pool.getResource();   //创建jedis实例
            new Thread(()->{
                while(true){
                    try{
                        Thread.sleep(500L);
                        //下面的 xreadGroup 方法等同与redis中的xreadgroup命令,将block阻塞时间设置为0表示一直阻塞知道收到消息,然后上面StreamEntryID设置为接收最新值
                        List>> entries = jedis.xreadGroup(groupName, consumerNames[i], XReadGroupParams.xReadGroupParams().block(0), entryIDMap);
                        logger.info("Thread:-{},result:{}",Thread.currentThread().getName(), JSONObject.toJSONString(entries.get(0).getValue().get(0).getFields()));
//                        jedis.xack(keyName,groupName,entries.get(0).getValue().get(0).getID()); //确认消息
                        jedis.xdel(keyName,entries.get(0).getValue().get(0).getID());   //删除消息
                    } catch (Exception e){
                        logger.error(e.getMessage());
                    }
                }
            }).start();
        });

    }

    
    private void listenerDefault(JedisPool pool, String keyName){
        Map entryIDMap = new HashMap<>();
        entryIDMap.put(keyName,StreamEntryID.LAST_ENTRY);
        //以下为了演示简单就不用线程池了,直接创建两个线程说明问题
        IntStream.range(0,2).forEach(i->{
            new Thread(()->{
                Jedis jedis = pool.getResource();   //创建jedis实例
                while(true){
                    try{
                        Thread.sleep(500L);
                        List>> entries = jedis.xread(XReadParams.xReadParams().block(0), entryIDMap);
                        logger.info("Thread:-{},result:{}",Thread.currentThread().getName(), JSONObject.toJSONString(entries.get(0).getValue().get(0).getFields()));
                        jedis.xdel(keyName,entries.get(0).getValue().get(0).getID());
                    } catch (Exception e){
                        logger.error(e.getMessage());
                    }
                }
            }).start();
        });
    }

    private void createGroup(JedisPool pool, String keyName, String groupName){
        Jedis jedis = pool.getResource();
        try{
            //StreamEntryID 表示创建群组并接收新的消息,这里可以根据自己需要设置,0表示读取所有历史消息,后面的boolean值表示如果stream不存在是否创建stream
            jedis.xgroupCreate(keyName,groupName,StreamEntryID.LAST_ENTRY,true);
        } catch (Exception e){
            //这里捕获异常的原因是可能创建时群组已经存在
            logger.error(e.getMessage());
        }
    }

}

本demo的代码还是比较简单的,大家可以根据自己的需求修改和封装。我也在继续探索使用这种方式封装和完善成一个完整的项目,不依赖于第三方框架例如Spring的项目,这样就可以灵活的使用它了,觉得有用的小伙伴要点赞哟!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/992190.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号