1。使用场景在很多系统中,尤其是电子商务系统中,经常需要执行延时任务。例如,订单待付款需要在30分钟后自动关闭。虽然实现的方式有很多,比如Job等,这里主要介绍Redis的一个新特性keyspacenotifications的使用。2.基础知识要点!!!Redis版本2.8.0支持键空间通知。如果你的Redis版本太低,可以洗洗睡了。。。如果你还不知道Redis的Pub/Sub,强烈推荐你先看这篇文章:Redis发布与订阅接下来说说我们的主角:keyspacenotificationskeyspace通知默认关闭。启用需要修改redis.conf文件或通过CONFIGSET启用或禁用该功能。这里我们使用CONFIGSET开启:$redis-cliconfigsetnotify-keyspace-eventsEx这里有人会问,Ex是什么意思?这是notify-keyspace-events的参数。完整的参数列表见下表:字符发送的通知K键空间通知,所有通知都以keyspace@为前缀E键事件通知,所有通知以keyevent@为前缀gDEL,EXPIRE,RENAME等与类型无关的通用命令通知$string命令lnotificationsset命令hnotificationhash命令zsortedset命令xexpirationevent:每当删除过期键时发送evict事件:发送A参数的别名g$lshzxe每当由于最大内存策略而删除密钥时。可以看出,我们只启用了按键事件通知和过期事件。因为我们只需要这两个来实现延时任务。话不多说,直接看代码。3.实现方案延迟任务应该具备哪些属性?我觉得至少有以下几个属性:任务类型。(示例:关闭订单)任务ID。(示例:订单ID)任务延迟时间。(示例:30分钟)任务额外数据。(例如:订单的其他相关数据)确定好后,我们就可以继续往下了。3.1注册事件处理程序首先,项目启动后,我们需要根据不同的事件注册不同的处理程序:const_=require('lodash')//taskhandlermapconsthandlers={}//eventtypemapconstevents={}constregisterEventHandler=(type,handler)=>{if(!type){thrownewError('typecannotbeempty')}if(!_.isFunction(handler)){thrownewError('handlertypeisnotfunction')}handlers[type]=handlerevents[type]=true}3.2创建延迟任务constredis=require('redis')constclient=redis.createClient()consteventKeyPrefix='custom_event_'//任务列表constjobs={}constaddDelayEvent=(type,id,body={},delay=10*60)=>{constkey=`${eventKeyPrefix}${type}_${id}`constjobKey=`${type}_${id}`client.setex(key,delay,'delayevent',(err)=>{if(err){returnconsole.log('添加延迟事件失败:',err);}console.log('Adddelayedeventsuccessfully');jobs[jobKey]=body})}这里的重点是client.setex(key,expired,value)方法,我们需要给key加上一个过期时间,那么当keyexpires后,redis会发出过期事件。3.3订阅过期事件实现了前两步后,我们就已经可以将带有过期时间的key写入redis中了。下一个关键是订阅过期事件并处理它。constredis=require('redis')constsub=redis.createClient()sub.on('pmessage',(pattern,channel,message)=>{//匹配键constkeyMatcher=newRegExp(`^${eventKeyPrefix}(${_.keys(events).join('|')})_(\\S+)$`)constresult=message.match(keyMatcher)if(result){consttype=result[1];constid=result[2];consthandler=handlers[type]console.log('订阅消息:type=%s,id=%s',type,id);if(_.isFunction(handler)){constjobKey=`${type}_${id}`if(jobs[jobKey]){handler(id,jobs[jobKey])}else{console.log('未找到延迟事件,type=%s,id=%s',type,id);}}else{console.log('Noeventhandlerfound.type=%s',type)}}})//订阅频道sub.psubscribe('__key*__:expired')3.4写一个Demo最后我们写一个Demo来验证我们的功能。consteventManager=require('./utils/eventManager')eventManager.registerEventHandler('closeorder',(id,body)=>{console.log('关闭预订单id=%s,body=%o',id,body);})eventManager.addDelayEvent('closeorder',1111,{name:'test'},5)完成!