前言,最近接手的项目中需要跨网络调用其他项目服务的API。由于网络中存在各种复杂的因素,导致curl请求偶尔出现下面错误。
Failed connect to www.xxx.com:80; Connection timed out
做了一下几点尝试:
-
抓包分析,也没有分析出个所以然。
-
以为是尝试连接的等待时间太小了,于是通过调整增大CURLOPT_CONNECTTIMEOUT参数,发现也无济于事。
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); //在尝试连接时等待的秒数
重试机制 由于之前的工作经历中,经常遇到各种偶发无法连接服务的情况如mysql等等,都是通过增加重试连接的方式解决的。
于是根据过往经验,干脆写了个curl请求重试机制,轻松解决了问题。特此分享一下:
$ch = curl_init($url);
//curl_setopt($ch, CURLINFO_HEADER_OUT, TRUE);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $postdata);
//curl_setopt($ch, CURLOPT_HTTPHEADER, Array("Content-type:application/json;charset=utf-8"));
curl_setopt($ch, CURLOPT_TIMEOUT, 1800);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); //在尝试连接时等待的秒数
if (!empty($options)) {
curl_setopt_array($ch, $options);
}
$data = curl_exec($ch);
$curlErrno = curl_errno($ch);
//curl增加重试机制
$retryTimes=0;
$maxRetryTimes=10;
if($curlErrno)
{
for($retryTimes=0;$retryTimes<$maxRetryTimes;$retryTimes++)
{
$retryTimes++;
$data = curl_exec($ch);
$curlErrno = curl_errno($ch);
if(!$curlErrno)
{
error_log("CURL重试{$retryTimes}次后成功".addslashes(json_encode(curl_getinfo($ch), JSON_UNESCAPED_UNICODE)));
break;
}
sleep(1);
}
}
//错误监控告警
if ($curlErrno) {
//curl请求异常,往上层抛出异常信息
$errorMessage = 'CURL请求出错了!';
$errorMessage .= '【地址】:' . $url . '。';
$errorMessage .= '【方法】: GET。';
$errorMessage .= '【参数】:' . addslashes(json_encode($postdata, JSON_UNESCAPED_UNICODE)) . '。';
$errorMessage .= '【错误信息】:' . curl_error($ch) . "({$curlErrno}).";
$errorMessage .= '【CURL_INFO】:' . addslashes(json_encode(curl_getinfo($ch), JSON_UNESCAPED_UNICODE)) . '。';
#增加调用信息
$backtrace = "";
$backtraceList = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
foreach ($backtraceList as $v) {
$fileName = $v['file'] ?? '';
$lineNo = $v['line'] ?? 0;
$className = $v['class'] ?? '';
$functionName = $v['function'] ?? '';
$backtrace .= "{$fileName} - {$lineNo} - {$className}::{$functionName}()" . '。';
}
$errorMessage .= '【调用】:' . $backtrace;
$errorMessage .= '【重试次数】:' . $retryTimes;
$errorMessage .= '【处理服务器】:' . gethostname();
$errorMessage = str_replace(array("\r\n", "\r", "\n", " "), '', $errorMessage);
//告警通知
qywx_robot_error_notice($errorMessage);
unset($errorMessage, $backtrace, $backtraceList);
//throw new \Exception(curl_error($ch),$errorNo);
//return false;
}
curl_close($ch);
return $data;