在一百万循环中使用curl,出现transfer closed with ocsding read data remaining

业务大概是这样:一百万数据,循环一百万的数据,在循环的时候调用curl。

代码:

// 开始循环一百万的数据
foreach($data as $value){
    // 调用curl
    tocurl($url, $header, $value);
}
// curl函数
function tocurl($url, $header, $content){
    $ch = curl_init();
    if(substr($url,0,5)=='https'){
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // 跳过证书检查
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, true);  // 从证书中检查SSL加密算法是否存在
    }
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    if($content) {
        curl_setopt($ch, CURLOPT_POST, true);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $content);
    }
    $response = curl_exec($ch);
    if($error=curl_error($ch)){
        die($error);
    }
    curl_close($ch);
    return $response;
}

尝试:

1. 开始没有设置 CURLOPT_TIMEOUT 和 CURLOPT_CONNECTTIMEOUT,然后设置为:
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
然而还是出现 transfer closed with ocsding read data remaining 这个问题
2. 通过 stack overflow 搜索找到一个方法,回答是这样的:缺少 Content-Length 这个 header,
然后设置 POST 的数据 json_encode 通过 strlen() 计算其字数,然后设置 Content-lenth,
在去尝试运行,还是出现 transfer closed with ocsding read data remaining 这个问题
3. 后面以为是数据量太大,把这一百万数据准备分成了十个线程去跑,在测试跑一个线程(10万数据)的时候也出现了 transfer closed with ocsding read data remaining 这个问题,看起来像是curl的问题。

php的版本是5.3,这个问题如何解决?还有谢谢各位的回答。

阅读 4.2k
2 个回答

这里你循环十万次,就是发起了十万次的请求,这相当于一个串联,是效率极低的行为,你可以改为并行,在curl中循环,发起一次请求就可以。下面是我封装的一个方法:

/*
*  $url  string 请求地址
*  $data array  你的数据
*/
 function multi_curl_post($url, $data)
    {
        $ch_list = array();
        $multi_ch = curl_multi_init();
        foreach ($data as $key => $value){ //这里开始循环处理你的数据
            $ch_list[$key] = curl_init($url);
            curl_setopt($ch_list[$key], CURLOPT_RETURNTRANSFER, 1);
            curl_setopt($ch_list[$key], CURLOPT_POSTFIELDS, $value);
            curl_multi_add_handle($multi_ch, $ch_list[$key]);
        }
        $active = null;
        do {
            $mrc = curl_multi_exec($multi_ch, $active); //处理在栈中的每一个句柄。无论该句柄需要读取或写入数据都可调用此方法。
        } while ($mrc == CURLM_CALL_MULTI_PERFORM);
        //Note:
        //该函数仅返回关于整个批处理栈相关的错误。即使返回 CURLM_OK 时单个传输仍可能有问题。

        while ($active && $mrc == CURLM_OK) {
            if (curl_multi_select($multi_ch) != -1) { //阻塞直到cURL批处理连接中有活动连接。
                do {
                    $mrc = curl_multi_exec($multi_ch, $active);
                } while ($mrc == CURLM_CALL_MULTI_PERFORM);
            }
        }
        $result = [];
        //获取http返回的结果
        foreach ($ch_list as $k => $ch) {
            $result[$k] = curl_multi_getcontent($ch);
            curl_multi_remove_handle($multi_ch, $ch);
            curl_close($ch);
        }
        curl_multi_close($multi_ch);
        return $result;
    }

为何不用 curl_multi_init

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题