请教workerman/http-client用法

Forsend

问题描述

业务场景:webman开发,提供一个给用户端调用的api接口,实现逻辑如下:
1、把用户提交的数据处理下,然后请求若干个三方系统的http接口
2、根据这些三方接口返回的内容,来生成api接口响应数据

比如,有3个三方接口A、B、C,都有可能返回我想要的数据
如果A响应最快,返回了['IS_A'=> true, 'A DATA' => '...'],那么就直接拿'A DATA'处理下生成接口响应数据。B、C的响应结果直接丢弃不用
如果A响应最快,但是返回了['IS_A' => false],那么就继续等待下一个最先响应、并且'IS_X'是true的三方接口

为此你搜索到了哪些方案及不适用的原因

现在要求就是这个api接口,性能要高,速度要快(假设这些三方接口本身响应都足够快)
然后搜了下站内很多帖子,都是推荐用workerman/http-client
但是我不太清楚,怎么才能最好的实现我想要的效果

已经用了workermanV5、http-client2.0、revolt/event-loop
截图

<?php

namespace app\controller;

use support\Request;

class ApiController
{
    public function index(Request $request)
    {
        $http = new \Workerman\Http\Client();

        // 怎么实现下面代码的业务
        // 但是三方接口1,和三方接口2能同时请求,节省时间
        // 哪个先返回我想要的结果,接口就响应哪个数据

        $resp1 = $http->get('三方接口1');
        $data1 = json_decode($resp1->getBody(), true);
        if ($data1['success']) {
            return json(['data' => $data1['data']]);
        }

        $resp2 = $http->get('三方接口2');
        $data2 = json_decode($resp2->getBody(), true);
        if ($data2['success']) {
            return json(['data' => $data2['data']]);
        }

        return json(['data' => null]);
    }
}
641 1 4
1个回答

walkor

这种情况用curl_multi就可以,下面代码是让webman AI的gpt4生成的,你试下

<?php
// 要请求的URL列表
$urls = [
    'http://example.com',
    'http://example.org',
    'http://example.net'
];

// 初始化多个CURL句柄
$multiCurl = curl_multi_init();
$curlArray = array();

foreach ($urls as $i => $url) {
    $curlArray[$i] = curl_init($url);
    curl_setopt($curlArray[$i], CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($multiCurl, $curlArray[$i]);
}

// 执行批处理句柄
$index=null;
do {
    while (($execrun = curl_multi_exec($multiCurl, $index)) == CURLM_CALL_MULTI_PERFORM);
    if ($execrun != CURLM_OK) break;
    // 一旦有一个请求完成,找出是哪个请求
    while ($done = curl_multi_info_read($multiCurl)) {
        // 获取信息和内容
        $info = curl_getinfo($done['handle']);
        if ($info['http_code'] == 200)  {
            $output = curl_multi_getcontent($done['handle']);

            //关闭句柄
            foreach ($urls as $i => $url) {
                curl_multi_remove_handle($multiCurl, $curlArray[$i]);
                curl_close($curlArray[$i]);
            }
            curl_multi_close($multiCurl);

            // 返回获取的输出
            return $output;
        }
    }
    // 如果没有请求完成,就暂时挂起连接
    if ($index > 0) {
        curl_multi_select($multiCurl);
    }
} while ($index > 0);

// 如果所有请求都没有成功,关闭CURL句柄并返回false
foreach ($urls as $i => $url) {
    curl_multi_remove_handle($multiCurl, $curlArray[$i]);
    curl_close($curlArray[$i]);
}
curl_multi_close($multiCurl);
return false;
  • Forsend 2023-12-21

    谢谢解答,我先试下。
    请问我的这种需求,没必要用workerman/http-client么?
    搜了站内一些帖子,感觉我的需求跟这个好像差不多,不是用异步协程http可以提高性能么
    https://www.workerman.net/q/11997

  • walkor 2023-12-21

    异步或者协程性能好一些,但是要求业务不能使用全局变量(包括静态成员)存储请求相关的状态数据,否则会导致逻辑错乱。
    下面是webman协程用法

        public function index(Request $request)
        {
            $urls = [
                'https://www.163.com',
                'https://www.bing.com',
                'https://example.com'
            ];
            $http = new \Workerman\Http\Client();
            $suspension = EventLoop::getSuspension();
            foreach ($urls as $url) {
                $http->get($url, function ($response) use ($suspension, $http) {
                    $data = (string)$response->getBody();
                    if (strpos($data, 'Example Domain')) { // 假设返回中必须有Example Domain才算成功
                        $suspension->resume($data);
                    }
                });
            }
            $data = $suspension->suspend();
            return response($data);
        }
  • Forsend 2023-12-21

    Error: Must call suspend() before calling resume() in /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Internal/DriverSuspension.php:52
    Stack trace:

    0 /home/web/webman-co/app/controller/ApiController.php(60): Revolt\EventLoop\Internal\DriverSuspension->resume()

    1 [internal function]: app\controller\ApiController->app\controller{closure}()

    2 /home/web/webman-co/vendor/workerman/http-client/src/Client.php(181): call_user_func()

    3 [internal function]: Workerman\Http\Client->Workerman\Http{closure}()

    4 /home/web/webman-co/vendor/workerman/http-client/src/Emitter.php(120): call_user_func_array()

    5 [internal function]: Workerman\Http\Emitter->emit()

    6 /home/web/webman-co/vendor/workerman/http-client/src/Request.php(130): call_user_func_array()

    7 /home/web/webman-co/vendor/workerman/http-client/src/Request.php(449): Workerman\Http\Request->emit()

    8 /home/web/webman-co/vendor/workerman/http-client/src/Request.php(376): Workerman\Http\Request->emitSuccess()

    9 /home/web/webman-co/vendor/workerman/http-client/src/Request.php(360): Workerman\Http\Request->handleData()

    10 /home/web/webman-co/vendor/workerman/http-client/src/Request.php(327): Workerman\Http\Request->checkComplete()

    11 /home/web/webman-co/vendor/workerman/workerman/src/Connection/TcpConnection.php(725): Workerman\Http\Request->onMessage()

    12 /home/web/webman-co/vendor/workerman/workerman/src/Events/Revolt.php(146): Workerman\Connection\TcpConnection->baseRead()

    13 /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php(589): Workerman\Events\Revolt->Workerman\Events{closure}()

    14 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal{closure}()

    15 /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php(497): Fiber->resume()

    16 /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php(553): Revolt\EventLoop\Internal\AbstractDriver->invokeCallbacks()

    17 [internal function]: Revolt\EventLoop\Internal\AbstractDriver->Revolt\EventLoop\Internal{closure}()

    18 /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Internal/AbstractDriver.php(113): Fiber->start()

    19 /home/web/webman-co/vendor/revolt/event-loop/src/EventLoop/Driver/EventDriver.php(119): Revolt\EventLoop\Internal\AbstractDriver->run()

    20 /home/web/webman-co/vendor/workerman/workerman/src/Events/Revolt.php(88): Revolt\EventLoop\Driver\EventDriver->run()

    21 /home/web/webman-co/vendor/workerman/workerman/src/Worker.php(1594): Workerman\Events\Revolt->run()

    22 /home/web/webman-co/vendor/workerman/workerman/src/Worker.php(1394): Workerman\Worker::forkOneWorkerForLinux()

    23 /home/web/webman-co/vendor/workerman/workerman/src/Worker.php(1368): Workerman\Worker::forkWorkersForLinux()

    24 /home/web/webman-co/vendor/workerman/workerman/src/Worker.php(572): Workerman\Worker::forkWorkers()

    25 /home/web/webman-co/vendor/workerman/webman-framework/src/support/App.php(131): Workerman\Worker::runAll()

    26 /home/web/webman-co/start.php(4): support\App::run()

    27 {main}

    worker[webman:9729] exit with status 64000

    这样运行之后会报错。

  • kspade 2023-12-21

    guzzle 万能

  • walkor 2023-12-21

    Must call suspend() before calling resume() 意思是你必须在resume()被调用之前调用suspend(),你检查下你的代码。
    如果搞不懂就用curl_multi

  • 无法埋名 2024-03-14

    guzzle 更方便

🔝