美文网首页
怎么实现PHP异步

怎么实现PHP异步

作者: 宸风如苏 | 来源:发表于2019-06-17 17:41 被阅读0次
    在开发中遇到过这样的问题(我是在支付宝回调的时候,处理的业务逻辑有点多),某段程序执行很慢,但是这又涉及到用户体验,这时候我们就会想如果我先返回给用户成功的提示,然后我在服务器上再去慢慢执行我的代码不就两全齐美了吗-----这就是我理解的异步。
    PHP是支持socket编程的,就是fsockopen函数,
    函数说明:fsockopen — 打开一个网络连接或者一个Unix套接字连接

    fscokopen会返回一个到远程主机连接的句柄。你可以像使用fopen返回的句柄一样,对她进行写fwrite,读取fgets, fread等操作。
    1:要使用这个函数,首先就要确认自己服务器上配置的是支持fsock的,找到php.ini,修改allow_url_fopen =on,还有一个就是打开extension=php_openssl.dll扩展
    2:如果没有安装openssl扩展,则先安装扩展,流程如下

        (1) 先安装依赖包:yum install openssl openssl-devel
    
        (2) 进入PHP安装包里的OpenSSL文件夹,根据个人的安装包位置不同,此处是
        cd /home/local/php.5.6.25/ext/openssl/phpize
    
        (3) 可能报错:Cannot find config.m4. Make sure that you run /usr/local/bin/phpize 
        in the top level source directory of the module,
    
        (4) 在当前目录下执行:mv config0.m4   config.m4  
    
        (5) 重新phpize
        ./configure --with-php-config=/usr/local/php/bin/php-config(php-config文件的路径)
    
        (6) 编译安装  make&&make install
    
        (7) 将openssl.so复制到PHP扩展文件夹中
    
        (8) 修改php.ini中 extesion=openssl.so
    

    3:接下来就是编写方法和PHP脚本了(贴上本人测试代码:post请求)

    //测试发fsock异步调用PHP
            /**fsockopen 抓取页面
             * @parem $url 网页地址 host 主机地址 
             * @parem $port 网址端口 默认80
             * @parem $t 脚本请求时间 默认30s
             * @parem $method 请求方式 get/post
             * @parem $data 如果单独传数据为 post 方式
             * @return 返回请求回的数据
             * */
        public function luelue()
        {
            $url = "http://seckill.com/hehe.php"; #url 地址必须 http://xxxxx
            $port=80;
            $t=30;
            $method='post';
            $data = array(
                'id'=>'1',
                'content'=>'这是第2次测试',); 
            $info=parse_url($url);
            $fp = fsockopen($info["host"],$port, $errno, $errstr,$t);
    
            // 判断是否有数据
            if(isset($data) && !empty($data))
            {
                $query = http_build_query($data); // 数组转url 字符串形式
            }else
            {
                $query=null;
            }
            // 如果用户的$url "http://www.manongjc.com/";  缺少 最后的反斜杠
            if(!isset($info['path']) || empty($info['path']))
            {
               $info['path']="/index.html";
            }
            // 判断 请求方式
            if($method=='post')
            {
                $head = "POST ".$info['path']." HTTP/1.0".PHP_EOL;
            }else
            {
                $head = "GET ".$info['path']."?".$query." HTTP/1.0".PHP_EOL;
            }
    
           $head .= "Host: ".$info['host'].PHP_EOL; // 请求主机地址
           $head .= "Referer: http://".$info['host'].$info['path'].PHP_EOL;
            if(isset($data) && !empty($data) && ($method=='post'))
            {
                $head .= "Content-type: application/x-www-form-urlencoded".PHP_EOL;
                $head .= "Content-Length: ".strlen(trim($query)).PHP_EOL;
                $head .= PHP_EOL;
                $head .= trim($query);
            }else
            {
                $head .= PHP_EOL;
            }
            $write = fputs($fp, $head); 
            //写入文件(可安全用于二进制文件)。 fputs() 函数是 fwrite() 函数的别名
            //不等待返回结果,直接return true
            echo json_encode(['status'=>200,'message'=>'success']);
            // while (!feof($fp))
            // {
            //     $line = fread($fp,4096);
            //     echo $line;
            // }
        }
    

    所调用的PHP脚本

    <?php
      $data=$_POST; 
      define('DB_HOST','你的数据库host');
      define('DB_USER','你的数据库用户名');
      define('DB_PASS','数据库密码');
      define('DB_NAME','数据库名字');
      define('DB_CHAR','utf8');
    
      $db = @mysqli_connect(DB_HOST,DB_USER,DB_PASS);
      if(mysqli_connect_errno($db)){
          exit('对不起,连接错误哦'.mysqli_connect_error($db));
      }
      //选择数据库名
      mysqli_select_db($db,DB_NAME);
      //选择字符集
      mysqli_set_charset($db,DB_CHAR);
      $sql="update test set taa='".$data['content']."' where id=".$data['id'];
      sleep(10);  //用户观察是不是先返回再更新的
      $res=mysqli_query($db,$sql);
      var_dump($res);
    
    还有一个问题就是,当客户端断开连接以后。也就是接口发送出调用PHP脚本请求后,立即关闭了连接,那么可能会引起服务器端正在执行的脚本退出。

    在 PHP 内部,系统维护着连接状态,其状态有三种可能的情况:

         * 0 – NORMAL(正常)
         * 1 – ABORTED(异常退出)
         * 2 – TIMEOUT(超时)
    

    (1)当 PHP 脚本正常地运行 NORMAL 状态时,连接为有效。
    (2)当客户端中断连接时,ABORTED 状态的标记将会被打开。远程客户端连接的中断通常是由用户点击 STOP 按钮导致的。
    (3)当连接时间超过 PHP 的时限(请参阅 set_time_limit() 函数)时,TIMEOUT 状态的标记将被打开。

    1:客户端断开连接中断脚本

    脚本是否需要在客户端中断连接时退出是可以设置的,(默认的情况是当远程客户端连接 中断时脚本将会退出)。该处理过程可由 php.ini 的 ignore_user_abort 或由 Apache .conf 设置中对应的“php_value ignore_user_abort”以及 ignore_user_abort() 函数来控制。如果没有告诉 PHP 忽略用户的中断,脚本将会被中断,除非通过 register_shutdown_function() 设置了关闭触发函数。

    2:脚本执行时间超时中断

    脚本也有可能被内置的脚本计时器中断。默认的超时限制为 30 秒。这个值可以通过设置 php.ini 的 max_execution_time 或 Apache .conf 设置中对应的“php_value max_execution_time”参数或者 set_time_limit() 函数来更改。当计数器超时的时候,脚本将会类似于以上连接中断的情况退出,先前被注册过的关闭触发函数也将在这时被执行。在该关闭触发函数中,可以通过调 用 connection_status() 函数来检查超时是否导致关闭触发函数被调用。如果超时导致了关闭触发函数的调用,该函数将返回 2。

    需要注意的一点是 ABORTED 和 TIMEOUT 状态可以同时有效。这在告诉 PHP 忽略用户的退出操作时是可能的。PHP 将仍然注意用户已经中断了连接但脚本仍然在运行的情况。如果到了运行的时间限制,脚本将被退出,设置过的关闭触发函数也将被执行。在这时会发现函数 connection_status() 返回 3。

    所以还在要触发的脚本中指明:

     ignore_user_abort(TRUE); //如果客户端断开连接,不会引起脚本abort
     set_time_limit(0);//取消脚本执行延时上限
    

    或者,也可以使用:

      register_shutdown_function(callback fuction[, parameters]);//注册脚本退出时执行的函数
    
    本文参考文章:

    1: http://www.laruence.com/2008/04/16/98.html
    2: https://www.cnblogs.com/xuey/p/8463009.html

    相关文章

      网友评论

          本文标题:怎么实现PHP异步

          本文链接:https://www.haomeiwen.com/subject/arczfctx.html