이 글의 내용은 PHP에서 동시 요청(코드)을 구현하는 방법에 대한 내용입니다. 필요한 친구들이 참고할 수 있기를 바랍니다.
백엔드 서비스 개발에는 동시 요청에 대한 요구 사항이 있는 경우가 많습니다. 예를 들어 10개 공급업체(각각 다른 url
제공)의 대역폭 데이터를 얻은 다음 통합 데이터를 반환해야 합니다. , 당신은 무엇을 하시겠습니까? url
),然后返回一个整合后的数据,你会怎么做呢?
在PHP
中,最直观的做法foreach
遍历urls
,并保存每个请求的结果即可,那么如果供应商提供的接口平均耗时5s
,你的这个接口请求耗时就达到了50s
,这对于追求速度和性能的网站来说是不可接受的。
这个时候你就需要并发请求了。
PHP
请求PHP
是单进程同步模型,一个请求对应一个进程,I/O
是同步阻塞的。通过nginx/apache/php-fpm
等服务的扩展,才使得PHP提供高并发的服务,原理就是维护一个进程池,每个请求服务时单独起一个新的进程,每个进程独立存在。
PHP
不支持多线程模式和回调处理,因此PHP
内部脚本都是同步阻塞式的,如果你发起一个5s
的请求,那么程序就会I/O
阻塞5s
,直到请求返回结果,才会继续执行代码。因此做爬虫之类的高并发请求需求很吃力。
那怎么来解决并发请求的问题呢?除了内置的file_get_contents
和fsockopen
请求方式,PHP
也支持cURL
扩展来发起请求,它支持常规的单个请求:PHP cURL请求详解,也支持并发请求,其并发原理是cURL
扩展使用多线程来管理多请求。
PHP
并发请求我们直接来看代码demo
:
// 简单demo,默认支持为GET请求 public function multiRequest($urls) { $mh = curl_multi_init(); $urlHandlers = []; $urlData = []; // 初始化多个请求句柄为一个 foreach($urls as $value) { $ch = curl_init(); $url = $value['url']; $url .= strpos($url, '?') ? '&' : '?'; $params = $value['params']; $url .= is_array($params) ? http_build_query($params) : $params; curl_setopt($ch, CURLOPT_URL, $url); // 设置数据通过字符串返回,而不是直接输出 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $urlHandlers[] = $ch; curl_multi_add_handle($mh, $ch); } $active = null; // 检测操作的初始状态是否OK,CURLM_CALL_MULTI_PERFORM为常量值-1 do { // 返回的$active是活跃连接的数量,$mrc是返回值,正常为0,异常为-1 $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); // 如果还有活动的请求,同时操作状态OK,CURLM_OK为常量值0 while ($active && $mrc == CURLM_OK) { // 持续查询状态并不利于处理任务,每50ms检查一次,此时释放CPU,降低机器负载 usleep(50000); // 如果批处理句柄OK,重复检查操作状态直至OK。select返回值异常时为-1,正常为1(因为只有1个批处理句柄) if (curl_multi_select($mh) != -1) { do { $mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // 获取返回结果 foreach($urlHandlers as $index => $ch) { $urlData[$index] = curl_multi_getcontent($ch); // 移除单个curl句柄 curl_multi_remove_handle($mh, $ch); } curl_multi_close($mh); return $urlData; }
在该并发请求中,先创建一个批处理句柄,然后将url
的cURL
句柄添加到批处理句柄中,并不断查询批处理句柄的执行状态,当执行完成后,获取返回的结果。
curl_multi
相关函数/** 函数作用:返回一个新cURL批处理句柄 @return resource 成功返回cURL批处理句柄,失败返回false */ resource curl_multi_init ( void ) /** 函数作用:向curl批处理会话中添加单独的curl句柄 @param $mh 由curl_multi_init返回的批处理句柄 @param $ch 由curl_init返回的cURL句柄 @return resource 成功返回cURL批处理句柄,失败返回false */ int curl_multi_add_handle ( resource $mh , resource $ch ) /** 函数作用:运行当前 cURL 句柄的子连接 @param $mh 由curl_multi_init返回的批处理句柄 @param $still_running 一个用来判断操作是否仍在执行的标识的引用 @return 一个定义于 cURL 预定义常量中的 cURL 代码 */ int curl_multi_exec ( resource $mh , int &$still_running ) /** 函数作用:等待所有cURL批处理中的活动连接 @param $mh 由curl_multi_init返回的批处理句柄 @param $timeout 以秒为单位,等待响应的时间 @return 成功时返回描述符集合中描述符的数量。失败时,select失败时返回-1,否则返回超时(从底层的select系统调用). */ int curl_multi_select ( resource $mh [, float $timeout = 1.0 ] ) /** 函数作用:移除cURL批处理句柄资源中的某个句柄资源 说明:从给定的批处理句柄mh中移除ch句柄。当ch句柄被移除以后,仍然可以合法地用curl_exec()执行这个句柄。如果要移除的句柄正在被使用,则这个句柄涉及的所有传输任务会被中止。 @param $mh 由curl_multi_init返回的批处理句柄 @param $ch 由curl_init返回的cURL句柄 @return 成功时返回0,失败时返回CURLM_XXX中的一个 */ int curl_multi_remove_handle ( resource $mh , resource $ch ) /** 函数作用:关闭一组cURL句柄 @param $mh 由curl_multi_init返回的批处理句柄 @return void */ void curl_multi_close ( resource $mh ) /** 函数作用:如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流 @param $ch 由curl_init返回的cURL句柄 @return string 如果设置了CURLOPT_RETURNTRANSFER,则返回获取的输出的文本流。 */ string curl_multi_getcontent ( resource $ch )
CURLM_CALL_MULTI_PERFORM: (int) -1
CURLM_OK: (int) 0
PHP
并发请求耗时对比第一次请求使用上面的curl_multi_init
方法,并发请求105
次。
第二次请求使用传统的foreach
方法,遍历105
次使用curl_init
方法请求。
实际的请求耗时结果为:
刨除download
的约765ms
耗时,单纯的请求耗时优化达到了39.83/1.58
达到了25
倍,如果继续刨除建连相关的耗时,应该会更高。这其中的耗时:
方案1:最慢的一个接口达到了1.58s
方案2:105
个接口的平均耗时是384ms
curl_multi
会消耗很多的系统资源,在并发请求时并发数有一定阈值,一般为512
,是由于CURL
内部限制,超过最大并发会导致失败。
为了防止慢请求影响整个服务,可以设置CURLOPT_TIMEOUT
来控制超时时间,防止部分假死的请求无限阻塞进程处理,最后打死机器服务。
CPU
负载打满在代码示例中,如果持续查询并发的执行状态,会导致cpu
的负载过高,所以,需要在代码里加上usleep(50000);
的语句。
同时,curl_multi_select
也可以控制cpu
占用,在数据有回应前会一直处于等待状态,新数据一来就会被唤醒并继续执行,减少了CPU
PHP
에서 가장 직관적인 방법은 urls
를 foreach
하고 각 요청의 결과를 저장하는 것입니다. 그런 다음 공급자가 평균을 제공합니다. 인터페이스는 5초
가 걸리고 인터페이스 요청은 50초
가 소요됩니다. 이는 속도와 성능을 추구하는 웹사이트에서는 허용되지 않습니다. 이때 동시요청을 하셔야 합니다.
PHP
요청PHP
는 단일 프로세스 동기화 모델이며 하나의 요청은 하나의 프로세스에 해당하며 I/O
code>는 동기화가 차단되었습니다. nginx/apache/php-fpm
과 같은 서비스 확장을 통해 PHP는 프로세스 풀을 유지하고 프로세스가 존재할 때마다 새로운 프로세스를 시작하는 것을 원칙으로 합니다. 독립적으로. PHP
는 멀티스레딩 모드와 콜백 처리를 지원하지 않으므로 5s
를 시작하면 PHP
의 내부 스크립트가 동기적으로 차단됩니다. 요청하면 프로그램은 5초
동안 I/O
를 차단하고 요청이 결과를 반환할 때까지 코드를 계속 실행하지 않습니다. 따라서 크롤러와 같은 높은 동시성 요청 요구 사항을 수행하는 것은 매우 어렵습니다. 그럼 동시 요청 문제를 해결하는 방법은 무엇일까요? 내장된 file_get_contents
및 fsockopen
요청 메서드 외에도 PHP
는 요청을 시작하는 cURL
확장도 지원합니다. 일반 단일 요청을 지원하는 , 동시 요청도 지원하는 PHP cURL 요청에 대한 자세한 설명 동시성 원칙은 cURL
확장이 다중 요청을 관리하기 위해 멀티스레딩을 사용한다는 것입니다.
PHP
동시 요청demo
코드를 직접 살펴보겠습니다.
이 동시 요청에서는 먼저 배치 핸들을 생성한 다음 url
의 cURL
핸들을 배치 핸들에 추가하고, 배치 핸들의 실행 상태를 지속적으로 쿼리하여 실행이 완료되면 반환된 결과를 얻습니다.
curl_multi
관련 함수CURLM_CALL_MULTI_PERFORM: (int) -1
🎜CURLM_OK: (int) 0
PHP
동시 요청 시간 소모 비교105
개의 동시 요청이 있습니다. 🎜foreach
메서드를 사용하여 105
번 순회하고 curl_init
메서드 요청을 사용합니다. 🎜765ms다운로드
제외 /code>는 시간이 많이 걸리는 순수 요청의 최적화가 39.83/1.58
에 도달했고 시간이 많이 걸리는 부분을 계속 제거하면 25
번에 도달했습니다. 연결 설정과 관련하여 더 높아야 합니다. 시간 소모: 🎜1.58s
🎜105
인터페이스의 시간 소모는 384ms
🎜curl_multi
는 많은 시스템 리소스를 소비합니다. 동시 요청 수에는 특정 임계값이 있습니다(일반적으로 ). 512
는 CURL
의 내부 제한으로 인해 발생합니다. 최대 동시성을 초과하면 오류가 발생합니다. 🎜구체적인 테스트 결과는 해보지 않았습니다. 다른 분들의 글을 참고하시면 됩니다. 컬 멀티를 사용할 때마다 동시 요청 수는 몇 개가 적당한가요? CURLOPT_TIMEOUT을 설정하여 시간 초과를 제어하여 일부 일시 중단된 요청이 프로세스를 무기한 차단하고 결국 시스템 서비스를 종료하는 것을 방지할 수 있습니다. 🎜<h3>
<code>CPU
가 완전히 로드되었습니다🎜코드 예에서 동시 실행 상태를 계속 쿼리하면 cpu
로드가 발생하여 너무 높으므로 코드에 usleep(50000);
문을 추가해야 합니다. 🎜동시에 curl_multi_select
는 cpu
점유도 제어할 수 있습니다. 데이터가 응답될 때까지 대기 상태가 되며 즉시 실행됩니다. 새로운 데이터가 들어오면 CPU
의 불필요한 소모를 줄여줍니다. 🎜🎜관련 권장 사항: 🎜🎜🎜AJAX 대기열 요청을 구현하는 방법(코드 포함) 🎜🎜🎜🎜🎜curl_multi를 사용하여 동시 요청을 구현하는 PHP 예제 PHP 기술 🎜🎜🎜🎜위 내용은 PHP에서 동시 요청을 구현하는 방법(코드)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!