8

好的,这里有一点背景。我有一个大型 Web 应用程序 (MVC3),它可以处理各种不重要的事情。我需要这个 Web 应用程序能够在 Oracle 数据库中安排临时 Quartz.NET 作业。然后,我希望稍后通过Windows 服务执行这些作业。理想情况下,我想安排它们以均匀的间隔运行,但可以选择通过 Web 应用程序添加作业。

基本上,所需的架构是这样的一些变体:

Web 应用程序 <--> Quartz.NET <--> 数据库 <--> Quartz.NET <--> Windows 服务

到目前为止我编写的代码:

  • (目前)计划和运行作业的 Windows 服务。从长远来看,这显然不会是这种情况,但我想知道我是否可以保留它并对其进行修改,使其基本上代表上图中的两个“Quartz.NET”。
  • 网络应用程序(我想细节在这里不是很重要)
  • 作业(实际上只是另一个Windows 服务)

还有一些重要的注意事项:

  • 它必须从 Windows 服务运行,并且必须通过 Web 应用程序进行调度(以减少 IIS 上的负载)
  • 假设上面的项目符号仍然适用,上面的架构可以重新排列一点。

现在,有几个问题:

  1. 这甚至可能吗?
  2. 假设(1)通过,你们认为最好的架构是什么?请参阅我编写的第一个项目符号。
  3. 有人可以给我一些 Quartz 方法,这些方法可以帮助我查询数据库,以便在它们已经安排好后执行?

只要它符合条件,就会有关于这个问题的赏金。如果问题在此之前以令人满意的方式回答,我仍然会将赏金奖励给答案的发布者。因此,无论如何,如果您在这里给出一个好的答案,您将获得赏金。

4

1 回答 1

27

我将尝试按照您提出问题的顺序回答您的问题。

  1. 是的,有可能做到这一点。这实际上是使用 Quartz.Net 的一种常见方式。事实上,您也可以编写一个管理 Quartz.Net 调度程序的 ASP.Net MVC 应用程序。

  2. 建筑学。理想情况下,在高层次上,您的 MVC 应用程序将使用 Quartz.Net API 与作为 Windows 服务安装在某处的 Quartz.Net 服务器通信。Quartz.Net 使用远程处理进行远程通信,因此使用远程处理的任何限制都适用(例如 Silverlight 不支持它等)。Quartz.Net 提供了一种将其安装为开箱即用的 Windows 服务的方法,因此除了将服务本身配置为使用(在您的情况下)AdoJobStore 以及启用远程处理。关于如何正确安装该服务需要注意一些事项,所以如果您还没有这样做,请查看这篇文章

在内部,在您的 MVC 应用程序中,您需要获取对调度程序的引用并将其存储为单例。然后在您的代码中,您将安排作业并通过此唯一实例获取有关调度程序的信息。你可以使用这样的东西:

public class QuartzScheduler
{
    public QuartzScheduler(string server, int port, string scheduler)
    {
        Address = string.Format("tcp://{0}:{1}/{2}", server, port, scheduler);
        _schedulerFactory = new StdSchedulerFactory(getProperties(Address));

        try
        {
            _scheduler = _schedulerFactory.GetScheduler();
        }
        catch (SchedulerException)
        {
            MessageBox.Show("Unable to connect to the specified server", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
        }
    }
    public string Address { get; private set; }
    private NameValueCollection getProperties(string address)
    {
        NameValueCollection properties = new NameValueCollection();
        properties["quartz.scheduler.instanceName"] = "RemoteClient";
        properties["quartz.scheduler.proxy"] = "true";
        properties["quartz.threadPool.threadCount"] = "0";
        properties["quartz.scheduler.proxy.address"] = address;
        return properties;
    }
    public IScheduler GetScheduler()
    {
        return _scheduler;
    }
}

此代码设置您的 Quart.Net 客户端。然后访问远程调度程序,只需调用

GetScheduler()
  1. 查询 下面是一些从调度程序获取所有作业的示例代码:

    public DataTable GetJobs()
    {
        DataTable table = new DataTable();
        table.Columns.Add("GroupName");
        table.Columns.Add("JobName");
        table.Columns.Add("JobDescription");
        table.Columns.Add("TriggerName");
        table.Columns.Add("TriggerGroupName");
        table.Columns.Add("TriggerType");
        table.Columns.Add("TriggerState");
        table.Columns.Add("NextFireTime");
        table.Columns.Add("PreviousFireTime");
        var jobGroups = GetScheduler().GetJobGroupNames();
        foreach (string group in jobGroups)
        {
            var groupMatcher = GroupMatcher<JobKey>.GroupContains(group);
            var jobKeys = GetScheduler().GetJobKeys(groupMatcher);
            foreach (var jobKey in jobKeys)
            {
                var detail = GetScheduler().GetJobDetail(jobKey);
                var triggers = GetScheduler().GetTriggersOfJob(jobKey);
                foreach (ITrigger trigger in triggers)
                {
                    DataRow row = table.NewRow();
                    row["GroupName"] = group;
                    row["JobName"] = jobKey.Name;
                    row["JobDescription"] = detail.Description;
                    row["TriggerName"] = trigger.Key.Name;
                    row["TriggerGroupName"] = trigger.Key.Group;
                    row["TriggerType"] = trigger.GetType().Name;
                    row["TriggerState"] = GetScheduler().GetTriggerState(trigger.Key);
                    DateTimeOffset? nextFireTime = trigger.GetNextFireTimeUtc();
                    if (nextFireTime.HasValue)
                    {
                        row["NextFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(nextFireTime.Value.DateTime);
                    }
    
                    DateTimeOffset? previousFireTime = trigger.GetPreviousFireTimeUtc();
                    if (previousFireTime.HasValue)
                    {
                        row["PreviousFireTime"] = TimeZone.CurrentTimeZone.ToLocalTime(previousFireTime.Value.DateTime);
                    }
    
                    table.Rows.Add(row);
                }
            }
        }
        return table;
    }
    

您可以在Github上查看此代码

于 2012-05-31T21:10:54.650 回答