代码之家  ›  专栏  ›  技术社区  ›  Kendall Hopkins

可扩展、延迟的PHP处理

  •  19
  • Kendall Hopkins  · 技术社区  · 15 年前

    我正在开发一个需要延迟PHP事件的在线PHP应用程序。基本上,我需要能够执行任意PHP代码x许多秒(但可能是几天)后,最初击中一个网址。我需要这些PHP事件相当精确的执行,而且我希望它是相当可伸缩的。我试图避免需要安排cron作业每秒钟运行一次。我在调查 Gearman

    如果我能告诉某个外部进程在下一个事件应该运行的确切时间轮询PHP服务器上的“事件检查器”url,那就太理想了。此轮询时间需要能够随意减少或增加,因为可以删除事件并将其添加到队列和队列中。有没有什么好办法来完成这个任务? 在外部调用PHP(必须解析HTTP请求或通过CLI调用)以使这个想法适合我的需要时,开销非常大。

    我目前的计划是编写一个PHP守护进程,它将运行事件并通过gearman从PHP服务器与之交互。PHP守护进程将围绕 SplMinHeap 所以希望表演不会太差。这个主意让我口齿不清,我想知道有没有人有更好的主意?

    编辑:

    我正在创建一个在线游戏,让玩家在不同的时间限制下轮换。我使用XMPP和BOSH来允许我向我的客户推送消息,但是我已经完成了这部分工作。现在我尝试添加一个任意事件,在客户端播放后触发,让客户端(以及游戏中的其他ppl)长时间等待。我不能在客户端使用定时触发器,因为这是可以利用的(因为客户端可以自己玩)。希望有帮助。

    谢谢大家的反馈。虽然我认为您的大多数想法在小范围内都能很好地工作,但我有一种感觉,它们的可扩展性不好(外部事件管理器),或者缺乏这个项目所需的精确性(CRON)。而且,在这两种情况下,它们都是外部部件,可能会失败,并给已经复杂的系统增加复杂性。

    清洁的 第一 PHP运行循环。它处理监视套接字和执行延迟的PHP事件。希望当我接近完成这个项目,我可以张贴的来源,如果你们有兴趣的。到目前为止,在测试中它已经被证明是有希望的解决方案(没有内存泄漏或不稳定的问题)。

    编辑3: LooPHP 对于那些感兴趣的人。

    热释光;DR要求

    • 任意处理事件的创建/更新/删除(我预计会有大量取消的调用)。
    • 处理计划的高负载事件(每台服务器每秒100-1000个)
    • 在这一点上,我不愿意将代码库重写成另一种语言(也许有一天我会)
    15 回复  |  直到 15 年前
        1
  •  9
  •   Mehdi    9 年前

    我认为只有PHP的解决方案很难实现(几乎不可能)。我想出了两个解决你问题的办法。

    PHP/Redis解决方案

    肯德尔提出的问题:

    • redis有多稳定:

    Redis非常稳定。开发人员确实编写了一些干净的C代码。您应该在github;)上查看它。很多大型网站也在使用redis。例如github,他们有一个非常有趣的博客 post 他们是如何使github快速的:)。超级进料器也使用 redis . 有更多的大公司正在使用redis;)。我建议你用谷歌搜索一下。

    • redis对PHP的友好程度:

    PHP非常友好。很多用户都在为redis编写PHP库。协议非常简单。您可以使用telnet;)进行调试。例如,快速查看predis是否实现了阻塞pop。

    • 如何删除事件:

    ZRemCommand .

    它与memcached类似,但是 数据集不是易变的,并且 可以是字符串,就像 memcached,还包括列表、集合和 被原子操作操纵 要推动/弹出元素,请添加/删除 交集,集合之间的差异, 等等。Redis支持不同的

    我想到的(伪代码……):

    处理器.php:

    <?php
    ######----processer.php
    ######You should do something like nohup php processor.php enough times for processors to run event. 
    #$key: should be unique, but should also be used by wakeup.php
    while(true) {
        $event = blpop($key); #One of the available blocking threads will wakeup and process event
        process($event); #You should write process. This could take some time so this process could not be available
        zrem($key1, $event); #Remove event after processing it. Added this later!!!!!!
    }
    

    ######----client.php
    ######The user/browser I guess should generate these events.
    #$key1: should be unique.
    #$millis: when event should run
    #$event: just the event to work on.
    
    if ("add event") {
      zadd($key1, $millis, $event);
    } else if ("delete event") {
      zremove($key1, $event)
    }
    
    #Get event which has to be scheduled first
    $first = zrange($key1, 0, 0);
    
    if ($oldfirst <> $first) { #got different first event => notify wakeup.php.
        lpush($key2, $first);
    }
    
    $oldfirst = $first;
    

    ####wakeup.php
    #### 1 time do something like nohup php wakeup.php
    #http://code.google.com/p/redis/wiki/IntroductionToRedisDataTypes => read sorted set part.
    while(true) {
        $first = zrange($key1, 0, 0);
        $event = blpop($key2, $timeoutTillFirstEvent);
    
        if ($event == nill) {
            #Blockingqueue has timedout which means event should be run by 1 of blocking threads.
            blpop($key2, $first);
        }    
    }
    

    我的java解决方案

    今天早上我想我创造了一个 java program 你可以用它来解决你的问题。

    1. 下载 :

      访问 github's download page

    2. 安装 :

      java-jar schedule-broadcaster-1.0-SNAPSHOT-jar-with-dependencies-1277709762.jar

    3. Run simple PHP snippets

      1. 弗斯特 php -f scheduler.php
      2. 下一个 php -f receiver.php
    4. Questions

      我创建了这些小片段,希望您能理解如何使用我的程序。文档中还有一些文档 WIKI .

    应用程序引擎的任务队列

    使用这个模型,appengine的任务 队列API允许您指定任务 请求作为其数据 参考)。程序引用 时尚有时被称为“网络”

    任务队列API允许您指定 提前挂网,无需 等待他们的真正执行。 因此,应用程序可能会创建许多 关闭应用程序引擎;系统将 然后在中异步处理它们 后台(通过“调用”HTTP 请求)。这个web钩子模型支持 高效并行处理-应用程序 网钩,同时。

    总之,任务队列API 允许开发人员在中执行工作 后台,异步地,通过 钩子。系统将调用这些 优化性能的调度 可能正在执行多个webhook 同时进行。这个模型的颗粒 工作单元,基于HTTP 标准,允许应用程序引擎 高效执行后台 以一种与 任何编程语言或网络

        2
  •  10
  •   Zak    12 年前

    让您的php脚本执行一个exec调用,以安排您的php脚本在您需要的时候使用命令“at”运行

    exec(“22:56/usr/bin/php myscript.php”);

    at在指定的时间执行命令。

    从手册页:

    At允许相当复杂的时间规范,扩展POSIX.2 标准。它接受HH:MM格式的时间来在spe上运行作业 cific时间(如果时间已经过去了,第二天就是 您还可以指定午夜、中午或茶点(下午4点)和 你可以在一天中的某个时间加上AM或PM作为跑步的后缀 以月份名称day的形式给出日期,可选择年份,或 注明日期,格式为MMDDYY或MM/DD/YY或DD.MM.YY。指定 也可以给时间像现在+计数时间单位,其中的时间单位 可以是分钟、小时、天或周,您可以告诉at运行 用今天的时间做今天的工作,明天再做 把时间安排在明天。

    此外,如果您需要1秒的时间分辨率,请让脚本在一分钟开始时运行,然后只需休眠n秒,直到该执行为止。

        3
  •  4
  •   Evan    15 年前

    这似乎是数据库中事件队列的最佳位置。

    让您的用户创建的事件(通过访问网页触发)在数据库中创建一个条目,其中包括要执行的操作的说明,以及该操作何时发生的时间戳。守护进程(持久化应用程序或CRON触发的)检查DB中本应发生的事件( $TriggerTime <= time() )而且还没有被标记为“已处理”。如果您发现一个或多个这样的事件,请执行该指令,最后在数据库中将该事件标记为“已处理”,或者简单地删除该条目。

    另外,有很多人在服务器上使用PHP作为通用的守护程序脚本语言,Cron可以执行一个PHP脚本(并确认“app”的一个实例已经在运行),该脚本每隔一段时间检查一次事件队列。你可以让一个小应用程序在一分钟的不活动后死掉,然后由CRON重新启动。该应用程序可以检查数据库的条目在一个快速频率的你选择(如1)。通常Cron不能以超过每分钟一次的速度执行定时事件。

        4
  •  2
  •   towe75    15 年前

    我也推荐队列策略,但您似乎不喜欢将数据库用作队列。你有一个XMPP基础设施,所以利用它:使用 pubsub 节点并将事件发布到此节点。Pubsub可以选择配置为以持久的方式存储未缓存的项。

    您的守护进程(无论使用何种语言)可以在启动时获取所有存储项,并订阅更改以获得有关传入操作的通知。通过这种方式,您可以以一种优雅的异步方式解决问题。

        5
  •  1
  •   Colin O'Dell    15 年前

        6
  •  0
  •   Matthew    15 年前
        7
  •  0
  •   Phil Wallach    15 年前

    我不知道你为什么要避开克朗。您可以在表中创建一个请求队列,并让cron启动一个进程来检查当前作业。

    有几个问题,取决于您的确切要求。例如:

    我有许多运行PHP的守护程序(使用守护程序工具)。使用这种方法,您可以将请求保存在核心中,并在内部执行您想要的任何时间。

    然而,如果您想要的是精确和可靠的计时,那么您可能应该完全远离PHP。

        8
  •  0
  •   Alix Axel    15 年前

    我想不出任何东西能满足你的要求:

    • 必须非常精确
    • 长时间拖延
    • 移除/更改事件时间的能力

    简单的方法是使用以下函数的组合:

    set_time_limit(0);
    ignore_user_abort(true);
    time_sleep_until(strtotime('next Friday'));
    // execute code
    

    但是,正如@deceze所说的,这可能不是一个好主意,因为如果设置高延迟,Apache最终可能会杀死子进程(除非您使用的是PHP CLI,否则这会更容易)。它也不允许您更改/删除事件,除非您设置了更复杂的逻辑和数据库来保存事件。也, register_shutdown_function() 如果你想走这条路可能会有用。

        9
  •  0
  •   Brent Baisley    15 年前

    我只是经常使用cron运行一个PHP文件(即5分钟)。PHP文件将检查是否有任何事件需要在下一个间隔内触发,获取间隔事件列表,并休眠到下一个事件。醒来,启动列表中的下一个事件,睡眠到下一个事件,重复直到完成。

    您甚至可以通过分叉或启动另一个php文件来扩展它,从而实际触发事件。然后您可以同时触发多个事件。

        10
  •  0
  •   Killer_X    15 年前

    使用其中一个cron来运行一个checker怎么样?例如,它可以执行DB中的内容。

    或者使用“at”linux命令来调度某个命令的执行?

        11
  •  0
  •   nathan    15 年前

    您真正需要的是一种支持xmpp的事件驱动语言,为此,您只需查看node.js/v8和支持xmpp的库—它本机支持并专为您所需而设计。您也可以沿着Java路线前进,但是如果您想快速地进行移植并获得大量新特性和对所做工作的支持,node就是其中之一。

        12
  •  0
  •   Ibrahim Okuyucu    14 年前

    存在一个纯PHP解决方案。和埃文的回答差不多。只需为事件引入一个“处理”状态,就可以减少DB上的负载(以及锁定问题)。当processing脚本从队列(DB)中提取事件时,它们被标记为“processing”并提交。脚本完成后,它们被标记为“已处理”。如果出现错误或脚本失败,“处理”事件必须更新回原始状态。

        13
  •  0
  •   William Entriken    12 年前
    • 将所有任务存储在具有启动时间的数据库中
    • Cron作业每小时运行一次
      • 阅读接下来的60分钟作业
      • 主回路
        • 微睡到下一个工作
        14
  •  0
  •   Saurabh Chandra Patel cskwg    10 年前

    用redis检查一下。可能对你的问题有用

    https://github.com/chrisboulton/php-resque-scheduler

        15
  •  -9
  •   Andres SK    15 年前

    使用睡眠功能: http://php.net/sleep