欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

php 使用fsockopen实现异步

程序员文章站 2024-01-28 10:56:58
...

场景

最近开发了一个管理系统,发现数据字典里面的数据很多,而用到字典的地方也很多。直接从数据库查询对数据库压力很大,因为数据量多的缘故,查询也会很缓慢。所以考虑将所有的数据字典存入缓存(文件形式存储)。但是这样做会有一个问题:就是如果字典的数据有更新,缓存的数据该如何更新。最开始我是在每次写入操作执行完成之后,再查询一遍所有的数据,存入缓存(不去比较缓存的数据,是因为无限极分类以及数据排序的问题)。但是随着数据越来越多,发现这种操作,为了优化查询,牺牲了用户修改数据的体验(修改、删除、添加这样的数据库写操作执行方法请求时间变长)。因为字典要求的实时性不是很高。所以考虑异步的方法去执行。

异步的几种方法

  1. 首先最简单的是,web调用ajax异步,或者image标签或者script标签进行异步,实现原理想必大家都知道,但是缺点是:如果执行没有完成,关闭浏览器,那么缓存不能更新。
  2. 其次,使用消息队列。网上有很多方法,缺点就是依赖于定时或者持续运行的服务,需要我们监控服务的运行和定时的执行,并且依赖于外部,需要安装和配置一些东西。复杂的队列学习成本也高。
  3. 使用curl, 设置CUROPT_TIMEOUT为1(最小为1)。也就是说,客户端至少必须等待1秒钟。
  4. popen() 打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生
    pclose(popen("/home/xinchen/backend.php &", 'r'));
    
    这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法穿大量参数给被调用脚本。
  5. fsockopen 这个是比较好的一种实现方案。我贴上代码,代码里面解释一下。
    官方地址 https://www.php.net/manual/zh/function.fsockopen.php
    /**
     * @param $host string 请求host
     * @param $port int 请求的端口号
     * @param $timeout int 设置连接的时限,单位为秒
     * @param $runPath string 请求地址
     */
    function asyncRun($host, $port, $timeout, $runPath)
    {
    	// 这里我们判断如果端口不是80,则拼接端口作为host
        if ($port != 80) {
            $host = $host . ':' . $port;
        }
    	
        $fp = fsockopen($host, (int)$port, $errno, $errstr, (int)$timeout);
        if (!$fp) {
            // 这里记录日志,或者直接输出(根据需求)
        } else {
            // 如果webserver是nginx,需要开启非阻塞模式
            stream_set_blocking($fp,0);
    		// 拼接请求头
            $out = "GET /$runPath HTTP/1.1\r\n";
            $out .= "Host: {$host}\r\n";
            $out .= "Accept: application/json, text/plain, */*\r\n";
            $out .= "Content-Type: application/json;charset=UTF-8\r\n";
            $out .= "Connection: Close\r\n\r\n";
    
            fwrite($fp, $out);
            // 给足够时间把请求转到fastcgi去执行(nginx)
            usleep(20000);
            fclose($fp);
        }
    }
    

总结

  • 如果执行的过程中直接发生错误,根据错误提示解决。一般查看是否支持socket,相关扩展是否打开等问题。
  • 如果执行没有错误,查看是否是webserver是nginx,考虑nginx阻塞的问题。试着先开启非阻塞模式,再在fclose之前usleep较长时间试一试。
相关标签: php基础