作者:RobanLee

原创文章,转载请注明: 萝卜李 http://www.robanlee.com

源码在这里: https://github.com/robanlee123/RobCron

时间有限,就不详细注释,有问题或者意见欢迎@我,也欢迎大家批评指正.

本文所必须的一些资料如下:

1. NODEJS ==> 可以去NODEJS.ORG下载最新的源码.
2. Redis ==> Redis.io
3. KUE ==> Nodejs的一个开源队列系统
4. NODE-SCHEDULE ==> NODEJS 一个开源调度系统

废话不多说,先来介绍任务的流程:

1. NODEJS或者PHP或者其他语言 写入REDIS 一个计划任务, 比如每分钟做某件事,这里就用SAYHELLO来代替好了
2. 使用NODEJS读取这个任务,并将它转换为NODE的调度任务(node-schedule 来完成)
3. 调度器[node-schedule]根据设定的规则来分发任务.
4. KUE接受任务,并且加入列队,执行.
5. DONE

STEP1: 创建一个任务

/**
 * Add task
 * @author Robanlee@gmail.com
 */ //加载函数,集中加载一些LIB,这个源码请参照最后的附属文件
var loader = require('./loader');   function addTask(opts){
        new loader(this); 
        
        //默认设置
        this.opts = {
                keyIDs:'schedule:job:ids',
                keyLists:'schedule:job:list',
                keyJob:'schedule:job:'
        }
        
        //合并配置,类似JQUERY: extend
        this.mergeParams(opts);  
}; //Merge options
addTask.prototype.mergeParams = function ( param ){
        if(undefined === this.opts ) {
                return false;
        }
        
        for(var x in param) { 
                if(param[x] != undefined && '' != param[x]) {
                        this.opts[x] = param[x];
                }
        }
}; //添加数据方法
addTask.prototype.pushData = function ( data ){ 
        if(undefined == data ) {
                console.log('--ERROR:data is null');
                return false;
        }
        this.getIncr.call(this,function(response,obj){
                var id = response;
                obj.redisClient.rpush(obj.opts.keyLists,id,function(err,response){
                        if(err) throw err;
                });
         
                data.id = id;
                var m = obj.redisClient.multi();
                for(var x in data) {
                        m.hset( obj.opts.keyJob+id,x,data[x] );
                }
                
                m.exec(function(err,response){
                        if(err) throw err; 
                        console.log('[info] Task: ['+data.name+'] has been set successful!');
                });   
               
        }); 
}; //获取REDIS目前的自增ID
addTask.prototype.getIncr = function (callBack){
        var obj = this; 
        this.redisClient.incr(this.opts.keyIDs,function(err,response){
                console.log("[info] Current id is : " + response);
                callBack(response, obj);
        });
};

加载这个lib 写入一个DEMO:

var data = { 
        'name':'taskDemo',
        'created':Date.now(),
        'state':1,
        'type':'untitled',
        'rule':'*/1 * * * *'  //这个任务规则可以为CRONTAB的规则,这个表示每分钟执行一次
}; var job = new addTask();
job.pushData(data);

执行这个脚本,如果一切正常,你会看到如下信息:

NODEJS 输出:

REDIS:

接下来就是获取数据,并且转换为调度任务了,

源码:

var loader = require('./loader');
var taskLog = require("./TaskLog"); function scheduleTask(){
        new loader(this); 
        this.opts = {
                keyIDs:'schedule:job:ids',
                keyLists:'schedule:job:list',
                keyJob:'schedule:job:'
        }
        
        this.task = {
                taskDemo:undefined
        };
        
        //监听取消任务操作
        this.listenCancel();
}; scheduleTask.prototype.setScheduleTask = function (data,obj){ 
        this.task[data.name] =  this.libs['node-schedule'].scheduleJob(data.rule,function(){
                obj.setQueue(data);
                console.log('[info] Task :' + data.name + ' has been set in queue!');
        });
         
}; scheduleTask.prototype.setQueue = function (datas){
        
        var jobs = this.libs.kue.createQueue(); 
        jobs.create(datas.name,{
                'name:':datas.name,
                'state':1
        }).save();
         
        console.log("[info] Task ["+datas.name+"] has been queued!");
        
        this.setLog(datas);
}; scheduleTask.prototype.setLog = function (responseData){
        var logData = {
                jobid:responseData.id,
                name:responseData.name,
                result:1
        };
                                        
        new taskLog(logData);
        console.log("[info] Task has been loged");
}; scheduleTask.prototype.getJob = function (){
        this.getJobIndex.call(this,function(response,obj){
                for(var x in response ) {
                        obj.redisClient.hgetall(obj.opts.keyJob+response[x],function(err,data){
                                console.log("[info] Task:["+data.name+"] has been loaded!");
                                obj.setScheduleTask(data, obj);
                        });
                }
        });
}; scheduleTask.prototype.getJobIndex = function(callBack){
        //Read tasks from <list schedule:job:list>
        var o = this;
        this.redisClient.lrange(this.opts.keyLists,0,-1,function(err,response){
                if(err) throw err;
                callBack(response, o);
        });
}; scheduleTask.prototype.listenCancel = function (){
        var job = this.libs.kue.createQueue();
        var that = this;         job.process('cancelJob',function(data,done){  
                that.task[data.data.data].cancel();
                console.log('[info] Task: '+data.data.data + ' has been canceled') ;
                done();
        });
}

执行代码:

var x = new scheduleTask();
x.getJob();

等待一分钟后,NODEJS控制台会输出(这个任务在没有取消的情况下,每分钟都会执行):

第二分钟:

REDIS 现在的数据:

这个数据中增加了KUE的一些任务, q:job:[]:inactive 这个就标识任务还未被执行,执行后的任务状态有

complete active failed delay 四种

至此,就只剩下执行任务的步骤了

var loader = require('./loader');

function execTask(){
        new loader(this); 
        
        var job = this.libs.kue.createQueue();
        job.process('taskDemo',function(data,done){
                console.log('[info] Task:'+data.type+'#'+data.id+' has been executed successful!');
                
                
                //DONE之前可以做你想要做的事情
                
                done(); //千万别忘记调用此方法
        });
        
        
} //添加一个取消定时任务的KUE任务
execTask.prototype.addCancelJob = function (){
        var job =this.libs.kue.createQueue();
        job.create('cancelJob', {data:'taskDemo'}).save();
        console.log('[info] Task: cancelJob has been send!');
}

执行这个脚本:

var et = new execTask();

//取消定时任务
et.addCancelJob();

执行后会有2个结果

1. 程序会执行当前列队里的任务.

2. 定时任务会被取消,下一分钟后任务不会再由SCHEDULE分配

任务执行结果:

取消任务的回应:

注意最后一行…

使用NODEJS+REDIS开发一个消息队列以及定时任务处理的更多相关文章

  1. 如何使用NODEJS+REDIS开发一个消息队列

    作者: RobanLee 原创文章,转载请注明: 萝卜李 http://www.robanlee.com MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应 ...

  2. 什么鬼,面试官竟然让我用Redis实现一个消息队列!!?

    GitHub 9.4k Star 的Java工程师成神之路 ,不来了解一下吗? GitHub 9.4k Star 的Java工程师成神之路 ,真的不来了解一下吗? GitHub 9.4k Star 的 ...

  3. Redis 学习笔记(六)Redis 如何实现消息队列

    一.消息队列 消息队列(Messeage Queue,MQ)是在分布式系统架构中常用的一种中间件技术,从字面表述看,是一个存储消息的队列,所以它一般用于给 MQ 中间的两个组件提供通信服务. 1.1 ...

  4. Redis+php-resque实现消息队列

      服务器硬件配置 Dell PowerEdge R310英特尔单路机架式服务器 Intel Xeon Processor X3430 2.4GHz, 8MB Cache 8GB内存(2 x 4GB) ...

  5. Redis实现简单消息队列

    http://www.jianshu.com/p/9c04890615ba 任务异步化 打开浏览器,输入地址,按下回车,打开了页面.于是一个HTTP请求(request)就由客户端发送到服务器,服务器 ...

  6. Delayer 基于 Redis 的延迟消息队列中间件

    Delayer 基于 Redis 的延迟消息队列中间件,采用 Golang 开发,支持 PHP.Golang 等多种语言客户端. 参考 有赞延迟队列设计 中的部分设计,优化后实现. 项目链接:http ...

  7. Spring Cloud(7):事件驱动(Stream)分布式缓存(Redis)及消息队列(Kafka)

    分布式缓存(Redis)及消息队列(Kafka) 设想一种情况,服务A频繁的调用服务B的数据,但是服务B的数据更新的并不频繁. 实际上,这种情况并不少见,大多数情况,用户的操作更多的是查询.如果我们缓 ...

  8. 进阶高阶IoT架构-教你如何简单实现一个消息队列

    前言 消息队列是软件系统领域用来实现系统间通信最广泛的中间件.基于消息队列的方式是指由应用中的某个系统负责发送消息,由关心这条消息的相关系统负责接收消息,并在收到消息后进行各自系统内的业务处理.消息可 ...

  9. skynet源代码学习 - 从全局队列中弹出/压入一个消息队列过程

    学习云风的skynet源代码,简单记录下. void skynet_globalmq_push(struct message_queue * queue) { struct global_queue ...

随机推荐

  1. Java IO4:字符流进阶及BufferedWriter、BufferedReader

    字符流和字节流的区别 拿一下上一篇文章的例子: 1 public static void main(String[] args) throws Exception 2 { 3 File file = ...

  2. Ubuntu14.04上安装tftpd服务

    首先sudo apt-get install tftpd-hpa,  tftp-hpa 然后sudo vim /etc/default/tftpd-hpa 配置文件如下 TFTP_USERNAME=& ...

  3. poj 2112 floyd+Dinic最大流+二分最小值

    题目大意是: K台挤奶机器,C头牛,K不超过30,C不超过200,每台挤奶机器最多可以为M台牛工作,给出这些牛和机器之间,牛和牛之间,机器与机器之间的距离,在保证让最多的牛都有机器挤奶的情况下,给出其 ...

  4. 解决Eclipse编译器报错ClassNotFoundException:Org.hibernate.hql.ast.HqlToken

    最近开发遇到Eclipse编译器老是报出ClassNotFoundException:Org.hibernate.hql.ast.HqlToken [from User Where id=1] 的错误 ...

  5. Lucene学习总结之七:Lucene搜索过程解析

    一.Lucene搜索过程总论 搜索的过程总的来说就是将词典及倒排表信息从索引中读出来,根据用户输入的查询语句合并倒排表,得到结果文档集并对文档进行打分的过程. 其可用如下图示: 总共包括以下几个过程: ...

  6. SqlServer排序(null值,和非空值排列顺序)

    项目中遇到一个问题,需要设置序号排序,而该字段中的默认值为空,使用普通排序,空值就会在最前边.可以使用如下语句:   其中 col 为 排序的字段名称. then 0 else 1 代表先空值,后数字 ...

  7. CSS3 @font-face 指定英文网页字体

      @font-face是CSS3中的一个模块,他主要是把自己定义的Web字体嵌入到你的网页中,随着@font-face模块的出现,我们在Web的开发中使用字体不怕只能使用Web安全字体.可能有人要问 ...

  8. Anniversary party(POJ 2342 树形DP)

    Anniversary party Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 5767   Accepted: 3335 ...

  9. springMVC+ freemark多视图配置

    <!--通用视图解析器--> <bean id="viewResolverCommon" class="org.springframework.web. ...

  10. 就是爱Java

    就是爱Java,提供了Java代码示例,文章和教程,可以帮助你学习Java编程语言. 网站名称:就是爱Java 网站地址:http://java.openyu.org