V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
frozenway
V2EX  ›  PHP

2020 年了,现在最简单的 PHP 执行异步任务的方式是什么,不要框架,尽量十几二十行 PHP 代码能搞定?

  •  
  •   frozenway · 2020-06-12 15:50:05 +08:00 · 7340 次点击
    这是一个创建于 1623 天前的主题,其中的信息可能已经有所发展或是发生改变。

    想在这台服务器(国内)php 保存某些数据后,去更新另外一台服务器(国外)的文件缓存,如果是同步更新,保存数据的响应结果会太慢太卡,想着异步去做。要求尽量简单,不想左一个框架右一个框架,不用 redis,memkache 等,因为开启这些扩展需要编译 php,麻烦

    44 条回复    2020-06-23 15:31:53 +08:00
    luyaolu
        1
    luyaolu  
       2020-06-12 15:51:44 +08:00
    接口请求另一个接口 我想到最简单的
    CODEWEA
        2
    CODEWEA  
       2020-06-12 15:53:32 +08:00
    搜索关键词:linux 进程通信
    yakumo520
        3
    yakumo520  
       2020-06-12 15:56:46 +08:00
    那你干脆不用 php 吧,直接 linux 协程
    qiayue
        4
    qiayue  
       2020-06-12 16:05:36 +08:00
    每一步一个 php 文件,假设 3 步,那就 3 个文件
    step1.php 执行完之后,跳转到 step2.php ,执行完之后再跳转到 step3.php ,执行完之后跳回 step1.php
    每次 step1.php 来判断是否有新的任务需要执行。
    注意跳转不要用 header location 形式,而是用 html meta refresh 形式,延迟 1 秒跳转

    然后你就开浏览器让他不断自动执行就行。
    frozenway
        5
    frozenway  
    OP
       2020-06-12 16:08:42 +08:00
    @qiayue 这个方法不错
    richangfan
        6
    richangfan  
       2020-06-12 16:12:05 +08:00 via Android
    加个定时任务
    TORYOI
        7
    TORYOI  
       2020-06-12 16:22:48 +08:00
    @qiayue 我以前用过这种方式,只是感觉好别扭
    mokeyjay
        8
    mokeyjay  
       2020-06-12 16:31:13 +08:00   ❤️ 1
    crontab -e
    php job.php
    keepeye
        9
    keepeye  
       2020-06-12 16:32:00 +08:00
    单独写个服务,提供一个任务接口,调用接口的时候 fork 子进程处理,直接返回不用等待处理结果
    whahuzhihao
        10
    whahuzhihao  
       2020-06-12 16:32:01 +08:00
    用 popen 另外起一个 php 进程去执行异步任务呗
    ben1024
        11
    ben1024  
       2020-06-12 16:48:56 +08:00
    要是数据库可以用 canal
    纯文件挂文件流传输,一个点开,一个点请求
    yuzo555
        12
    yuzo555  
       2020-06-12 16:56:21 +08:00
    如果只是这个简单的需求,那么 PHP 是可以先响应然后继续执行的呀。

    https://stackoverflow.com/questions/15273570/continue-processing-php-after-sending-http-response
    Ech0o0
        13
    Ech0o0  
       2020-06-12 17:23:57 +08:00
    ```php
    <?php

    set_time_limit(30);
    ignore_user_abort(false);

    header("Content-Length: 0");
    header("Connection: Close");
    echo str_pad('', ini_get('output_buffering'));
    ob_end_flush();
    ob_flush();
    flush();

    //耗时任务
    sleep(10);
    file_put_contents('test.log', date('Y-m-d H:i:s'), FILE_APPEND);
    ```

    ```php
    <?php

    $curl = curl_init();
    curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1/async.php');
    curl_setopt($curl, CURLOPT_HEADER, 0);
    curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
    curl_exec($curl);
    curl_close($curl);

    echo '结束了';
    ```

    有局限性,最好还是用队列吧
    explore365
        14
    explore365  
       2020-06-12 17:30:56 +08:00
    还是用一下框架吧,workerman,比较简单一些
    frozenway
        15
    frozenway  
    OP
       2020-06-12 17:33:49 +08:00
    @yuzo555 这个长见识了
    luckyrayyy
        16
    luckyrayyy  
       2020-06-12 17:37:12 +08:00
    crontab 系统自带啊
    I2E
        17
    I2E  
       2020-06-12 17:40:59 +08:00
    尝试过,没搞出来,应该没法光靠 php 。说 sleep 的是认真的吗?
    I2E
        18
    I2E  
       2020-06-12 17:43:29 +08:00
    php 只能启多个进程去搞
    I2E
        19
    I2E  
       2020-06-12 17:46:36 +08:00
    我们现在通过这种方式实现异步任务。通过 pm2 启动一个 rabbitmq 死信队列,设置到期时间。一旦到期就会执行指定任务
    AngryPanda
        20
    AngryPanda  
       2020-06-12 17:54:48 +08:00
    @I2E fastcgi_finish_request
    caola
        21
    caola  
       2020-06-12 17:58:04 +08:00
    swoole,编译只是一条命令而已 🐶
    falcon05
        22
    falcon05  
       2020-06-12 18:01:20 +08:00
    beanstalkd 简单粗暴
    Tokin
        23
    Tokin  
       2020-06-12 18:53:55 +08:00
    记得当时弄的一个简单的队列,后来改用 node 了。
    dadmin
        24
    dadmin  
       2020-06-12 19:47:45 +08:00
    国外服务器更新缓存功能写成接口 (需要设置 set_time_out(0); ignore_user_abort(true);)
    国内服务器通过 curl 请求国外更新缓存接口 并设置执行超时时间
    xing393939
        25
    xing393939  
       2020-06-12 20:39:42 +08:00
    kaneg
        26
    kaneg  
       2020-06-12 21:51:51 +08:00 via iPhone
    crontab 就是干这个活的
    LevineChen
        27
    LevineChen  
       2020-06-13 01:19:18 +08:00
    fastcgi_finish_request 正解啊 应该是最简单方式了一行代码搞定.
    edk24
        28
    edk24  
       2020-06-13 09:34:11 +08:00
    RabbitMQ, 最近刚用上这个队列功能处理视频转码.
    http://aili.edk24.com/1754293

    也许可以试一下, 消费者和生产者连接同一个服务, 生产者投递任务即时响应返回, 消费者在另一个服务器收到任务对它进行处理


    同步文件可以用 scp 之类的命令. 个人不精的建议. 也就 docker 运行个 Rabbitmq, 其他还真是几行代码
    edk24
        29
    edk24  
       2020-06-13 09:35:25 +08:00
    或者 swoole 有异步任务
    PHPJit
        30
    PHPJit  
       2020-06-13 09:57:56 +08:00 via iPhone
    fastcgi_finish_request
    raysonlu
        31
    raysonlu  
       2020-06-13 10:06:02 +08:00
    “开启这些扩展需要编译 php,麻烦” 。。。???
    yincrow
        32
    yincrow  
       2020-06-13 10:28:29 +08:00
    swoole 的 worker 可以满足
    jay4497
        33
    jay4497  
       2020-06-13 10:42:48 +08:00
    曾经用 curl 做过类似的,就是发 curl 请求,设置不需要返回结果,然后再设个一秒的超时时间,效果就是程序到这会停一秒,然后就继续执行了,不会等待那边处理完成返回结果。。。
    pandait
        34
    pandait  
       2020-06-13 11:04:29 +08:00
    用鸟哥的那个 blog 的 fsockopen
    realpg
        35
    realpg  
       2020-06-13 13:23:37 +08:00
    @frozenway #5 PHP 海量数据下 删除数据、生成数据防卡死的标准操作 自刷新也可以
    ViggoSite
        36
    ViggoSite  
       2020-06-13 13:39:12 +08:00
    系统定时任务 或者 workman,简单又方便
    encro
        37
    encro  
       2020-06-13 13:52:14 +08:00
    dnf or apt install php-redist 还好吧
    workerman swoole Gearman 也还好吧
    phpize 也麻烦?
    blurh11E27
        38
    blurh11E27  
       2020-06-13 15:35:05 +08:00
    redis 发布订阅
    go522000
        39
    go522000  
       2020-06-13 15:51:43 +08:00
    以前用过 fsockopen 做异步请求,挺好用,你可以搜索相关文章参考一下。
    yincrow
        40
    yincrow  
       2020-06-13 16:11:10 +08:00
    @yincrow 我看了下 swoole 的文档,coroutine 和 process
    sagaxu
        41
    sagaxu  
       2020-06-13 16:18:55 +08:00 via Android
    “报告长官,有人在诋毁和攻击 PHP”
    “他都胡说八道些什么?”
    “他竟然把 PHP 特性和技巧讲了一遍”
    msg7086
        42
    msg7086  
       2020-06-13 19:35:34 +08:00
    建议招个运维,解决一下开 PHP 扩展要编译 PHP 的问题。
    cbasil
        43
    cbasil  
       2020-06-15 09:48:26 +08:00
    最简单的使用 fsockopen
    $fp = fsockopen ( $hostname, $port, $errno, $errstr, 600 );
    stream_set_blocking ( $fp, 0 ); //开启非阻塞模式
    fputs ( $fp, "GET " . $url . "\r\n" );
    fclose ( $fp );
    jeepc
        44
    jeepc  
       2020-06-23 15:31:53 +08:00   ❤️ 1
    <?php

    namespace app\util;


    class HttpUtils
    {


    /**
    * 异步执行
    * @param $host
    * @param $path
    * @param array $param
    * @return false|int|string
    */
    public static function requestAsync($host, $path, $param = array())
    {

    $query = isset($param) ? http_build_query($param) : '';

    $fp = @fsockopen($host);

    if (!$fp) {
    logError('连接失败');
    return 'connection error';
    }

    stream_set_blocking($fp, 0); //非阻塞
    stream_set_timeout($fp, 1);//响应超时时间( S )
    $out = "POST " . $path . " HTTP/1.1\r\n";
    $out .= "host:" . $host . "\r\n";
    $out .= "content-length:" . strlen($query) . "\r\n";
    $out .= "content-type:application/x-www-form-urlencoded\r\n";
    $out .= "connection:close\r\n\r\n";
    $out .= $query;

    $result = @fputs($fp, $out);

    @fclose($fp);
    return $result;

    }

    }

    <?php

    namespace app\api\controller;

    use app\util\HttpUtils;
    use think\Controller;
    use think\facade\Log;

    class Test extends Controller
    {
    public function test(){
    sleep(5);
    Log::record(1111);
    }

    public function testAsync(){

    HttpUtils::requestAsync($_SERVER['HTTP_HOST'],'/api/test/test');
    dump('return');
    }

    }

    仅供参考
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4613 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 04:01 · PVG 12:01 · LAX 20:01 · JFK 23:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.