澳门美高梅手机网站下精炼的环形延时队列处理秒级定时任务之缓解方案

 业务背景

当稍微复杂点业务体系受,不可避免会遇到做定时任务的要求,比如淘宝的市超时自动关闭订单、超时自动确认收货等等。对于有些定时作业于多之系,通常都见面搭建专门的调度平台来管理,通过创办定时器来周期性执行任务。如刚所说之观,我们得以叫订单创建一个特别的天职来拍卖贸易状态,每秒轮询一赖订单表明,找有那些符合超时条件的订单然后标记状态。这是最好简便易行粗暴的做法,但强烈也格外low,自己还生非失去手写这样的代码,所有必须使摸个更好的方案。

回到真正项目面临的场景,系统面临有活动上线后而受目标用户发送短信通知,这些通知需要遵循日点批量发送。虽然曾经因quartz.net给系统搭建了任务调度平台,但实在无思就此上述方案来实现。在网上各种搜索与琢磨,找到同样篇文章为我眼前一亮,稍加分析发现其间的笔触完全符合现在底景,于是决定在祥和种蒙实现出来。

 

而且交了周末,这几乎上气温下降,真是个出来的好时,来到上海吧起一段时间了,是时去外面浪一环抱了。

规律分析

 这种方案的为主就是结构一种植多少结构,称之为环形队列,但事实上要一个屡屡组,加上对它的循环遍历,达到同等种环状的假象。然后还配合定时器,就得实现准需延时的职能。上面提到的文章被吗介绍了落实思路,这里我下自的了解还进一步详细的解释一下。

咱们先行为这数组分配一个一定大小的半空中,比如60,每个数组的素用来存放在任务之汇。然后打开一个定时器每隔一秒来围观是数组,扫了一环抱刚好是同样分钟。如果提前设置好职责让围观的圈数(CycleNum)和于数组吃之位置(Slot),在刚好扫到数组的Slot位置时,集合里那些CycleNum为0的职责就是是达到触发条件的天职,拉出来开作业操作然后换除掉,其他的把圈数减掉一蹩脚,然后养到下次前仆后继扫描,这样就兑现了延时底效益。原理如下图所示:

澳门美高梅手机网站 1

可以看出中间的显要是计量产生每个任务所于的职与要循环的圈数。假设当前时吧15:20:08,当前围观位置是2,我之天职要在15:22:35夫随时点,也即是147秒后。那么自己欲循环的圈数就是147/60=2环绕,需要吃围观的职务就是是(147+2)%60=29的地方。计算好任务之坐标后塞到数组中属于它们的职,然后静静等候被消费就哼啊。

 

出门,外面的天气的确不易啊,总感觉到像许多年没见了太阳了。

撸码实现

仅说原理不达到代码怎么能行呢,根据上面的笔触,下面一步步当.net平台下促成出来。

事先开片基础封装。

首先构造任务参数的基类,用来记录任务之位置信息及定义业务回调方法:

    public class DelayQueueParam
    {
        internal int Slot { get; set; }

        internal int CycleNum { get; set; }

        public Action<object> Callback { get; set; }
    }

连通下是中心地方。再组织队列的泛型类,真实类型必须派生自端的基类,用来扩张部分事务字段方便消费时用。队列的重要性能有眼前岗位指针以及数组容器,主要的操作发生插入、移除和花。插入任务时需传入执行时,用来算这职责的坐标。

    public class DelayQueue<T> where T : DelayQueueParam
    {
        private List<T>[] queue;

        private int currentIndex = 1;

        public DelayQueue(int length)
        {
            queue = new List<T>[length];
        }

        public void Insert(T item, DateTime time)
        {
            //根据消费时间计算消息应该放入的位置
            var second = (int)(time - DateTime.Now).TotalSeconds;
            item.CycleNum = second / queue.Length;
            item.Slot = (second + currentIndex) % queue.Length;
            //加入到延时队列中
            if (queue[item.Slot] == null)
            {
                queue[item.Slot] = new List<T>();
            }
            queue[item.Slot].Add(item);
        }

        public void Remove(T item)
        {
            if (queue[item.Slot] != null)
            {
                queue[item.Slot].Remove(item);
            }
        }

        public void Read()
        {
            if (queue.Length >= currentIndex)
            {
                var list = queue[currentIndex - 1];
                if (list != null)
                {
                    List<T> target = new List<T>();
                    foreach (var item in list)
                    {
                        if (item.CycleNum == 0)
                        {
                            //在本轮命中,用单独线程去执行业务操作
                            Task.Run(()=> { item.Callback(item); });
                            target.Add(item);
                        }
                        else
                        {
                            //等下一轮
                            item.CycleNum--;
                            System.Diagnostics.Debug.WriteLine($"@@@@@索引:{item.Slot},剩余:{item.CycleNum}");
                        }
                    }
                    //把已过期的移除掉
                    foreach (var item in target)
                    {
                        list.Remove(item);
                    }
                }
                currentIndex++;
                //下一遍从头开始
                if (currentIndex > queue.Length)
                {
                    currentIndex = 1;
                }
            }
        }
    }

接下是运用办法。

缔造一个管理序列实例的静态类,里面封装对班的操作:

    public static class NotifyPlanManager
    {
        private static DelayQueue<NotifyPlan> _queue = new DelayQueue<NotifyPlan>(60);

        public static void Insert(NotifyPlan plan, DateTime time)
        {
            _queue.Insert(plan, time);
        }

        public static void Read()
        {
            _queue.Read();
        }
    }

构建我们的骨子里工作参数近似,派生自DelayQueueParam:

    public class NotifyPlan : DelayQueueParam
    {
        public Guid CamId { get; set; }

        public int PreviousTotal { get; set; }

        public int Amount { get; set; }
    }

生儿育女端往队列中插数据:

    Action<object> callback = (result) =>
    {
        var np = result as NotifyPlan;
        //这里做自己的业务操作
        //举个例子:
        Debug.WriteLine($"活动ID:{np.CamId},已发送数量:{np.PreviousTotal},本次发送数量:{np.Amount}");
    };
    NotifyPlanManager.Insert(new NotifyPlan
    {
        Amount = set.MainAmount,
        CamId = camId,
        PreviousTotal = 0,
        Callback = callback
    }, smsTemplate.SendDate);

重创一个每秒执行同样次等的定时器用做消费端,我这里用的凡FluentScheduler,核心代码:

    internal class NotifyPlanJob : IJob
    {
        /// <summary>
        /// 执行计划
        /// </summary>
        public void Execute()
        {
            NotifyPlanManager.Read();
        }
    }

    internal class JobFactory : Registry
    {
        public JobFactory()
        {
            //每秒运行一次
            Schedule<NotifyPlanJob >().ToRunEvery(1).Seconds();
        }
    }

  JobManager.Initialize(new JobFactory());

下一场开调试运行,打开本机的网时面板,对正在时光看输出结果。亲测有效。

 

这种气候,毫无疑问,正是出去的好时刻呀。

总结

 这种方案的裨益是避免了往往地环顾数据库和莫必要之作业操作,另外也充分有利控制时间精度。带来的题目是一旦web服务特别要另行开可能会见出任务少的状,我当下之拍卖措施是当数据库中标记任务状态,服务启动时把状态呢“排队吃”的天职重新加载到行列中等候消费。

如上方案以单机环境测试没问题,多节点情况下小并未探究。若有筹划实现上的缺陷,欢迎讨论以及指正,要是发生还好的方案,那就当抛砖引玉,再好不过了~

 

 

先是立-大婶婆卤肉饭

杀婶婆卤肉饭,在上海于老的相同之中台湾小吃料理店,来自微博上之引进,在虹井路797声泪俱下(近延安西路),地铁十号线上海动物园站,再倒相同扭转就是到了,味道不错,价格也还是坏灵的,20片为主会吃的不错。本人也吃了无数卤肉饭(譬如永和高手的卤肉饭),这家宾馆之卤肉有肥有瘦,肥的尚广大,但是并无见面腻,好像还有不少分号,比较差的是,我点了排骨饭,虽然是同可怜块炸排骨,加上同样碗饭,但是卤肉太少,不舒服,建议大家要么点同样碗小碗的卤肉饭加一个炸排骨,卤肉饭还是要小碗的才好。

排骨饭

第二站-韩国街

沿虹井路一直倒,一直倒,就交了上海闵行著名的韩国街-虹泉路,这长达场是韩国丁当上海之聚居处,基本都是来韩国底旅舍,韩国烤肉,韩式火锅,韩国炸鸡,韩国咖啡吧,韩国面包,韩国供销社等等。

虹泉路

则好想念吃烤肉,水源王烤肉,喜来稀肉,但是都吃罢饭了(绝对免是盖从没钱)。沿着韩国街走来走去,看了同一寒而平等寒烤肉,部队锅,然而都吃不打,找了caffebene,张根硕,金秀贤代言的,中文名翻译的差不多好呀,咖啡陪您,奈何没有钱,只触及了一个冰淇淋。

冰淇淋

说实话,有接触最幸福了,果然穷人不合乎咖啡馆,有时候也是眷恋像文艺青年一样,那么一个午后,安静的以于咖啡馆的靠窗的职务,看正在书写。好刺激,好装逼呀。
值得一说之是,咖啡陪而的取餐系统,好高端呀,乡下来的儿女,没见了这么的也罢。

叫单机

第三站-韩国街-酷必香

崭新鸡肉和年糕组合“Sweety”受捧!,尝下,没有那香,其实很一般的,又拧的点了25初次而乐鸡,就是加了同等盏可乐,性价比也未是杀高,有硌失望。

可乐鸡

季站-韩国街-多乐之日

蛋糕店什么的突发性真是又当贵又禁不住诱惑,多乐之日当作韩国底蛋糕店,果然要以贵。。又鲜美。奶油号角是此处的牌号,奶油好好吃,外皮也格外酥化。

面包

奶油号角

发表评论

电子邮件地址不会被公开。 必填项已用*标注