RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1033445
Accepted
Aleksej_Shherbak
Aleksej_Shherbak
Asked:2020-10-11 05:47:44 +0000 UTC2020-10-11 05:47:44 +0000 UTC 2020-10-11 05:47:44 +0000 UTC

如何在.NET Core 中组织每 N 秒数千个 http 请求的分布?

  • 772

例如,我的系统必须每 60 秒发送大量 http 请求。这是使用 FCM 云消息传递发送推送通知。这是文档。我不是一个特别有经验的 .NET 开发人员。我想问那些在通过邮件实现后台任务方面有经验的人,如何最好地做到这一点。我试过这样的代码:

public class Example
{
    private static Timer aTimer;

    public static void Main()
    {
        // Create a timer and set a two second interval.
        aTimer = new System.Timers.Timer();
        aTimer.Interval = 2000;

        // Hook up the Elapsed event for the timer. 
        aTimer.Elapsed += OnTimedEvent;

        // Have the timer fire repeated events (true is the default)
        aTimer.AutoReset = true;

        // Start the timer
        aTimer.Enabled = true;

        Console.WriteLine("Press the Enter key to exit the program at any time... ");
        Console.ReadLine();
    }

    private static void OnTimedEvent(Object source, System.Timers.ElapsedEventArgs e)
    {
        Console.WriteLine("The Elapsed event was raised at {0}", e.SignalTime);
    }
} 

这是 Microsoft 官方文档中的一个示例System.Timers。起初我认为它非常适合。由于我对实现有要求,从 Web(MVC 控制器)启动和停止分发,在视图上显示分发是否正在进行,使用 DI。但后来我遇到了一个问题。如果一项任务没有时间完成,而另一项任务开始,则系统崩溃。不只是一个任务落下,而是一般的一切,包括 MVC。

然后我想,也许这就是如何做到的。每分钟都有一个可供推送的用户选择。所有这些都添加到某个队列(可能是 rebbitMQ)。这个队列可以正常工作。我的意思是,它按照自己的节奏工作,或者如果没有推送任务则处于空闲状态。我不会每 10 秒重新推送一次用户,所以这个队列迟早会开始在轻量模式下工作。但有时她必须努力工作。这是我在脑海中看到的模式。我的意思是,最终确定。每隔 60 秒,就会启动一组用户进行推送,将用户添加到某个队列中,当有任务时,队列会按照自己的节奏发送推送。这里出现了问题:

  • 可行吗?
  • 是否会有一个定期的定时器来将推送任务写入队列?还是将 cron 与控制台应用程序一起使用?
  • 或者,也许有某种现成的调度程序用于此类任务,而我将推送任务添加到队列并发送出去的方案是过度工程?

我试过这样的调度程序https://github.com/fluentscheduler/FluentScheduler。他有一个非常奇怪的DI。一切都以某种方式在静态方法上。尝试过 托管服务。但我不明白如何从 Web 停止或启动这样的服务,Start 和 Stop 方法的工作很奇怪。我在这里问了一个关于这个的问题。

总的来说,我请你分享那些做出类似决定的人的经验。非常感谢您!

更新

因此,感谢用户 @tym32167 的评论,我决定简单地等待具有工作的方法在我的计时器的某个迭代中完成。为此,在此方法开始时,我将停止计时器,当它结束时,我将再次启动它。这是代码:

public class Scheduler
{
    private const int MSecond = 1000;
    private readonly int _seconds = MSecond * 10;
    private Timer _aTimer;

    public void Start()
    {
        Console.WriteLine("Sending is started ...");

        _aTimer = new Timer();
        _aTimer.Interval = _seconds;

        _aTimer.Elapsed += OnTimedEvent;

        _aTimer.AutoReset = true;

        _aTimer.Enabled = true;
    }

    public bool IsWorking()
    {
        return _aTimer != null;
    }

    private async void OnTimedEvent(object sender, ElapsedEventArgs e)
    {
        await JustDoIt();
    }

    private async Task JustDoIt()
    {
        _aTimer.Stop();

        // большая и трудная работа 
        await Task.Delay(1000 * 12);
        Console.WriteLine("Done !!");

        _aTimer.Start();
    }

    public void Stop()
    {
        _aTimer.Stop();
        _aTimer = null;
    }
}

这是对真实任务的模拟,因为它只是等待并将“完成”打印到控制台。话虽如此,请注意,我正在尝试模拟方法运行时间超过计时器的情况。示例中的计时器运行 10 秒,方法运行 12 秒。如果同时,并行任务会以执行相同的方法开始,那么它们可能会损坏数据库中的数据,因为在我的真实项目,此方法写入数据库并读取 . 这段代码中实现的内容是这样的。首先,计时器启动,执行带有工作的方法。方法一开始关闭定时器,然后执行代码(读、写数据库),打开定时器,我们继续无限循环工作。这堂课一切都正确吗?

c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    tym32167
    2020-10-13T01:41:14Z2020-10-13T01:41:14Z

    计时器选项可能如下所示

    public class Scheduler
    {
        private const int TimerIntervalInSeconds = 10;
        private Timer _aTimer;
    
        public void Start()
        {
            Stop();
            Console.WriteLine("Sending is started ...");
            _aTimer = new System.Threading.Timer(OnTimedEvent, 
                null, TimeSpan.Zero, 
                TimeSpan.FromSeconds(TimerIntervalInSeconds));
        }
    
        public bool IsWorking => _aTimer != null;   
    
        private async void OnTimedEvent(object state)
        {
            var now = DateTime.UtcNow;
            var next = now.AddSeconds(TimerIntervalInSeconds);
            _aTimer?.Change(Timeout.Infinite, Timeout.Infinite);        
            try
            {           
                await JustDoIt();
            }
            finally
            {
                var diff = next - DateTime.UtcNow;          
                if (diff.TotalMilliseconds > 0){
                    _aTimer?.Change(diff, 
                        TimeSpan.FromSeconds(TimerIntervalInSeconds));
                }
                else {
                    OnTimedEvent(state);
                }
            }
        }
    
        private Random r = new Random();
        private async Task JustDoIt()
        {
            var wait = r.Next(25);
            Console.WriteLine($"{DateTime.Now} Waiting for next {wait} seconds !!");        
            // большая и трудная работа 
            await Task.Delay(TimeSpan.FromSeconds(wait));
            Console.WriteLine($"{DateTime.Now} Done !!");       
        }
    
        public void Stop()
        {       
            _aTimer?.Dispose();
            _aTimer = null;
        }
    }
    

    我添加了一些随机性,使某些任务持续时间超过 10 秒,而有些则没有。

    此外,作业本身放置在 tfy-finally 中,因为如果当前作业失败,我们不希望中止作业。如果你愿意,可以在那里添加一个捕获。

    好吧,我使用了另一个计时器 - System.Threading.Timer,在我看来,使用它更容易,甚至更容易。虽然你的选择也没有,如果你这样做_aTimer.AutoReset = false;

    如何检查计时器:

    var scheduler = new Scheduler();
    scheduler.Start();
    Console.ReadLine();
    scheduler.Stop();
    

    结论

    Sending is started...
    12.10.2019 19:32:02 Waiting for next 17 seconds !!
    12.10.2019 19:32:19 Done !!
    12.10.2019 19:32:19 Waiting for next 5 seconds !!
    12.10.2019 19:32:24 Done !!
    12.10.2019 19:32:29 Waiting for next 16 seconds !!
    12.10.2019 19:32:45 Done !!
    12.10.2019 19:32:45 Waiting for next 11 seconds !!
    12.10.2019 19:32:56 Done !!
    12.10.2019 19:32:56 Waiting for next 21 seconds !!
    12.10.2019 19:33:17 Done !!
    12.10.2019 19:33:17 Waiting for next 12 seconds !!
    12.10.2019 19:33:29 Done !!
    12.10.2019 19:33:29 Waiting for next 3 seconds !!
    12.10.2019 19:33:32 Done !!
    12.10.2019 19:33:39 Waiting for next 18 seconds !!
    12.10.2019 19:33:57 Done !!
    12.10.2019 19:33:57 Waiting for next 12 seconds !!
    12.10.2019 19:34:09 Done !!
    12.10.2019 19:34:09 Waiting for next 6 seconds !!
    12.10.2019 19:34:15 Done !!
    

    具有异步循环的第二个变体(我更喜欢)

    public class Scheduler
    {
        private const int TimerIntervalInSeconds = 10;  
        private CancellationTokenSource _cancelTokenSource;
    
        public void Start()
        {
            Stop();
            _cancelTokenSource = new CancellationTokenSource();
            Console.WriteLine("Sending is started ...");        
            MainCycle(_cancelTokenSource.Token);
        }
    
        public bool IsWorking => _cancelTokenSource != null;
    
        async Task MainCycle(CancellationToken token)
        {
            while (!token.IsCancellationRequested)
            {
                var nextTime = Task.Delay(TimeSpan.FromSeconds(TimerIntervalInSeconds));            
                try
                {           
                    await Task.WhenAll(nextTime, JustDoIt());
                }
                catch (Exception ex)
                {
                    /// log errors          
                }
            }
        }
    
        private Random r = new Random();
        private async Task JustDoIt()
        {
            var wait = 5 + r.Next(25);
            Console.WriteLine($"{DateTime.Now} Waiting for next {wait} seconds !!");
            // большая и трудная работа 
            await Task.Delay(TimeSpan.FromSeconds(wait));
            Console.WriteLine($"{DateTime.Now} Done !!");
        }
    
        public void Stop()
        {
            _cancelTokenSource?.Cancel();
            _cancelTokenSource = null;
        }
    }
    

    操作原理大致相同,可以检查一模一样

    var scheduler = new Scheduler();
    scheduler.Start();
    Console.ReadLine();
    scheduler.Stop();
    

    结论

    Sending is started ...
    12.10.2019 19:32:47 Waiting for next 16 seconds !!
    12.10.2019 19:33:03 Done !!
    12.10.2019 19:33:03 Waiting for next 26 seconds !!
    12.10.2019 19:33:29 Done !!
    12.10.2019 19:33:29 Waiting for next 24 seconds !!
    12.10.2019 19:33:53 Done !!
    12.10.2019 19:33:53 Waiting for next 9 seconds !!
    12.10.2019 19:34:02 Done !!
    12.10.2019 19:34:03 Waiting for next 23 seconds !!
    12.10.2019 19:34:26 Done !!
    12.10.2019 19:34:26 Waiting for next 22 seconds !!
    12.10.2019 19:34:48 Done !!
    12.10.2019 19:34:48 Waiting for next 27 seconds !!
    12.10.2019 19:35:15 Done !!
    12.10.2019 19:35:15 Waiting for next 15 seconds !!
    12.10.2019 19:35:30 Done !!
    12.10.2019 19:35:30 Waiting for next 7 seconds !!
    12.10.2019 19:35:37 Done !!
    12.10.2019 19:35:40 Waiting for next 9 seconds !!
    12.10.2019 19:35:49 Done !!
    12.10.2019 19:35:50 Waiting for next 23 seconds !!
    

    看看时间,看看什么最适合你。

    • 1

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5