我的日常

登录/注册
您现在的位置:论坛 资料库 开源社区 > 基于Redis实现分布式消息队列(2)
总共48087条微博

动态微博

查看: 1876|回复: 0

基于Redis实现分布式消息队列(2)

[复制链接]
admin    

1244

主题

544

听众

1万

金钱

管理员

  • TA的每日心情

    2021-2-2 11:21
  • 签到天数: 36 天

    [LV.5]常住居民I

    管理员

    跳转到指定楼层
    楼主
    发表于 2016-02-05 13:40:31 |只看该作者 |正序浏览
    1、消息队列需提供哪些功能?
    在功能设计上,我崇尚奥卡姆剃刀法则。
    对于消息队列,只需要两个方法: 生产 和 消费。
    具体的业务场景是任务队列,代码设计如下:


    public abstract class TaskQueue{
        private final String name ;
        public String getName(){return this.name;}


        public abstract void addTask(Serializable taskId);
        public abstract Serializable popTask();
    }
    同时支持多个队列,每个队列都应该有个名字。final确保TaskQueue是线程安全的。TaskQueue的实现类也应该确保线程安全。


    addTask向队列中添加一个任务。队列中仅保存任务的id,不存储任务的业务数据。


    popTask从队列中取出一个任务来执行。
    这种设计不是特别友好,因为她需要调用者自行保证任务执行成功,如果执行失败,自行确保重新把任务放回队列。 无论如何,这种机制是可以工作的。想想奥卡姆剃刀法则,我们先按照这个设计实现出来看看。
    如果调用者把业务数据存在数据库中,业务数据中包含“状态“列,标识任务是否被执行,调用者需要自省管理这个状态,并控制事务。


    2、后续可能提供的功能
    2.1、引入Task生命周期概念
    应用场景不同,需求也不同。
    在严格的应用场景中,需要确保每个Task执行“成功“了。
    对于上面提到的popTask后不管的“模式“,这是另外一种“运行模式“,两种模式可以并行存在。


    在这种新模式下,Task状态有3种:新创建(new,刚调用addTask加到队列中)、正在执行(in-process,调用popTask后,调用finish前)、完成(done,执行OK了,调用finishTask后)。
    调整后的代码如下:


    public abstract class TaskQueue{


        private final String name ;
        public String getName(){return this.name;}


        public abstract int getMode();


        public abstract void addTask(Serializable taskId);
        public abstract Serializable popTask();
        public abstract void finishTask(Serializable taskId);
    }
    2.2、增加批量取出任务的功能
    popTask()一次取出一个任务,太磨叽了。


    public abstract class TaskQueue{
        ... ...
        public abstract Serializable[] popTasks();
    }
    2.3、增加阻塞等待机制
    想象一种场景:
    小明同学,取出一个任务,发现干不了,放回队列,再去取,取出来发现还是干不了,又放回去。反反复复。
    小明童鞋肿么了?可能是他干活需要网络,网络断了。可能是他做任务需要写磁盘,磁盘满了。


    如果小明像邻居家的孩子一样优秀,当他发现哪里不对的时候,他应该冷静下来,歇会儿。
    但他不是啊!


    只有我们能帮他了。
    假如队列中有10000个待办任务。
    这时候小明来了。他失败100次后,我们应该拦他吗?不应该,除非他主动要求(在系统参数中配置)。5000次后呢?也不应该,除非他主动要求。我们的原则是:我们做的所有事情,对于调用者,都是可以预期的。


    我们可以在系统参数中要求调用者设置一个阀值N,如果不设置,默认为100。连续失败N次后,让调用者睡一会儿,睡多长时间,让调用者配置。


    假如我们的底层实现中包含待办子队列、重做子队列和完成子队列(这种设计好复杂!pop的时候先pop重做,还是先pop待办,复杂死了!但愿不需要这样)。
    待办子队列中有10000个任务。


    在小明失败10000次后,所有的任务都在重做子队列了。这时候我们应该拦他吗?
    重做子队列要不要设置大小,超过之后,让下一个访问者等。
    等的话就会涉及超时,超时后,任务也不能丢弃。
    太复杂 了!设置一个连续失败次数的限制就够了!


    2.4、考虑增加Task类
    不保存任务的相关数据是基本原则,绝对不动摇。
    增加Task类可以管理下生命周期,更有用的是,可以把Task本身设计成Listener,代码大概时这样的:


    public abstract class Task{


        public Serializable getId();
        public int getState();


        pubic void doTask();


        public void whenAdded(final TaskQueue tq);
        public void whenPoped(final TaskQueue tq);
        // public void whenFaild(final TaskQueue tq);
        public void whenFinished(final TaskQueue tq);
    }
    通过Task接口,我们可以对调用过程进行更强势的管理(如进行事务控制),对调用者施加更强的控制,用户也可以获得更多的交互机会,同TaskQueue有更好的交互(如在whenFinished中做持久化工作)。


    但这些真的有必要吗?是不是太侵入了?注解的方式会好些吗?
    再考虑吧。

    科帮网 1、本主题所有言论和图片纯属会员个人意见,与本社区立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与科帮网享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和科帮网的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、科帮网管理员和版主有权不事先通知发贴者而删除本文


    JAVA爱好者①群:JAVA爱好者① JAVA爱好者②群:JAVA爱好者② JAVA爱好者③ : JAVA爱好者③

    快速回复
    您需要登录后才可以回帖 登录 | 立即注册

       

    关闭

    站长推荐上一条 /1 下一条

    发布主题 快速回复 返回列表 联系我们 官方QQ群 科帮网手机客户端
    快速回复 返回顶部 返回列表