php 使用fsockopen实现异步
程序员文章站
2024-01-28 10:56:58
...
场景
最近开发了一个管理系统,发现数据字典里面的数据很多,而用到字典的地方也很多。直接从数据库查询对数据库压力很大,因为数据量多的缘故,查询也会很缓慢。所以考虑将所有的数据字典存入缓存(文件形式存储)。但是这样做会有一个问题:就是如果字典的数据有更新,缓存的数据该如何更新。最开始我是在每次写入操作执行完成之后,再查询一遍所有的数据,存入缓存(不去比较缓存的数据,是因为无限极分类以及数据排序的问题)。但是随着数据越来越多,发现这种操作,为了优化查询,牺牲了用户修改数据的体验(修改、删除、添加这样的数据库写操作执行方法请求时间变长)。因为字典要求的实时性不是很高。所以考虑异步的方法去执行。
异步的几种方法
- 首先最简单的是,web调用ajax异步,或者image标签或者script标签进行异步,实现原理想必大家都知道,但是缺点是:如果执行没有完成,关闭浏览器,那么缓存不能更新。
- 其次,使用消息队列。网上有很多方法,缺点就是依赖于定时或者持续运行的服务,需要我们监控服务的运行和定时的执行,并且依赖于外部,需要安装和配置一些东西。复杂的队列学习成本也高。
- 使用curl, 设置CUROPT_TIMEOUT为1(最小为1)。也就是说,客户端至少必须等待1秒钟。
- popen() 打开一个指向进程的管道,该进程由派生给定的 command 命令执行而产生
这种方法不能通过HTTP协议请求另外的一个WebService,只能执行本地的脚本文件。并且只能单向打开,无法穿大量参数给被调用脚本。pclose(popen("/home/xinchen/backend.php &", 'r'));
- 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较长时间试一试。