zoukankan      html  css  js  c++  java
  • php 使用fsockopen 发送http请求

    需求背景

    在公司开发这么一个需求,每天三次定时催付待客服催付状态的订单,设定每天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("
    ", $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("
    ", $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 ";
    $request.="Host: ".$URL_Info["host"]." ";
    $request.="Referer: $referrer ";
    $request.="Content-type: application/x-www-form-urlencoded ";
    $request.="Content-length: ".strlen($data_string)." ";
    $request.="Connection: close ";
    $request.=" ";
    $request.=$data_string." ";

    $fp = fsockopen($URL_Info["host"],$URL_Info["port"],$errno, $errstr);
    if (!$fp) {
    EdjLog::info('socket_open error:'.json_encode($data). "Error: $errstr ($errno)");
    } else {
    stream_set_blocking($fp, true);//开启了非阻塞模式
    fputs($fp, $request);
    fclose($fp);
    usleep(400000); //等待500ms
    EdjLog::info('socket_open success:'.json_encode($data));
    }
    // $result = '';
    // while(!feof($fp)) {
    //
    // $response = fgets($fp, 512);
    // if (!is_numeric(trim($response))) {
    // continue;
    // }
    // $result.= $response;
    // }

    // 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
    "; 
      $post_header .= "Content-Type: application/x-www-form-urlencoded
    "; 
      $post_header .= "User-Agent: MSIE
    "; 
      $post_header .= "Host: ".$srv_ip."
    "; 
      $post_header .= "Content-Length: ".$content_length."
    "; 
      $post_header .= "Connection: close
    
    "; 
      $post_header .= $post_str."
    
    "; 
      fwrite($fp,$post_header); 
     
      $inheader = 1; 
      while(!feof($fp)){//测试文件指针是否到了文件结束的位置 
       $line = fgets($fp,1024); 
       //去掉请求包的头信息 
       if ($inheader && ($line == "
    " || $line == "
    ")) { 
             $inheader = 0; 
        } 
        if ($inheader == 0) { 
          echo $line; 
        } 
      } 
      fclose($fp); 
      unset ($line); 
    ?> 

    其它博文

    https://blog.csdn.net/navioo/article/details/82771663

  • 相关阅读:
    21、Java并发性和多线程-Java中的锁
    20、Java并发性和多线程-Slipped Conditions
    19、Java并发性和多线程-嵌套管程锁死
    Java 架构师眼中的 HTTP 协议
    MonolithFirst
    【SpringMVC】从Fastjson迁移到Jackson,以及对技术选型的反思
    SpringMVC接口测试异常:Can not deserialize instance of int out of START_OBJECT token
    请不要盲目的进行横向扩展,优先考虑对单台服务器的性能优化,只有单台服务器的性能达到最优化之后,集群才会被最大的发挥作用
    Android Fragment中调用getActivity为null的问题
    Android 那些年,处理getActivity()为null的日子
  • 原文地址:https://www.cnblogs.com/wt645631686/p/11360148.html
Copyright © 2011-2022 走看看