寻找有效的任务计划程序

时间:2014-09-04 17:57:17

标签: php sql multithreading queue scheduled-tasks

我有几个工作需要完成X次,而且我有不同的工人有他们自己的信息。

除了工作有约会之外,我想在从最早的工作开始到下一份工作之前处理一份工作Y次。

工人只能处理一次工作。

目前,我正在使用mysql数据库以及作为工作人员的PHP脚本,但似乎每秒约有50个作业存在瓶颈,我需要更快,所以我必须寻找替代方案。 / p>

我的设置包含以下表格:

jobs

job_id  | job_info  | last_processed    | times_executed    | to_be_done
1   | 949461321 | 05-04-2014 00:14:56   | 192               | 1000
2   | 356454214 | 05-04-2014 00:14:57   | 8                 | 200
3   | 321564642 | 05-04-2014 00:14:58   | 16                | 10000
4   | 546412131 | 05-04-2014 00:14:59   | 3                 | 50

workers 

worker_id   | specific_information  | status
1       | 4656439897543521456       | ok
2       | 6513165165465498498       | not_responsive
3       | 1046486479849870987       | not_responsive
4       | 6540498465494131131       | ok
5       | 6484654321654657498       | ok

除此之外,我还有以下设置(为简单起见,这里是伪代码):

$jobs_per_run = 10; // Process 10 jobs when the script runs
$workers_per_run = 5; // Process each job 5 times before it moves to the next job

FOR $i = 0; $i < $jobs_per_run; $i++ {
    SQL_QUERY "SELECT * FROM jobs WHERE times_executed < to_be_done GROUP BY last_processed ASC"; // get least used job that needs processing
    FOR $j = 0; $j < $workers_per_run; $j++ {
        SQL_QUERY "SELECT * FROM workers WHERE status = 'ok' AND worker_id NOT IN($processed_workers)";
        IF job_was_executed_successfully {
            SQL_QUERY "UPDATE jobs SET times_executed = times_executed + 1, last_processed = NOW() WHERE job_id = $job_id";
        } else {
            SQL_QUERY "UPDATE workers SET status = 'not_responsive' WHERE worker_id = $worker_id"
        }
    }
}

我希望能解释我的需要。我甚至不知道这实际上是如何调用的。

我已经阅读了queuestask schedulersmessaging systems,但问题是我需要由last_processed排序的不同工作人员多次执行作业。

我认为我提到的这些结构就像你把一些工作投入其中并且一个接一个地执行。根据我的设置,我想我需要从一开始就分配工人,然后将工作1分配给1000名分配给不同工人的工作。

对我而言,这是一个问题。有时候工作人员不能随时可用,如果我将工作人员1列入工作2,如果它在几分钟内被执行并且工作人员1在那时没有响应则将是无用的。这就是我目前在执行时分配工作人员的原因。

task scheduler需要比这更容易管理,我需要能够暂停和恢复单个工作,并根据工作人员的不同标准为工人选择工作。表格行#34;。

我希望有300多名工作人员在没有问题的情况下在队列系统上工作。一名工人在大约1秒钟内完成一项工作,这样每秒就能完成300多份工作。

理想情况下,我希望拥有像Amazon AWS这样的云设置,其中我有一个实例作为任务调度程序,以及其他几个执行作业的实例。

获得此类内容的最佳设置是什么?我现在的mysql解决方案似乎最方便和适合。也许这个具有类似功能的工作的数据库更快?

5 个答案:

答案 0 :(得分:6)

尽管我们的老师告诉我们, 这是一个愚蠢的问题;这就是你到目前为止没有得到合适答案的原因。

在这个场合,我不会忽视你提出的问题,而是花时间解释为什么这个问题是愚蠢的,希望未来的读者和你自己都会受益。

  

我希望有300多名工作人员在没有问题的情况下在队列系统上工作

这表明存在误解;它表明你认为你的硬件能够同时执行300个线程,它不是:我会打赌在我们有生之年不会有这样的硬件。

Multi-Threading in PHP with pthreads

  

有倾向于浪费;认为线程化可以使其更快,但却不能,这是一种常见的误解。更多的线程并不总是等同于更多的吞吐量,就像更多的水并不总是等于更湿润一样。

     

more water !== wetter

     

在盒子外思考是一个优秀的多线程程序员的先决条件;常识应该要求更多的水确实意味着更湿润,但是如果你考虑到碗底部的中心点:一旦它被弄湿了,你放在顶部的水量无关紧要,它不会变得更湿润...... / p>      

太多水或线,你会淹死。

您应该阅读所有引用的文档,它应该让您很好地了解可能的内容以及如何。

答案 1 :(得分:0)

我注意到有关您的查询的一件事是您正在选择FOR循环中的所有记录。

而不是使用ORDER BY而不是GROUP,因为我相信你正在尝试做的事情并限制查询本身的作业数量:

SELECT * FROM jobs 
WHERE times_executed < to_be_done 
ORDER BY last_processed ASC LIMIT 10

由于工作只能由单个工作人员完成,因此您应该考虑使用LIMIT

SELECT * FROM workers 
WHERE status = 'ok' AND worker_id NOT IN($processed_workers) LIMIT 1

如果您计划向5名可用工作人员提供同样的工作,仍然会限制您的查询:

SELECT * FROM workers 
WHERE status = 'ok' AND worker_id NOT IN($processed_workers) LIMIT 5

现在使用PHP代替FOR,使用WHILE来完成作业和工作人员记录集。

其他有趣的事情是每项工作的执行需要多长时间。看起来您的解决方案不是异步的,因此作业本身可能会产生瓶颈而不是查询。

答案 2 :(得分:0)

我认为0MQ解释的Divide and Conquer策略可能会对您有所帮助:

术语

首先,&#34; job&#34;有两个含义。在你的问题中:

  1. &#34;&#34; job&#34;这意味着最终将产生最终结果的工作总量。我将继续称之为职位
  2. 需要运行某个进程的1次迭代。这样一来,工人就可以做一件事了。我称之为任务
  3. 所以1 作业由X 任务组成。

    策略

    • 使用呼吸机启动作业。这意味着将初始任务添加到&#34;命令队列&#34;。

    • 拥有任意数量的工作人员来完成实际工作。单个工作人员将从&#34;命令队列&#34;中提取单个任务,对其进行处理,并将结果推送到&#34;结果队列&#34;。

    • 有一个接收器,它将收集&#34;结果队列&#34;的结果。它可以做两件事之一:

      1. 作业尚未完成(需要更多迭代),因此它会将新的任务推送到&#34;命令队列&#34;。
      2. 作业已完成,因此会将最终结果存储在某处。

    为此,任务的有效负载需要包含作业需要运行的次数及其已运行的次数。

    不同类型的工作

    这里有两种方法:

    1. 有多种类型的工人。您还需要多个&#34;命令队列&#34;,每种类型的工作人员都需要从专用于该类型的队列中提取任务。接收器还需要知道(基于任务的有效负载)&#34;命令队列&#34;它需要添加一个新的任务

    2. 让单一类型的工作人员知道如何执行所有不同的任务。包括需要在任务的有效负载中完成的工作类型。通过这种方式,工人将知道该做什么。

    3. 哪种方法更可行取决于实际工作的内容。所以你必须选择自己。

      优点

      此设置的主要优点是:

      • 您可以根据需要添加任意数量的工作人员,这些工作人员将并行运行。您甚至可以将它们放在不同的服务器上。
      • 您不需要中央数据存储(如SQL数据库)来运行完整的作业。您仍然可以使用数据库作为源来启动作业,和/或存储最终结果,但中间的所有内容都将作为有效负载传递。

      工具

      您可以选择任何类型的消息队列或任务管理器。你可以在Queues.io上找到一个不错的列表。

      我建议你同时查看React(使用PHP的事件驱动,非阻塞I / O),它将帮助您进行异步。

答案 3 :(得分:0)

要获得更多吞吐量,您必须使用多线程。

  1. 您可以使用pthreads自行完成。

    来自PHP站点: pthreads是面向对象的API,允许在PHP中进行用户域多线程。它包括创建针对Web或控制台的多线程应用程序所需的所有工具。 PHP应用程序可以创建,读取,写入,执行和与Threads,Workers和Threaded对象同步。

  2. 使用Gearman等开源工具。它解决了你提到的所有问题,如它将允许创建线程&amp;让它们继续运行,并使用其他脚本重新连接它以检查作业状态。

  3. 来自 Gearman 网站:

    Gearman提供了一个通用的应用程序框架,可以将工作分配给更适合工作的其他机器或进程。 它允许您并行工作,负载均衡处理以及在语言之间调用函数。它可以用于各种应用程序,从高可用性网站到数据库复制的传输事件。换句话说,它是分布式处理如何通信的神经系统

    以下一些功能:

    1. 开源
    2. 多语言
    3. 快速
    4. 嵌入式
    5. 没有单点故障
    6. 参考链接如下:

      1. 拉斯穆斯&#39;博客:Playing with Gearman
      2. 来自PHP:Gearman
      3. PHP multithreading (multitasking) on Windows 。我没有经历过这个。在此添加仅供参考。

答案 4 :(得分:0)

为了提高您的表现,您可以做4件简单的事情:

  • 在查询中使用LIMIT仅获取所需的行数
  • 只有在获取多行后调用查询,然后循环结果
  • 不要在查询中使用分组(这些不是分组的情况,只是用户ORDER BY)
  • 检查您的表索引(对此进行基准测试以验证改进):作业需要last_processed的索引,worker需要状态索引

$ jobs_per_run = 10; //脚本运行时处理10个作业 $ workers_per_run = 5; //在每个作业转移到下一个作业之前处理5次

SQL_QUERY "SELECT * FROM jobs WHERE times_executed < to_be_done ORDER BY last_processed ASC LIMIT $jobs_per_run";
while (FETCH ROW) {
    SQL_QUERY "SELECT * FROM workers WHERE status = 'ok' LIMIT $workers_per_run";
    WHILE (FETCH ROW) {
        IF job_was_executed_successfully {
            SQL_QUERY "UPDATE jobs SET times_executed = times_executed + 1, last_processed = NOW() WHERE job_id = $job_id";
        } else {
            SQL_QUERY "UPDATE workers SET status = 'not_responsive' WHERE worker_id = $worker_id"
        }
    }
}

如果您的工作岗位少于$ jobs_per_run,或者可用工作人员数量低于$ workers_per_run,则可能会在计划方面有所不同。在这些情况下,每次运行的处理次数会减少,但运行速度也会快得多。 在任何情况下,每次运行现在最多可以执行11个SELECTS而不是之前的50个。

如果您总是有很多工作人员,您甚至可以在运行开始时获取最多50名工人的列表,并在内部循环中获取progressiveli。这会将SELECTS的数量减少到每次运行2次。