石英性能

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

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

或者我在这里遗漏了什么?

关于我们想要实现的目标的细节:

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

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

服务器细节:

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

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

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

为了处理单个作业,我们使用了主工作者设计,其中主人负责将作业的预定交付(限制)处理到multithreading工作者池。

如果我今天必须再次这样做,我依靠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 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环境提供强大function,例如检查失败的任务,管理重试(如果支持)等等)。

通过一些测试+调整,我们发现了Quartz作业与一个预定集中的作业数量之间的最佳平衡。

我们以这种方式经历了100倍的吞吐量改进。 网络带宽是我们的实际限制。

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

正如你可能猜到的那样,它具有绝对值和明确的指标。 这一切都取决于您的设置。 但是这里有一些提示:

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

  • 考虑将Quartz设置分发到群集。 但是,如果数据库是一个瓶颈,它将无济于事。 也许TerracottaJobStore会来救援?

  • 系统中有K核,低于K所有内容都将无法充分利用您的系统。 如果您的工作是CPU密集型的,那么K就可以了。 如果他们呼叫外部Web服务,阻止或睡眠,请考虑更大的值。 但是,由于上下文切换,超过100-200个线程会显着降低系统速度。

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

您应该将线程数限制在nn*3之间,其中n是可用处理器的数量。 旋转更multithreading将导致大量上下文切换,因为大多数线程将在大多数时间被阻塞。

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

此外,需要考虑的是,石英可能不是您需要的工具。 如果您每天发送1-2百万个工作,您可能需要查看自定义解决方案。 你甚至每天有200万个工作岗位做什么?!

另一种选择,这是解决问题的一种非常糟糕的方式,但有时可行……它正在运行的服务器是什么? 它是旧服务器吗? 它可能是撞击撞击或其他规格会给你一些额外的’umph’。 肯定不是最好的解决方案,因为这会延迟问题,而不是地址,但如果你处于紧缩状态,它可能有所帮助。

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

从根本上说,当你在如此短的时间内处理如此众多的事情时,一次做1件物品的方法是注定要失败的。 您需要对事物进行分组 – 使用作业集的建议方法然后微调每个单独的工作是第一步,但这仍然意味着每个工作几乎不做任何事情。 更好的方法是改进您的Web服务,以便您可以告诉它一次处理N个项目,然后使用要处理的项目集来调用它。 更好的方法是避免通过webservices做这种事情,并在数据库中处理所有这些事情,就像集合一样,这就是数据库的优点所在。 任何一次处理一个项目的工作从根本上说是一个不可扩展的设计。