PHPは混合要求の同時実行を実現する


インタフェーステストでは、単一インタフェースレベルの順方向テストと異常テストだけでなく、同じ情報を同時に注文または同時支払いを作成し、同じクーポンテンプレートidを同時に照会し、同じユーザーを同時に更新するなど、いくつかのインタフェースに対して同時要求テストを行う必要があります.便宜上、PHPのcurlで同時リクエスト方法をカプセル化しました.
POST要求の同時
/**
 * POST      
 * @param $requestBodyArr ,     json     
 * @param $category ,    transfers,charges, v1     url
 * @return array
 * @throws \Exception
 */
public static function apiMultiCreate($category, $requestBodyArr)
{
    $handles = $data = $headers = array();
    $threadCount = count($requestBodyArr);

    //create the multiple cURL handle
    $mh = curl_multi_init();
    
    //                    。
    $active = null;

    for ($i = 0; $i < $threadCount; $i++) {
        $handles[$i] = curl_init();
        curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, 1);//  TRUE curl_exec()       ,       
        curl_setopt($handles[$i], CURLOPT_POST, 1);//post    
        if (is_array($category)) {
            $url = '/v1/' . $category[$i];
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
        } else {
            $url = '/v1/' . $category;
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//Pingpp::$apiBaseUrl    https://host
        }
        if (!is_array($requestBodyArr)) {
            $data[$i] = $requestBodyArr;//$arr     
        } else {
            $data[$i] = $requestBodyArr[$i];//$arr     
        }

        $request_TimeStamp = time();

        $headers[$i] = array('Authorization: Bearer ' . Pingpp::$apiKey,
            'Content-type: application/json;charset=UTF-8',
            'Pingplusplus-Request-Timestamp:' . $request_TimeStamp,
            'Pingplusplus-Signature: ' . Util::genSignatureForAPI(json_encode($data[$i]), $url, $request_TimeStamp)
        );
        curl_setopt($handles[$i], CURLOPT_HTTPHEADER, array_filter($headers[$i]));

        curl_setopt($handles[$i], CURLOPT_POSTFIELDS, json_encode($data[$i]));

        // curl           curl  
        curl_multi_add_handle($mh, $handles[$i]);
    }

    //execute the handles
    //curl_multi_exec —      cURL       
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active && $mrc == CURLM_OK) {
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    $responseArr = array();

    /**
     * curl_multi_getcontent-     CURLOPT_RETURNTRANSFER,            
     * curl_multi_remove_handle-  curl               
     */
    for ($i = 0; $i < $threadCount; $i++) {
        $info = curl_getinfo($handles[$i]);
        print_r("Took " . $info['total_time'] . " seconds to send a request to " . urldecode($info['url']) . " and http status code is " . $info['http_code'] . "
"); print_r('Thread#' . $i . " content is
" . curl_multi_getcontent($handles[$i]) . "
"); curl_multi_remove_handle($mh, $handles[$i]); $responseArr[] = curl_multi_getcontent($handles[$i]) . "
";// string curl_close($handles[$i]); } // cURL curl_multi_close($mh); return $responseArr; }

コードのUtil::genSignatureForAPIは署名に使用され、無視することができます.
同じ内容をテストして注文書を作成する必要がある場合は、次のように操作できます.
$threads = 2;
$data = array();
for ($i = 0; $i < $threads; $i++) {
    $data[$i] = array(
        "app" => $this->appId,
        "uid" => 'user007', //email、   、UID     (      )
        "merchant_order_no" => Util::genString(20),//     
        "amount" => 10,
        "currency" => 'cny,
        "client_ip" => '127.0.0.1',
        "subject" => '    ', //     
        "body" => $this->body, //       
    );
}
HttpRequest::apiMultiCreate("orders", $data);

PUT要求の同時
/**
 * PUT      
 * @param $requestBodyArr ,     json     
 * @param $category ,   transfers,charges
 * @return array
 * @throws \Exception
 */
public static function apiMultiPut($category, $requestBodyArr)
{
    $handles = $data = $headers = array();

    $threadCount = count($requestBodyArr);

    //create the multiple cURL handle
    $mh = curl_multi_init();

    //                    。
    $active = null;
    for ($i = 0; $i < $threadCount; $i++) {
        $handles[$i] = curl_init();
        curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, 1);//  TRUE curl_exec()       ,       
        curl_setopt($handles[$i], CURLOPT_CUSTOMREQUEST, 'PUT');//put    
        if (is_array($category)) {
            $url = '/v1/' . $category[$i];
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
        } else {
            $url = '/v1/' . $category;
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
        }

        if (!is_array($requestBodyArr)){
            $data[$i] = $requestBodyArr;//$arr     
        } else {
            $data[$i] = $requestBodyArr[$i];//$arr     
        }
        $request_TimeStamp = time();

        $headers[$i] = array('Authorization: Bearer ' . Pingpp::$apiKey,
            'Content-type: application/json;charset=UTF-8',
            'Pingplusplus-Request-Timestamp:' . $request_TimeStamp,
            'Pingplusplus-Signature: ' . Util::genSignatureForAPI(json_encode($data[$i]), $url, $request_TimeStamp)
        );
        curl_setopt($handles[$i], CURLOPT_HTTPHEADER, array_filter($headers[$i]));
        curl_setopt($handles[$i], CURLOPT_POSTFIELDS, json_encode($data[$i]));

        // curl           curl  
        curl_multi_add_handle($mh, $handles[$i]);
    }

    //execute the handles
    //curl_multi_exec —      cURL       
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active && $mrc == CURLM_OK) {
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    $responseArr = array();

    /**
     * curl_multi_getcontent-     CURLOPT_RETURNTRANSFER,            
     * curl_multi_remove_handle-  curl               
     */
    for ($i = 0; $i < $threadCount; $i++) {
        $info = curl_getinfo($handles[$i]);
        print_r("Took " . $info['total_time'] . " seconds to send a request to " . urldecode($info['url']) . " and http status code is " . $info['http_code'] . "
"); print_r('Thread#' . $i . " content is
" . curl_multi_getcontent($handles[$i]) . "
"); curl_multi_remove_handle($mh, $handles[$i]); $responseArr[] = curl_multi_getcontent($handles[$i]) . "
";// string curl_close($handles[$i]); } // cURL curl_multi_close($mh); return $responseArr; }

たとえば、新しく作成されたユーザーidをテストする必要があるシーンがあります.更新するか無効にするか、次のように同時操作を作成できます.
$user_id = "user1568020989";
$data = array(
    array(
        "address" => $this->address . "update", //     
    ),
    array(
        "disabled" => true //    。      ,          。
    )
);
HttpRequest::apiMultiPut("apps/" . $this->appId . "/users/" . $user_id, $data);

GETリクエストの同時実行
/**
 *    id   
 * @param $category ,    transfers,charges
 * @param $idArr
 * @return array
 * @throws \Exception
 */
public static function apiMultiGet($category, $idArr)
{
    $handles = $headers = array();

    //create the multiple cURL handle
    $mh = curl_multi_init();

    //                    。
    $active = null;
    $threadCount = count($idArr);

    for ($i = 0; $i < $threadCount; $i++) {
        $handles[$i] = curl_init();
        curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, 1);//  TRUE curl_exec()       ,       
        if (is_array($category)) {
            $url = '/v1/' . $category[$i] . '/' . $idArr[$i];
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
        } else {
            $url = '/v1/' . $category . '/' . $idArr[$i];
            curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
        }
        $request_TimeStamp = time();
        $headers[$i] = array('Authorization: Bearer ' . Pingpp::$apiKey,
            'Content-type: application/json;charset=UTF-8',
            'Pingplusplus-Request-Timestamp:' . $request_TimeStamp,
            'Pingplusplus-Signature: ' . Util::genSignatureForAPI(null, $url, $request_TimeStamp)
        );
        curl_setopt($handles[$i], CURLOPT_HTTPHEADER, array_filter($headers[$i]));

        // curl           curl  
        curl_multi_add_handle($mh, $handles[$i]);
    }
    //execute the handles
    //curl_multi_exec —      cURL       
    do {
        $mrc = curl_multi_exec($mh, $active);
    } while ($mrc == CURLM_CALL_MULTI_PERFORM);

    while ($active && $mrc == CURLM_OK) {
        if (curl_multi_select($mh) != -1) {
            do {
                $mrc = curl_multi_exec($mh, $active);
            } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        }
    }
    $responseArr = array();
    /**
     * curl_multi_getcontent-     CURLOPT_RETURNTRANSFER,            
     * curl_multi_remove_handle-  curl               
     */
    for ($i = 0; $i < $threadCount; $i++) {
        $info = curl_getinfo($handles[$i]);
        print_r("Took " . $info['total_time'] . " seconds to send a request to " . urldecode($info['url']) . " and http status code is " . $info['http_code'] . "
"); print_r("Took " . $info['namelookup_time'] . " seconds -- (namelookup_time)
"); print_r("Took " . $info['connect_time'] . " seconds -- (connect_time) ( )
"); print_r("Took " . $info['pretransfer_time'] . " seconds -- (pretransfer_time)
"); print_r("Took " . $info['starttransfer_time'] . " seconds -- (starttransfer_time) curl
"); print_r('Thread#' . $i . " content is
" . curl_multi_getcontent($handles[$i]) . "
"); curl_multi_remove_handle($mh, $handles[$i]); $responseArr[] = curl_multi_getcontent($handles[$i]) . "
";// string curl_close($handles[$i]); } // cURL curl_multi_close($mh); return $responseArr; }

支払われていない受注idを同時に問い合わせる必要がある場合があります.そのパフォーマンスをざっと見てみましょう.例えば、order idなどです.問い合わせると、多くの要求が行われます.かつてのパフォーマンススロットはこのように発見されました(同時に要求した後、ログを見て最も多くの要求を見つけ、最適化可能なポイントを発見しました).異なるid同時クエリーである場合もあります.次のようにして、同時idクエリースクリプトを作成できます.
$transferArr = array(
    'tr_nLyrrHvjTO0880y58Oub5OSS',
    'tr_C0Kyf15CS8u5OiT8y9LS4i50'
);
HttpRequest::apiMultiGet( "transfers", $transferArr);

ここを見て、なぜ自分で同時リクエストの方法を書くのかと思うかもしれません.性能テストツールでテストしたほうがいいのではないでしょうか.もちろん、あなたの考えは正しいですが、多くの場合、性能テストは特定のシーンと需要に対してだけで、すべてのインタフェースが必要ではありません.テストの時間が通常緊張していることを知っておく必要があります.多くの場合、インタフェースの性能をざっと理解すると、テストの経済性が高くなります.
ハイブリッド要求の同時実行
もちろん、PUTインタフェースの同時性を見て、要求を混合して同時性を求める場面を思い浮かべる同僚もいるに違いない.確かに、このようなシーンは多くないが、少なくはない.最近、私たちは新しい需要を開発しました.1つの注文は作成後、次の3つの操作があり、1つの成功しかありません.
  • payインタフェースを呼び出して支払いを完了
  • キャンセルインタフェースを呼び出して注文をキャンセル
  • 更新インタフェースを呼び出し、注文の記述情報、金額などを更新
  • このうち1はPOSTリクエストであり,2と3はPUTリクエストであり,この3つのリクエストが1つしか成功しないことを要求しているが,上記の単一リクエストメソッドの同時化はこのようなテストシーンを満たすことができないことは明らかである.そこでハイブリッドコンカレントを思いつき、(これまでLoadRunnerで性能テストをしていたときにハイブリッドシーンのテストをしたことがあり、それに感銘しました)、POST/PUT/GET/DELETEリクエストをブレンドしてコンカレントテストをすることです.コンカレント要求をブレンドする方法は、次のとおりです.
    /**
     *    API       , curl_multi           ,              ,    512,   CURL    ,           。                 ,       。
     *    post   put    ,  $requestBodyArr      ,  ("POST", "charges",$requestBodyArr)
     *    post/put/get/delete      ,  $methods, $urls, $requestBodyArr       ,        
     *   get    delete     id  ,       id    url     ,  ("GET", $urls   )
     *   get    delete     id  ,$requestBodyArr   null,   $threadCount       ,  ("GET", "charges/CHARGE_ID",null,100)
     * @param $urls ,   transfers,charges
     * @param string $methods ,    post,put,get,delete
     * @param null $requestBodyArr ,     json     ,Get/Delete         null
     * @param null $threadCount
     * @return array
     * @throws \Exception
     */
    public static function apiMultiRequests($urls, $methods="GET", $requestBodyArr = null, $threadCount = null)
    {
        $handles = $data = $headers = array();
    
        //create the multiple cURL handle
        $mh = curl_multi_init();
    
        if ($threadCount !== null && is_array($requestBodyArr)) {
            assert($threadCount == count($requestBodyArr), "       body         !");
        } elseif ($threadCount === null && is_array($requestBodyArr)) {//      body      ,        charge     body
            $threadCount = count($requestBodyArr);
        } elseif ($threadCount === null && is_array($urls)) {//    url      ,      id   ,     url      
            $threadCount = count($urls);
        }
    
        for ($i = 0; $i < $threadCount; $i++) {
            $handles[$i] = curl_init();
            curl_setopt($handles[$i], CURLOPT_RETURNTRANSFER, 1);//  TRUE curl_exec()       ,       
            //             ,    CURLOPT_TIMEOUT       ,                 ,        。
            curl_setopt($handles[$i], CURLOPT_TIMEOUT, 60); //   cURL          ,    60 s
            if (is_array($methods)) {
                curl_setopt($handles[$i], CURLOPT_CUSTOMREQUEST, strtoupper($methods[$i]));//    
            } else {
                curl_setopt($handles[$i], CURLOPT_CUSTOMREQUEST, strtoupper($methods));//    
            }
            if (is_array($urls)) {
                $url = '/v1/' . $urls[$i];
                curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
            } else {
                $url = '/v1/' . $urls;
                curl_setopt($handles[$i], CURLOPT_URL, Pingpp::$apiBaseUrl . $url);//     URL
            }
    
            is_array($requestBodyArr) ? $data[$i] = $requestBodyArr[$i] : $data[$i] = $requestBodyArr;
    
            is_array($methods) ? $method = strtolower($methods[$i]) : $method = strtolower($methods);
    
            $request_TimeStamp = time();
            $signature = null;
            if ($method === 'post' || $method === 'put') {
                $signature = Util::genSignatureForAPI(json_encode($data[$i]), $url, $request_TimeStamp);
            } else {
                if (null != $requestBodyArr && is_array($requestBodyArr)) {
                    $signature = Util::genSignatureForAPI(null, $url . http_build_query($data[$i]), $request_TimeStamp);
                } elseif (null != $requestBodyArr && !is_array($requestBodyArr)) {
                    //$requestBodyArr,       
                    $signature = Util::genSignatureForAPI(null, $url . $requestBodyArr, $request_TimeStamp);
                } elseif (null == $requestBodyArr) {
                    $signature = Util::genSignatureForAPI(null, $url, $request_TimeStamp);
                }
            }
    
            $headers[$i] = array('Authorization: Bearer ' . Pingpp::$apiKey,
                'Content-type: application/json;charset=UTF-8',
                'Pingplusplus-Request-Timestamp:' . $request_TimeStamp,
                'Pingplusplus-Signature: ' . $signature,
            );
            curl_setopt($handles[$i], CURLOPT_HTTPHEADER, array_filter($headers[$i]));
    
            if ($method === 'post' || $method === 'put') {
                curl_setopt($handles[$i], CURLOPT_POSTFIELDS, json_encode($data[$i]));
            }
    
            //  curl             curl   
            curl_multi_add_handle($mh, $handles[$i]);
        }
    
        //                    。
        $active = null;
    
        //curl_multi_exec —      cURL       
        //            OK,CURLM_CALL_MULTI_PERFORM     -1
        do {
            //     $active         ,$mrc     ,    0,    -1
            $mrc = curl_multi_exec($mh, $active);
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
    
        //          ,       OK,CURLM_OK      0
        while ($active && $mrc == CURLM_OK) {
            //               ,  5ms     ,     CPU,      
            usleep(10000);
            if (curl_multi_select($mh) != -1) {
                do {
                    $mrc = curl_multi_exec($mh, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        $responseArr = array();
    
        //       
        foreach ($handles as $index =>$ch) {
            $info = curl_getinfo($ch);
            print_r("Took " . $info['total_time'] . " seconds to send a request to " . urldecode($info['url']) . " and http status code is " . $info['http_code'] . "
    "); print_r('Thread#' . $index . " content is
    " . curl_multi_getcontent($ch) . "
    ");//curl_multi_getcontent- CURLOPT_RETURNTRANSFER, $responseArr[$index] = curl_multi_getcontent($ch) . "
    ";// string curl_multi_remove_handle($mh, $ch);// curl curl_close($ch); } // cURL curl_multi_close($mh); return $responseArr; }

    私たちの新しいニーズに対して、私が作成したテストの同時スクリプトは以下の通りです.ここではコードの注釈に注意する必要があります.重複する内容も必ず省略してはいけません.データの一つ一つの対応をしなければなりません.
    $orderId = '2012003060000123456';
    
    $urls = array(
        "orders/" . $orderId . "/pay",
        "orders/" . $orderId,
        "orders/" . $orderId,//       ,   $data         
    );
    $methods = array(
        "POST",
        "PUT",
        "PUT",//       ,   $data         
    );
    $data = array(
        array(
            "charge_amount" => 10,//required, integer[0, 1000000000],       
            "channel" => 'alipay'
        ),
        array(
            "amount" => 1
        ),
        array(
            "status" => "canceled"
        )
    );
    HttpRequest::apiMultiRequests($urls, $methods, $data);

    その後、ハイブリッド同時要求を使用する方法は、リストクエリの同時実行をうまく実現することができ、スクリプトの作成方法は以下の通りであることが分かった.
    $path = "apps/" . Pingpp::$appId . "/users?";
    $data = array(
        $path . http_build_query(array(
            "page" => 1,
            "per_page" => 2,
            "disabled" => false
        )),
        $path . http_build_query(array(
            "page" => 2,
            "per_page" => 2,
            "disabled" => true
        ))
    );
    
    HttpRequest::apiMultiRequests($data, 'GET');

    その中のcurlについてはmulti_* いくつかの関数の解釈、私はここで怠けて、文章PHPを参考にして同時要求を実現してください.説明はまだはっきりしています.
    もちろん,ハイブリッドリクエストの同時メソッドがあれば,従来の単一メソッドの同時リクエストも不要であり,完全に代替できる.