Ric*_*tts 5 c# asp.net iis automation windows-services
我需要设置一个每分钟运行一次的自动化任务,并在队列中发送电子邮件.我正在使用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服务部署到两个数据库服务器会更有意义吗?这样可以确保一次只运行一个.
另外,您对从应用程序运行Quartz.NET有何看法?正如millimoose所提到的,我并不一定需要在Web前端运行它,但这样做可以让我不将Windows服务部署到多台机器上,我认为不会出现性能差异.思考?
感谢大家到目前为止的输入.如果需要任何其他信息,请告诉我.
我不得不解决你现在面临的确切问题.
首先,您必须意识到绝对无法在ASP.NET中可靠地运行长时间运行的进程.如果从global.asax实例化您的调度程序类,则无法控制该类的生命周期.
换句话说,IIS可能会决定随时回收承载您的类的工作进程.充其量,这意味着你的课程将被销毁(你无能为力).在最坏的情况下,你的班级将在工作中被杀死.哎呀.
运行长期过程的适当方法是在计算机上安装Windows服务.我会在每个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查询告诉DB将队列中的一个电子邮件(当前未分配)分配给当前实例.然后你SELECT锁定电子邮件并发送它.发送后,从队列中删除电子邮件(或者您处理已发送的电子邮件),然后重复此过程,直到队列为空.
现在您可以在两个方向上扩展:
由于锁定机制,您可以保证队列中的每个电子邮件只发送一次,即使多台计算机上的多个线程都运行相同的代码.
回应评论:我最终得出的实施方案存在一些差异.
首先,我的ASP应用程序可以通知服务队列中有新的电子邮件.这意味着我甚至不必按计划运行,我只需告诉服务何时开始工作.但是,这种通知机制很难在分布式环境中正确使用,因此只需每分钟检查一次队列就可以了.
您使用的时间间隔实际上取决于您的电子邮件递送的时间敏感度.如果需要尽快发送电子邮件,则可能需要每30秒触发一次甚至更少.如果不是那么紧急,你可以每5分钟检查一次.Quartz限制一次执行的作业数(可配置),并且您可以配置如果错过触发器会发生什么,因此您不必担心有数百个作业备份.
其次,我实际上一次锁定5封电子邮件,以减少数据库服务器上的查询负载.我处理的是高容量,因此这有助于提高效率(服务和数据库之间的网络往返次数减少).需要注意的是,如果一个节点在发送一组电子邮件时发生故障(无论出于何种原因,从异常到机器本身崩溃),会发生什么.您将在数据库中以"锁定"行结束,并且不会为它们提供任何服务.团体规模越大,风险越大.此外,如果所有剩余的电子邮件都被锁定,则空闲节点显然无法处理任何事情.
就线程安全而言,我的意思是一般意义上的.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并且不访问任何全局数据结构,您应该没问题.
| 归档时间: | 
 | 
| 查看次数: | 2236 次 | 
| 最近记录: |