20

Quartz 调度程序每秒可以运行的作业数量似乎是有限制的。在我们的场景中,我们每秒有大约 20 个作业以 24x7 的方式启动,quartz 工作良好,达到每秒 10 个作业(对于 JDBC 支持的 JobStore 有 100 个石英线程和 100 个数据库连接池大小),但是,当我们将其增加到 20每秒作业,石英变得非常非常慢,并且其触发的作业与实际预定时间相比非常晚,导致许多Misfires并最终显着降低系统的整体性能。一个有趣的事实是,JobExecutionContext.getScheduledFireTime().getTime()对于此类延迟触发,它们会在其预定时间之后 10 到 20 分钟甚至更多分钟。

石英调度程序每秒可以运行多少个作业而不影响作业的预定时间,对于这种负载,石英线程的最佳数量应该是多少?

或者我在这里错过了什么?

关于我们想要实现的目标的详细信息:

我们有将近 10k 个项目(分为 2 个或更多类别,在当前情况下我们有 2 个类别),我们需要以给定的频率对其进行一些处理,例如 15,30,60... 分钟,这些项目应在该频率内处理每分钟给定的油门。例如,假设 60 分钟频率,每个类别的 5k 个项目应以每分钟 500 个项目的节流进行处理。因此,理想情况下,这些项目应在一天中每小时的前 10 (5000/500) 分钟内处理,每分钟有 500 个要处理的项目均匀分布在每分钟的每一秒内,因此我们将有大约 8-一个类别每秒 9 个项目。

现在为了实现这一点,我们使用 Quartz 作为调度程序来触发处理这些项目的作业。但是,我们不会在 Job.execute 方法中处理每个项目,因为每个涉及 Web 服务调用的项目处理需要 5-50 秒(平均为 30 秒)。我们宁愿为JMS队列上的每个项目处理推送一条消息,并且单独的服务器机器处理这些作业。我注意到 Job.execute 方法所花费的时间不超过30 毫秒

服务器详情:

Solaris Sparc 64 位服务器,具有 8/16 核/线程 cpu,用于调度程序,具有 16GB RAM,我们在调度程序集群中有两台这样的机器。

4

5 回答 5

12

在之前的项目中,我遇到了同样的问题。在我们的例子中,Quartz 的性能可以达到一秒的精度。亚秒级调度是一个延伸,正如您所观察到的,经常发生失火并且系统变得不可靠。

通过创建 2 级调度解决了这个问题: Quartz 将调度 n 个连续作业的作业“集”。对于集群 Quartz,这意味着系统中的给定服务器将“设置”这个作业来执行。然后集合中的 n 个任务由“微调度程序”接收:基本上是一种计时工具,它使用本机 JDK API 将作业进一步计时到 10ms 粒度。

为了处理单个作业,我们使用了 master-worker 设计,其中 master 负责将作业按计划交付(节流)到一个多线程的 worker 池。

如果我今天必须再次这样做,我将依靠ScheduledThreadPoolExecutor来管理“微调度”。对于您的情况,它看起来像这样:

ScheduledThreadPoolExecutor scheduledExecutor;
...
    scheduledExecutor = new ScheduledThreadPoolExecutor(THREAD_POOL_SIZE);
...

// Evenly spread the execution of a set of tasks over a period of time
public void schedule(Set<Task> taskSet, long timePeriod, TimeUnit timeUnit) {
    if (taskSet.isEmpty()) return; // or indicate some failure ...
    long period = TimeUnit.MILLISECOND.convert(timePeriod, timeUnit);
    long delay = period/taskSet.size();
    long accumulativeDelay = 0;
    for (Task task:taskSet) {
        scheduledExecutor.schedule(task, accumulativeDelay, TimeUnit.MILLISECOND);
        accumulativeDelay += delay;
    }
}

这让您大致了解如何使用 JDK 工具来微调度任务。(免责声明:您需要为 prod 环境提供强大的功能,例如检查失败的任务、管理重试(如果支持)等...)。

通过一些测试和调优,我们在 Quartz 作业和一个计划集中的作业数量之间找到了最佳平衡。

通过这种方式,我们体验了 100 倍的吞吐量提升。网络带宽是我们的实际限制。

于 2012-07-19T18:29:54.957 回答
6

首先检查如何提高 JDBC-JobStore 的性能?在石英文档中。

正如您可能猜到的那样,存在绝对值和确定的度量。这完全取决于您的设置。但是这里有一些提示:

  • 每秒 20 个作业意味着每秒大约 100 个数据库查询,包括更新和锁定。这是相当多的!

  • 考虑将您的 Quartz 设置分发到集群。但是,如果数据库是一个瓶颈,它不会帮助你。也许TerracottaJobStore会来救援?

  • 在系统中拥有K所有核心都K不会充分利用您的系统。如果您的工作是 CPU 密集型的,K那很好。如果他们正在调用外部 Web 服务、阻塞或休眠,请考虑更大的值。但是,由于上下文切换,超过 100-200 个线程会显着降低系统速度。

  • 你试过剖析吗?你的机器大部分时间都在做什么?你可以发布线程转储吗?我怀疑数据库性能不佳而不是 CPU,但这取决于您的用例。

于 2012-07-19T17:23:52.907 回答
2

您应该将线程数限制在可用处理器数量之间n的某个n*3位置。n启动更多线程将导致大量上下文切换,因为它们中的大多数大部分时间都会被阻塞。

就每秒作业而言,它实际上取决于作业运行的时间以及它们因网络和磁盘 io 等操作而被阻塞的频率。

此外,需要考虑的是,也许石英不是您需要的工具。如果您每天发送 1 到 2 百万个工作岗位,您可能需要研究定制解决方案。每天有 200 万个工作,你在做什么?!

另一种选择,这是解决问题的一种非常糟糕的方法,但有时可以......它正在运行的服务器是什么?它是旧服务器吗?它可能会增加内存或其他规格,这会给你一些额外的“嗡嗡声”。当然,这不是最好的解决方案,因为这会延迟问题,而不是解决问题,但是如果您处于紧要关头,它可能会有所帮助。

于 2012-07-19T17:19:51.973 回答
0

在每秒有大量作业的情况下,请确保您的 sql server 使用行锁而不是表锁。在 mysql 中,这是通过使用 InnoDB 存储引擎完成的,而不是默认的仅提供表锁的 MyISAM 存储引擎。

于 2014-08-19T10:49:15.310 回答
0

从根本上说,当您在如此短的时间内处理如此大量的事情时,一次只做一件事情的方法注定是低效的。您需要将事情分组 - 使用工作集的建议方法然后对每个单独的工作进行微调度是第一步,但这仍然意味着每个工作几乎什么都不做。更好的是改进您的网络服务,以便您可以告诉它一次处理 N 个项目,然后使用要处理的项目集调用它。更好的是避免通过 web 服务做这种事情,并在数据库中处理它们,作为集合,这是数据库的优点。任何一次处理一个项目的工作基本上都是不可扩展的设计。

于 2017-10-09T18:27:43.210 回答