gpt4 book ai didi

c# - 当站点位于多台服务器上时,每分钟运行自动化任务的最佳方法

转载 作者:太空狗 更新时间:2023-10-29 23:08:53 26 4
gpt4 key购买 nike

我需要设置一个自动运行的任务,该任务每分钟运行一次,并在队列中发送电子邮件。我正在使用ASP.NET 4.5和C#。当前,我使用一个调度程序类,该类从global.asax开始,并利用了缓存和缓存回调。我读过这会导致几个问题。

我这样做的原因是因为此应用程序可在多个负载平衡的服务器上运行,这使我可以在一处执行,并且即使一台或多台服务器处于脱机状态,代码也可以运行。

我正在寻找一些方向来使它变得更好。我读过有关Quartz.NET的文章,但从未使用过。 Quartz.NET是否从应用程序中调用方法?或从Windows服务?或通过网络服务?

我还阅读了有关使用Windows服务的信息,但据我所知,这些服务是直接安装到服务器上的。问题是,无论有多少服务器在线并且我不想重复它,我都需要执行任务。例如,如果我在服务器1和服务器2上设置了计划任务,则它们将同时运行,因此会复制请求。但是,如果服务器1处于脱机状态,则我需要服务器2来运行任务。

关于如何向前发展的任何建议还是global.asax方法是多服务器环境的最佳方法?顺便说一句,Web服务器正在运行带有IIS 8的Win Server 2012。

编辑

在请求更多信息时,该队列存储在数据库中。我还要提到数据库服务器与Web服务器是分开的。有两台数据库服务器,但一次只能运行一台。它们都有一个中央存储,因此只有一个数据库实例。当一台数据库服务器出现故障时,另一台服务器将联机。

话虽这么说,将Windows Service部署到两个数据库服务器上是否更有意义?这样可以确保一次只运行一次。

另外,您对从应用程序运行Quartz.NET有什么想法?正如millimoose所提到的,我不一定非要在Web前端上运行它,但是,这样做可以使我不必将Windows服务部署到多台计算机上,并且我认为这两种方式都不会产生性能差异。有什么想法吗?

到目前为止,感谢大家的投入。如果需要任何其他信息,请告诉我。

最佳答案

我不得不解决您现在面临的确切问题。

首先,您必须意识到,您绝对不能可靠地在ASP.NET中运行长时间运行的进程。如果从global.asax实例化调度程序类,则无法控制该类的生命周期。

换句话说,IIS可以随时决定回收承载您的类的辅助进程。充其量,这意味着您的类(class)将被销毁(您对此无能为力)。最糟糕的是,您的类(class)会在工作中被杀死。哎呀。

运行长期进程的适当方法是在计算机上安装Windows Service。我会将服务安装在每个Web框上,而不是数据库上。

该服务实例化Quartz调度程序。这样,您就知道只要计算机启动就可以保证调度程序继续运行。当需要运行作业时,Quartz只需在您指定的IJob类上调用一个方法。

class EmailSender : Quartz.IJob
{
public void Execute(JobExecutionContext context)
{
// send your emails here
}
}

请记住,Quartz在单独的线程上调用 Execute方法,因此您必须小心线程安全。

当然,您现在将在多台计算机上运行相同的服务。听起来您在担心这件事,但实际上您可以将其运用到积极的事情上!

我所做的是在数据库中添加了“锁定”列。执行发送作业时,它通过设置锁定列来锁定队列中特定电子邮件。例如,当作业执行时,生成一个guid,然后:
UPDATE EmailQueue SET Lock=someGuid WHERE Lock IS NULL LIMIT 1;
SELECT * FROM EmailQueue WHERE Lock=someGuid;

这样,您就可以让数据库服务器处理并发性。 UPDATE查询告诉数据库将队列中的一封电子邮件(当前未分配)分配给当前实例。然后,您对锁定的电子邮件进行 SELECT并将其发送。发送后,从队列中删除电子邮件(或者您可以处理已发送的电子邮件),然后重复该过程,直到队列为空。

现在您可以向两个方向扩展:
  • 通过在多个线程上同时运行同一作业。
  • 由于它可以在多台计算机上运行,​​因此您可以有效地负载均衡所有服务器上的发送工作。

  • 由于采用了锁定机制,因此即使多台计算机上的多个线程都运行相同的代码,也可以确保队列中的每封电子邮件仅发送一次。

    回应评论:我最终实现的过程中有一些差异。

    首先,我的ASP应用程序可以通知服务队列中有新电子邮件。这意味着我什至不必按计划运行,我可以简单地告诉服务何时开始工作。但是,这种通知机制很难在分布式环境中正确实现,因此,每分钟左右检查一次队列就可以了。

    间隔时间实际上取决于电子邮件传递的时间敏感性。如果需要尽快发送电子邮件,则可能需要每30秒或更短时间触发一次。如果不是很紧急,您可以每5分钟检查一次。 Quartz限制了一次执行(可配置)的作业数量,并且您可以配置如果错过了触发器将发生的情况,因此您不必担心要备份数百个作业。

    其次,我实际上一次锁定了5封电子邮件,以减少数据库服务器上的查询负载。我处理大量事务,因此这有助于提高效率(减少服务和数据库之间的网络往返次数)。这里要注意的事情是,如果在发送一组电子邮件的过程中某个节点发生故障(无论出于何种原因,从异常到计算机本身崩溃),该怎么办。您最终将在数据库中获得“锁定”行,而没有任何服务。小组规模越大,这种风险越大。同样,如果所有剩余的电子邮件都被锁定,则空闲节点显然无法处理任何事情。

    至于线程安全性,我在 general sense中表示它。 Quartz维护一个线程池,因此您不必担心实际管理线程本身。

    您一定要注意作业中的代码访问什么。根据经验,局部变量应该没问题。但是,如果您访问功能范围之外的任何内容,则线程安全是一个真正的问题。例如:
    class EmailSender : IJob {
    static int counter = 0;

    public void Execute(JobExecutionContext context) {
    counter++; // BAD!
    }
    }

    此代码不是线程安全的,因为多个线程可能会尝试同时访问 counter
    Thread A           Thread B
    Execute()
    Execute()
    Get counter (0)
    Get counter (0)
    Increment (1)
    Increment (1)
    Store value
    Store value

    counter = 1
    counter应该为2,但是相反,我们很难调试竞争条件。下次运行此代码时,可能会这样发生:
    Thread A           Thread B
    Execute()
    Execute()
    Get counter (0)
    Increment (1)
    Store value
    Get counter (1)
    Increment (2)
    Store value

    counter = 2

    ...而您却挠头了脑筋,这一次为什么起作用了。

    在您的特殊情况下,只要您在 Execute的每次调用中创建一个新的数据库连接并且不访问任何全局数据结构,就可以了。

    关于c# - 当站点位于多台服务器上时,每分钟运行自动化任务的最佳方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14698834/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com