php异步执行的四种方法

2020-05-15
/**
 * @description: 异步执行 (post可行,get需要测试)
 * @param url url 脚本地址 www.xxx.com/text.php?var1=data1
 * @param timeout int 连接url地址时的超时时间 
 * @param method string 请求方法 默认get 
 * @param data array 请求参数数组 默认 []
 * @param cookie_array array cookie数组 默认[]
 * @return: 
 */
function async_exe($url,$timeout = 1,$method = 'get',$data = array(),$cookie_array = null)
{
    
    ignore_user_abort(true); // 忽略客户端断开
    set_time_limit(0);    // 设置执行不超时
    $cookie = '';
    foreach ($cookie_array as $k=>$v) {
        $cookie .= urlencode($k) .'='. urlencode($v) .'; ';
    }
    $urlinfo = parse_url($url);  
    $urlinfo['port'] = $urlinfo['port'] == 0 ? 80 : $urlinfo['port'];
    $query = !empty($data)? http_build_query($data) : '';
    
    //拼接请求头
    $crlf = "\r\n";
    $head_method = $method == 'post' ? 'POST ' : 'GET ';
    $out = $head_method.$urlinfo['path'].' HTTP/1.1'.$crlf; 
    $out .= 'Host: '.$urlinfo['host'].$crlf;
    if (!empty($cookie)) $out .= 'Cookie: '.substr($cookie, 0, -2).$crlf;
    if ($method == 'post' && !empty($data)) {
        $out .= 'Content-length: '.strlen($query).$crlf; 
        $out .= 'Content-type: application/x-www-form-urlencoded'.$crlf; 
        $out .= 'Connection: close'.$crlf.$crlf;
        $out .= $query.$crlf.$crlf;
    } else {
        $out .= 'Connection: close'.$crlf.$crlf;
    }
    $errno = 0; 
    $errstr = ''; 
    $fp = fsockopen($urlinfo['host'], $urlinfo['port'], $errno, $errstr, $timeout); 
    stream_set_blocking($fp,true);
    stream_set_timeout($fp,1);
    fputs($fp, $out);
    // while (!feof($fp)) { //可以查看返回信息(变成同步了),测试
    //     echo fgets($fp,128);
    // }
    //fwrite之后马上执行fclose,nginx会直接返回499,原因:客户端主动断开请求连接时,NGINX 不会将该请求代理给上游服务,设置休眠20毫秒可以避免这种情况
    usleep(20000);
    fclose($fp);
}

/**
 * @description: 异步执行
 * @param url url 脚本地址 http://xxx.xxx.xxx?var1=data1
 * @param method string 请求方法
 * @param params array 请求参数数组
 * @param cookie_array array cookie数组
 * @return: 
 */
function async_exe1($url,$method = 'get',$params = array(),$cookie_array = array())
{
    $ch = curl_init(); 
    $curl_opt = array(
        CURLOPT_URL => $url,
        CURLOPT_RETURNTRANSFER => 1, //获取的信息以文件流的形式返回,而不是直接输出。
        CURLOPT_TIMEOUT => 1, //允许 cURL 函数执行的最长秒数
    );
    if ($method == 'post') {
        $curl_opt[CURLOPT_POST] = true;
        $curl_opt[CURLOPT_POSTFIELDS] = http_build_query($params);
    }
    if (!empty($cookie_array)) {
        $cookie = '';
        foreach ($cookie_array as $k=>$v) {
            $cookie .= urlencode($k) .'='. urlencode($v) .'; ';
        }
        $curl_opt[CURLOPT_COOKIE] = $cookie;
    }
    curl_setopt_array($ch, $curl_opt); 
    curl_exec($ch);
    curl_close($ch); 
}

/**
 * @description: 异步执行 只能执行本地的脚本文件。并且只能单向打开,无法传大量参数给被调用脚本。并且如果,访问量很高的时候,会产生大量的进程。
 * @param path string 脚本地址 /home/xinchen/backend.php
 * @return: 
 */
function async_exe2($path)
{
    pclose(popen("php $path &", 'r'));
}

// 只有在FastCGI模式下存在,将echo的值返回给用户后,在此函数下面的代码将会继续执行,但是会关闭链接,
// 在fastcgi_finish_request()之后,脚本仍然会占用一个FPM进程。
// 只要会话处于活动状态,它们就会被锁定。这意味着后续的请求将被阻塞,直到会话关闭,应该尽快调用session_write_close()(甚至在fastcgi_finish_request()之前),以允许后续请求和良好的用户体验
// 只要一个锁处于活动状态(例如群锁或数据库锁),后续的请求就可能被锁住。
// windows上没有FPM,所以没有这个函数;linux上有
echo '返回值';
fastcgi_finish_request();

 

{/if}