需求背景
在公司开发这么一个需求,每天三次定时催付待客服催付状态的订单,设定每天15、16、17点三次执行job任务来给一批订单打电话催付,需要三个时间点都把待客服催付的订单拨打一遍电话,根据数据组统计,大概每天需要催付的订单数量在6000左右,对接第三方电话呼叫业务,拿到订单信息来呼叫。
测试状态
拿500个订单手动执行第一波测试,发现500个订单催付完毕需要30多分钟,那么6000个订单按照需求催付时间点是完全不够的,半小时500个,一小时最多1000个。
初步排查,是由于使用php curl请求导致每一次遍历的请求时间慢,由于curl请求最短的time时间耗时是1秒,那么一小时3600秒也是不够呼完这6000单。
解决方案
一、在遍历循环的时候把每次请求的量丢入消费系统(队列),然后根据开启多个消费者来消费这些(上线迫在眉睫,来不及)
二、有没有类似curl更快的方案,发现了fsockopen,按照使用方法配置完500个订单,遍历完成只需要18秒。
需求代码
/** * 通过订单信息组装呼叫信息 * @param array $order * @return array */ private function getCallInfoByOrder ($order = []) { $order_ext = OrderExt::model()->getPrimary($order['order_id']); $point = (isset($order_ext['app_ver'])&&version_compare($order_ext['app_ver'],Tools::PRICE_COMPARE_VERSION,">=")); $pay_detail = json_decode($order['pay_detail'], true); $call_text = OrderPayRemainService::organizeHeLiText($order['create_time'], $pay_detail['cash'], $point); return ['phone' => $order['phone'], 'call_text' => $call_text]; } //开始呼叫 private function toCall ($call_info) { $params = $this->formatGetParams($call_info); EdjLog::info(__METHOD__ .'he li to call info' . json_encode($call_info)); $this->doCurlGetRequest(self::CALL_API_URL, $params, $call_info); } //拨号请求 private function doCurlGetRequest($url, $data = [], $call_info = []){ if($url == "" || empty($data)){ return false; } $response = $this->fsockopen_request($url,$data); // $response_arr = explode("\r\n", $response); // // if (in_array(self::TOOKEN_INVALID,$response_arr)) { // EdjLog::info(__METHOD__ .'he li accessToken expire' . json_encode($call_info)); // $this->redis->del(self::ACCESS_TONEN_CACHE_KEY); // $this->toCall($call_info); // } $response_arr = explode("\r\n", $response); return true; } private function fsockopen_request($URL,$data, $referrer="") { EdjLog::info(__METHOD__ .'he li request url:' . $URL.'-data:'.json_encode($data)); $URL_Info = parse_url($URL); foreach($data as $key=>$value) $values[] = "$key=" . urlencode($value); $data_string = implode("&",$values); if(!isset($URL_Info["port"])) $URL_Info["port"] = 80; $request = ''; $request.="POST ".$URL_Info["path"]." HTTP/1.1\n"; $request.="Host: ".$URL_Info["host"]."\n"; $request.="Referer: $referrer\n"; $request.="Content-type: application/x-www-form-urlencoded\n"; $request.="Content-length: ".strlen($data_string)."\n"; $request.="Connection: close\n"; $request.="\n"; $request.=$data_string."\n"; $fp = fsockopen($URL_Info["host"],$URL_Info["port"],$errno, $errstr); if (!$fp) { EdjLog::info('socket_open error:'.json_encode($data). "Error: $errstr ($errno)"); } fputs($fp, $request); // $result = ''; // while(!feof($fp)) { // // $response = fgets($fp, 512); // if (!is_numeric(trim($response))) { // continue; // } // $result.= $response; // } fclose($fp); // return $result; }
完整说明
<?php $srv_ip = '192.168.1.5';//你的目标服务地址. $srv_port = 80;//端口 $url = 'http://localhost/fsock.php'; //接收你post的URL具体地址 $fp = ''; $errno = 0;//错误处理 $errstr = '';//错误处理 $timeout = 10;//多久没有连上就中断 $post_str = "username=demo&password=hahaha";//要提交的内容. //打开网络的 Socket 链接。 $fp = fsockopen($srv_ip,$srv_port,$errno,$errstr,$timeout); if (!$fp){ echo('fp fail'); } $content_length = strlen($post_str); $post_header = "POST $url HTTP/1.1\r\n"; $post_header .= "Content-Type: application/x-www-form-urlencoded\r\n"; $post_header .= "User-Agent: MSIE\r\n"; $post_header .= "Host: ".$srv_ip."\r\n"; $post_header .= "Content-Length: ".$content_length."\r\n"; $post_header .= "Connection: close\r\n\r\n"; $post_header .= $post_str."\r\n\r\n"; fwrite($fp,$post_header); $inheader = 1; while(!feof($fp)){//测试文件指针是否到了文件结束的位置 $line = fgets($fp,1024); //去掉请求包的头信息 if ($inheader && ($line == "\n" || $line == "\r\n")) { $inheader = 0; } if ($inheader == 0) { echo $line; } } fclose($fp); unset ($line); ?>
其它博文
https://blog.csdn.net/navioo/article/details/82771663