1

RESTful API からデータを取得するクライアントがあります。

私の要件は、指定された順序で API を使用することです。優先するものに到達できない場合は、次のものを使用する必要があります。

この理由は、ある種の負荷分散です (API は、アプリケーションでさらに使用される同じサーバーを指す生成されたリンクを返します)。

そのため、最初に APIS の URL を目的の順序で生成します。例:

  1. http://first-api.example.com/method?param=値

  2. http://first-api.example.com/method?param=値

  3. http://first-api.example.com/method?param=値

これで、これらの URL をループして、最初に成功した API から結果を返すことができます。ただし、時間がかかりすぎます。API が応答するまでにかなりの時間がかかる場合があります (最大数秒)。それが加算されると、プロセス全体でタイムアウトが発生する可能性があります。

したがって、許容できる時間 (すべてがうまくいけば API が正常に応答するまで) 待ちたいと思います。その時間に達したら、2 番目の API を要求しますが、応答に時間がかかる場合に備えて最初の API も待ちます。

しばらくすると、3 番目の API もプールに追加されます。

すべての API を最初からすぐに要求したくはありません。すべての要求は、可能であれば回避したいかなりの作業負荷を意味するからです。

最初に応答した API が仕事を獲得します。

そこで、curl_multi_exec を使用して上記のことを行う単純なクラスを作成しました。

2 つのメソッドが渡されます。一度に 1 つずつ API URL を配信するメソッド、レスポンスが成功かどうかを評価するメソッド、プロセスを開始して最初の成功したレスポンスを返すパブリック メソッド。

テストクラスを含むクラスは次のようになります。

<?
class MultiGetProxy
{
    private $url_src = null;
    private $response_evaluator = null;
    private $total_start = null;
    private $handles = array();
    private $mh = null;
    public function setUrlSource($callback)
    {
        $this->url_src = $callback;
        return $this;
    }
    public function setResponseEvaulator($callback)
    {
        $this->response_evaluator = $callback;
        return $this;
    }
    private function addNewHandle()
    {
        echo "adding new handle ... \n";
        $ch = curl_init();

        curl_setopt($ch, CURLOPT_URL, call_user_func($this->url_src));
        ;
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

        $this->handles[] = $ch;
        curl_multi_add_handle($this->mh, $this->handles[count($this->handles) - 1]);
    }
    private function getRunning()
    {
        curl_multi_exec($this->mh, $running);
        return $running;

    }
    private function getResponse()
    {
        foreach ($this->handles as &$handle) {
            if ($content = curl_multi_getcontent($handle)) {
                echo "one handle delivered content...\n";
                if (!call_user_func($this->response_evaluator, $content)) {
                    echo "evluater said it was an error...\n";
                    curl_multi_remove_handle($this->mh, $handle);
                    if (!$this->getRunning())
                        $this->addNewHandle();
                    return false;
                }
                echo "content looks fine\n";
                var_dump($content);
                return $content;

            }
        }
    }
    public function exec()
    {
        $this->total_start = microtime(true);
        $this->mh          = curl_multi_init();
        $response          = false;
        $lastadd           = 0;
        while (!$response) {
            if (microtime(true) > $lastadd + 5.0) {
                $this->addNewHandle();
                $lastadd = microtime(true);
                curl_multi_exec($this->mh, $running);
            }
            usleep(100000);
            curl_multi_exec($this->mh, $running);
            $response = $this->getResponse();
        }
        echo "do loop done here is some info:\n";
        var_dump(curl_multi_info_read($mh));
        // Handles schliessen
        foreach ($handles as &$handle) {
            curl_close($handle);
            curl_multi_remove_handle($mh, $handle);
        }
        curl_multi_close($mh);
        $total_run = microtime(true) - $total_start;
        echo "completed in: $total_run \n";
        echo "response is:\n";
        return $response;

    }
    public function getInfo()
    {
        $info               = array();
        $info["total_time"] = $this->total_run;
        $info["start"]      = $this->total_start;

    }

}


class Test
{
    function isvalid($data)
    {
        $data = json_decode($data);
        if ($data->status > 500) { // error
            return false;
        }
        return true;
    }
    function getUrl()
    {
        static $urls = array();
        if (empty($urls)) {
            $urls[] = 'http://test.example.com/sleep.php?s=8&status=200&message=first';
            $urls[] = 'http://test.example.com/sleep.php?s=2&status=503&message=2nd';
            $urls[] = 'http://test.example.com/sleep.php?s=2&status=200&message=3rd';
            $urls[] = 'http://test.example.com/sleep.php?s=21';
            $urls[] = 'http://test.example.com/sleep.php?s=22';
            $urls[] = 'http://test.example.com/sleep.php?s=23';
            $urls[] = 'http://test.example.como/sleep.php?s=25';
            $urls[] = 'http://test.example.com/sleep.php?s=25';
        }

        $url = array_shift($urls);
        echo "returning $url \n";
        return $url;



    }
    public function test()
    {
        $mgp = new MultiGetProxy();
        $mgp->setUrlSource(array(
            $this,
            "getUrl"
        ));
        $mgp->setResponseEvaulator(array(
            $this,
            "isvalid"
        ));
        $result = $mgp->exec();
        echo $result;
    }
}


$test = new Test();
$test->test();

私の実際の質問は、「コメントのリクエスト」です。なぜなら、私はそのようなことを実際に扱ったことがないからです。

高負荷環境でデプロイする必要があり、多くのリクエストが発生します。

また、睡眠時間は適切ですか?

TCP 接続の問題が発生する可能性はありますか?

申し訳ありませんが、これ以上具体的に言えません。私は非常に具体的で個別的な問題の解決策を見つけようとしましたが、これが私が思いついたものです.

4

1 に答える 1

1

API とコードの間にあるファサードを構築します。ファサードは、使用する API を決定する作業を行います。ファサードには、API サーバーを表す HTTP オブジェクトがあります。コードはファサードとの間でデータを送受信するだけで、使用しているサーバーは気にしません。

次に、ファサードのパフォーマンスの向上に取り組みます。予想した時間の後にデータを返すモック オブジェクトでテストします。

interface Proxy {
  function exec();
}

abstract class AbstractProxy {

  public $response;

  public function hasResponse() {
    return !empty($this->response);
  }

}

class SingleProxy extends AbstractProxy implements Proxy {

  // the real code for connecting to the API

}

class MultiProxy extends AbstractProxy implements Proxy {

  public $singleProxies = array();
  public $delayTime = 1000000;

  public function addProxy(Proxy $proxy) {
    $this->singleProxies[] = $proxy;
  }

  public function exec() {
     foreach($singleProxies as $proxy) {
        $proxy->exec();
        usleep($this->delayTime);
        if($proxy->hasResponse() {
          return $proxy;
        } else {
          $proxy->cancel();
        }
     }
  }

}

class SingleProxyStub extends AbstractProxy implements Proxy {

  public $timeToRespond = 0;

  public function exec() {
    usleep($this->timeToRespond);
    $this->response = 'response';
  }

}

class ProxyTest() {

   public function runTest() {

     $slowProxy = new SingleProxyStub;
     $slowProxy->timeToResponse = 2000000; // 2 seconds

     $fastProxy = new SingleProxyStub;
     $fastProxy->timeToResponse = 500000; // 0.5 seconds

     $MultiProxy = new MultiProxy;
     $MultiProxy->singleProxies = array($slowProxy, $fastProxy);

     $startTime = microtime(true);
     $MultiProxy->exec();
     $endTime = microtime(false);
     log($startTime, $endTime);

   }

}
于 2012-05-20T11:48:43.410 に答える