似乎Quartz Scheduler每秒可以运行的作业数量受到限制。在我们的方案中,我们每秒大约有20个作业,可以进行24x7的启动,而石英可以很好地完成每秒10个作业(对于JDBC支持的JobStore,它具有100个石英线程和100个数据库连接池大小),但是,当我们将其增加到20个时每秒的作业数量,石英变得非常非常慢,与实际的计划时间相比,石英的触发作业非常晚,从而导致许多失火,并最终显着降低了系统的整体性能。一个有趣的事实是,JobExecutionContext.getScheduledFireTime().getTime()对于此类延迟触发,将在其计划时间之后10到20分钟甚至更多分钟。
JobExecutionContext.getScheduledFireTime().getTime()
石英调度程序每秒可以运行多少个作业,而不会影响作业的预定时间?对于这种负载,石英线程的最佳数量应该是多少?
还是我在这里想念什么?
我们几乎有1万个项目(分类为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的调度程序,并且在调度程序集群中有两台这样的机器。
在上一个项目中,我遇到了同样的问题。在我们的案例中,Quartz的性能达到了每秒一秒的良好水平。不到一秒的时间安排很麻烦,而且正如您所观察到的,不发火经常发生,并且系统变得不可靠。
通过创建2级调度来解决此问题:Quartz将调度n个连续作业的作业“集合”。使用集群Quartz,这意味着系统中的给定服务器将使该作业“设置”执行。然后,集合中的n个任务由“微调度程序”承担:基本上是一种计时工具,该计时工具使用本机JDK API进一步将作业计时到10ms粒度。
为了处理单独的工作,我们使用了主工作人员设计,其中主工作人员负责将工作按计划交付(限制)到多线程工人池中。
如果我今天必须再次执行此操作,则可以依靠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工具进行微计划任务的总体思路。(免责声明:您需要在产品环境中使其健壮,例如检查失败的任务,管理重试(如果支持)等)。
通过一些测试和调整,我们发现了Quartz作业和一个计划集中的作业数量之间的最佳平衡。
通过这种方式,我们的吞吐量提高了100倍。网络带宽是我们的实际限制。