我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何实现定时推送的具体方案

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何实现定时推送的具体方案

详细内容

详细内容大概分为4个部分,1.应用场景 2.遇到问题 3.设计 4.实现 5.运行效果

1.应用场景

需要定时推送数据,且轻量化的实现。

2.遇到问题

  • 如果启动一个定时器去定时轮询
  • (1)轮询效率比较低
  • (2)每次扫库,已经被执行过记录,仍然会被扫描(只是不会出现在结果集中),会做重复工作
  • (3)时效性不够好,如果每小时轮询一次,最差的情况下会有时间误差
  • 如何利用“延时消息”,对于每个任务只触发一次,保证效率的同时保证实时性,是今天要讨论的问题。

3.设计

高效延时消息,包含两个重要的数据结构:

  1. 环形队列,例如可以创建一个包含3600个slot的环形队列(本质是个数组)
  2. 任务集合,环上每一个slot是一个Set

同时,启动一个timer,这个timer每隔1s,在上述环形队列中移动一格,有一个Current Index指针来标识正在检测的slot。

Task结构中有两个很重要的属性:

  1. Cycle-Num:当Current Index第几圈扫描到这个Slot时,执行任务
  2. Task-Function:需要执行的任务指针

假设当前Current Index指向第一格,当有延时消息到达之后,例如希望3610秒之后,触发一个延时消息任务,只需:

  1. 计算这个Task应该放在哪一个slot,现在指向1,3610秒之后,应该是第11格,所以这个Task应该放在第11个slot的Set中
  2. 计算这个Task的Cycle-Num,由于环形队列是3600格(每秒移动一格,正好1小时),这个任务是3610秒后执行,所以应该绕3610/3600=1圈之后再执行,于是Cycle-Num=1

Current Index不停的移动,每秒移动到一个新slot,这个slot中对应的Set,每个Task看Cycle-Num是不是0:

  1. 如果不是0,说明还需要多移动几圈,将Cycle-Num减1
  2. 如果是0,说明马上要执行这个Task了,取出Task-Funciton执行(可以用单独的线程来执行Task),并把这个Task从Set中删除

使用了“延时消息”方案之后,“订单48小时后关闭评价”的需求,只需将在订单关闭时,触发一个48小时之后的延时消息即可:

  1. 无需再轮询全部订单,效率高
  2. 一个订单,任务只执行一次
  3. 时效性好,精确到秒(控制timer移动频率可以控制精度)

4.实现

首先写一个方案要理清楚自己的项目结构,我做了如下分层。

Interfaces , 这层里主要约束延迟消息队列的队列和消息任务行。


public interface IRingQueue<T>
{
    /// <summary>
    /// Add tasks [add tasks will automatically generate: task Id, task slot location, number of task cycles]
    /// </summary>
    /// <param name="delayTime">The specified task is executed after N seconds.</param>
    /// <param name="action">Definitions of callback</param>
    void Add(long delayTime,Action<T> action);
    /// <summary>
    /// Add tasks [add tasks will automatically generate: task Id, task slot location, number of task cycles]
    /// </summary>
    /// <param name="delayTime">The specified task is executed after N seconds.</param>
    /// <param name="action">Definitions of callback.</param>
    /// <param name="data">Parameters used in the callback function.</param>
    void Add(long delayTime, Action<T> action, T data);
    /// <summary>
    /// Add tasks [add tasks will automatically generate: task Id, task slot location, number of task cycles]
    /// </summary>
    /// <param name="delayTime"></param>
    /// <param name="action">Definitions of callback</param>
    /// <param name="data">Parameters used in the callback function.</param>
    /// <param name="id">Task ID, used when deleting tasks.</param>
    void Add(long delayTime, Action<T> action, T data, long id);
    /// <summary>
    /// Remove tasks [need to know: where the task is, which specific task].
    /// </summary>
    /// <param name="index">Task slot location</param>
    /// <param name="id">Task ID, used when deleting tasks.</param>
    void Remove(long id);
    /// <summary>
    /// Launch queue.
    /// </summary>
    void Start();
}
 public interface ITask
 {
 }

Achieves,这层里实现之前定义的接口,这里写成抽象类是为了后面方便扩展。


using System;
using System.Collections.Concurrent;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using DelayMessageApp.Interfaces;
namespace DelayMessageApp.Achieves.Base
{
public abstract class BaseQueue<T> : IRingQueue<T>
{
    private long _pointer = 0L;
    private ConcurrentBag<BaseTask<T>>[] _arraySlot;
    private int ArrayMax;
    /// <summary>
    /// Ring queue.
    /// </summary>
    public ConcurrentBag<BaseTask<T>>[] ArraySlot
    {
        get { return _arraySlot ?? (_arraySlot = new ConcurrentBag<BaseTask<T>>[ArrayMax]); }
    }
    public BaseQueue(int arrayMax)
    {
        if (arrayMax < 60 && arrayMax % 60 == 0)
            throw new Exception("Ring queue length cannot be less than 60 and is a multiple of 60 .");
        ArrayMax = arrayMax;
    }
    public void Add(long delayTime, Action<T> action)
    {
        Add(delayTime, action, default(T));
    }
    public void Add(long delayTime,Action<T> action,T data)
    {
        Add(delayTime, action, data,0);
    }
    public void Add(long delayTime, Action<T> action, T data,long id)
    {
        NextSlot(delayTime, out long cycle, out long pointer);
        ArraySlot[pointer] =  ArraySlot[pointer] ?? (ArraySlot[pointer] = new ConcurrentBag<BaseTask<T>>());
        var baseTask = new BaseTask<T>(cycle, action, data,id);
        ArraySlot[pointer].Add(baseTask);
    }
    /// <summary>
    /// Remove tasks based on ID.
    /// </summary>
    /// <param name="id"></param>
    public void Remove(long id)
    {
        try
        {
            Parallel.ForEach(ArraySlot, (ConcurrentBag<BaseTask<T>> collection, ParallelLoopState state) =>
            {
                var resulTask = collection.FirstOrDefault(p => p.Id == id);
                if (resulTask != null)
                {
                    collection.TryTake(out resulTask);
                    state.Break();
                }
            });
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
    }
    public void Start()
    {
        while (true)
        {
            RightMovePointer();
            Thread.Sleep(1000);
            Console.WriteLine(DateTime.Now.ToString());
        }
    }
    /// <summary>
    /// Calculate the information of the next slot.
    /// </summary>
    /// <param name="delayTime">Delayed execution time.</param>
    /// <param name="cycle">Number of turns.</param>
    /// <param name="index">Task location.</param>
    private void NextSlot(long delayTime, out long cycle,out long index)
    {
        try
        {
            var circle = delayTime / ArrayMax;
            var second = delayTime % ArrayMax;
            var current_pointer = GetPointer();
            var queue_index = 0L;
            if (delayTime - ArrayMax > ArrayMax)
            {
                circle = 1;
            }
            else if (second > ArrayMax)
            {
                circle += 1;
            }
            if (delayTime - circle * ArrayMax < ArrayMax)
            {
                second = delayTime - circle * ArrayMax;
            }
            if (current_pointer + delayTime >= ArrayMax)
            {
                cycle = (int)((current_pointer + delayTime) / ArrayMax);
                if (current_pointer + second - ArrayMax < 0)
                {
                    queue_index = current_pointer + second;
                }
                else if (current_pointer + second - ArrayMax > 0)
                {
                    queue_index = current_pointer + second - ArrayMax;
                }
            }
            else
            {
                cycle = 0;
                queue_index = current_pointer + second;
            }
            index = queue_index;
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
    /// <summary>
    /// Get the current location of the pointer.
    /// </summary>
    /// <returns></returns>
    private long GetPointer()
    {
        return Interlocked.Read(ref _pointer);
    }
    /// <summary>
    /// Reset pointer position.
    /// </summary>
    private void ReSetPointer()
    {
        Interlocked.Exchange(ref _pointer, 0);
    }
    /// <summary>
    /// Pointer moves clockwise.
    /// </summary>
    private void RightMovePointer()
    {
        try
        {
            if (GetPointer() >= ArrayMax - 1)
            {
                ReSetPointer();
            }
            else
            {
                Interlocked.Increment(ref _pointer);
            }
            var pointer = GetPointer();
            var taskCollection = ArraySlot[pointer];
            if (taskCollection == null || taskCollection.Count == 0) return;
            Parallel.ForEach(taskCollection, (BaseTask<T> task) =>
            {
                if (task.Cycle > 0)
                {
                    task.SubCycleNumber();
                }
                if (task.Cycle <= 0)
                {
                    taskCollection.TryTake(out task);
                    task.TaskAction(task.Data);
                }
            });
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
            throw;
        }
    }
}
}
using System;
using System.Threading;
using DelayMessageApp.Interfaces;
namespace DelayMessageApp.Achieves.Base
{
public class BaseTask<T> : ITask
{
    private long _cycle;
    private long _id;
    private T _data;
    public Action<T> TaskAction { get; set; }
    public long Cycle
    {
        get { return Interlocked.Read(ref _cycle); }
        set { Interlocked.Exchange(ref _cycle, value); }
    }
    public long Id
    {
        get { return _id; }
        set { _id = value; }
    }
    public T Data
    {
        get { return _data; }
        set { _data = value; }
    }
    public BaseTask(long cycle, Action<T> action, T data,long id)
    {
        Cycle = cycle;
        TaskAction = action;
        Data = data;
        Id = id;
    }
    public BaseTask(long cycle, Action<T> action,T data)
    {
        Cycle = cycle;
        TaskAction = action;
        Data = data;
    }
    public BaseTask(long cycle, Action<T> action)
    {
        Cycle = cycle;
        TaskAction = action;
    }
    public void SubCycleNumber()
    {
        Interlocked.Decrement(ref _cycle);
    }
}
}

Logic,这层主要实现调用逻辑,调用者最终只需要关心把任务放进队列并指定什么时候执行就行了,根本不需要关心其它的任何信息。


public static void Start()
{
    //1.Initialize queues of different granularity.
    IRingQueue<NewsModel> minuteRingQueue = new MinuteQueue<NewsModel>();
    //2.Open thread.
    var lstTasks = new List<Task>
    {
        Task.Factory.StartNew(minuteRingQueue.Start)
    };
    //3.Add tasks performed in different periods.
    minuteRingQueue.Add(5, new Action<NewsModel>((NewsModel newsObj) =>
    {
        Console.WriteLine(newsObj.News);
    }), new NewsModel() { News = "Trump's visit to China!" });
    minuteRingQueue.Add(10, new Action<NewsModel>((NewsModel newsObj) =>
    {
        Console.WriteLine(newsObj.News);
    }), new NewsModel() { News = "Putin Pu's visit to China!" });
    minuteRingQueue.Add(60, new Action<NewsModel>((NewsModel newsObj) =>
    {
        Console.WriteLine(newsObj.News);
    }), new NewsModel() { News = "Eisenhower's visit to China!" });
    minuteRingQueue.Add(120, new Action<NewsModel>((NewsModel newsObj) =>
    {
        Console.WriteLine(newsObj.News);
    }), new NewsModel() { News = "Xi Jinping's visit to the US!" });
    //3.Waiting for all tasks to complete is usually not completed. Because there is an infinite loop.
    //F5 Run the program and see the effect.
    Task.WaitAll(lstTasks.ToArray());
    Console.Read();
}

Models,这层就是用来在延迟任务中带入的数据模型类而已了。自己用的时候换成任意自定义类型都可以。

5.运行效果

到此这篇关于如何实现定时推送的具体方案的文章就介绍到这了,希望对大家有所帮助,更多相关C#内容请搜索编程网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何实现定时推送的具体方案

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

PHP和WebSocket: 实现实时消息推送的高效方案

PHP和WebSocket: 实现实时消息推送的高效方案在Web应用程序中,实时消息推送是一项常见的需求。传统的基于HTTP协议的请求-响应模式无法满足实时性要求,因此需要采用其他技术来实现实时消息推送。WebSocket是一种能够在浏览器
PHP和WebSocket: 实现实时消息推送的高效方案
2023-12-18

如何利用Redis实现实时推送功能

如何利用Redis实现实时推送功能,需要具体代码示例概述:实时推送功能是指当服务器端有更新时,能够实时将这些消息推送给客户端,例如在线聊天、消息通知等场景。Redis作为一款高性能的内存数据库,有着快速读写的特性,可以很好地支持实时推送功能
如何利用Redis实现实时推送功能
2023-11-07

如何在MongoDB中实现数据的实时推送功能

如何在MongoDB中实现数据的实时推送功能MongoDB是一种面向文档的NoSQL数据库,其特点是具有高可扩展性和灵活的数据模型。在一些应用场景中,我们需要实时地推送数据更新给客户端,以便及时地更新界面或做出相应的操作。本文将介绍如何在M
2023-10-22

vbs如何实现定时发送邮件

本篇内容介绍了“vbs如何实现定时发送邮件”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!用VBS写个脚本,然后用WINDOWS平台下的计划任
2023-06-08

如何理解Python绑定C++程序的具体实现方法

本篇文章给大家分享的是有关如何理解Python绑定C++程序的具体实现方法,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。Python编程语言的应用范围比较广泛,应用方式灵活,可
2023-06-17

SpringBoot实现定时发送邮件的三种方法案例详解

这篇文章主要介绍了SpringBoot三种方法实现定时发送邮件的案例,Spring框架的定时任务调度功能支持配置和注解两种方式Spring Boot在Spring框架的基础上实现了继承,并对其中基于注解方式的定时任务实现了非常好的支持,本文给大家详细讲解,需要的朋友可以参考下
2023-03-06

python实现自定义日志的具体方法

1、导入logging模块:import logging2、创建日志收集器:logger = logging.getLogger(“日志收集器的name”)3、设置日志收集器的日志级别:logger.setLevel(logging.INF
2022-06-02

如何在PHP项目中实现实时消息推送和WebSocket支持?

如何在PHP项目中实现实时消息推送和WebSocket支持?随着互联网的发展,实时消息推送和WebSocket成为了现代web应用中不可或缺的功能。实时消息推送能够实现及时通知和消息传递,提高用户体验,而WebSocket可以用于实现实时双
如何在PHP项目中实现实时消息推送和WebSocket支持?
2023-11-02

SpringBoot实现定时发送邮件的方法有哪些

这篇“SpringBoot实现定时发送邮件的方法有哪些”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“SpringBoot实现
2023-07-05

python如何实现QQ定时发送新年祝福信息

这篇文章将为大家详细讲解有关python如何实现QQ定时发送新年祝福信息,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。效果图:第一步 配置go-cqhttp简单来说,go-cqhttp就是自己搭建的一个Q
2023-06-29

shell脚本如何实现一键同时推送代码至github和gitee

这篇文章将为大家详细讲解有关shell脚本如何实现一键同时推送代码至github和gitee,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。解决办法如下:1.先要初始化你的git 进
2023-06-09

Linux 修改系统时间的两种具体实现方式

在Linux系统中,修改系统时间可通过两种方式实现:命令行工具使用date命令,指定日期和时间参数进行修改。支持设置日期(MMDDhhmm[[[[CC]YY][.ss]]])和时间(-s[hh:mm:ss])。图形化界面GNOME:通过时钟图标访问日历和时间设置。KDE:右键单击时钟图标配置时钟,调整时间和日期。
Linux 修改系统时间的两种具体实现方式
2024-04-02

Linux 修改系统时间的两种具体实现方式

Linux系统时间修改有两种方式:使用date命令和hwclock命令。date命令可直接设置系统时间,而hwclock命令操作硬件时钟,需要同步后再生效。注意,修改系统时间前应备份时区文件,且某些系统需要root权限。
Linux 修改系统时间的两种具体实现方式
2024-04-02

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录