[Switch] PHP: How to Handle timeout when reading the HTTP interface
Address: https://www.jianshu.com/p/446ea7aaea86
Recently, I encountered a slow reading of the HTTP interface (using the PHP server language), so I want to talk about the timeout Mechanism for the server to read external resources, take a note of this document.
In Web development, a large amount of external resources are required for interaction, such as Mysql, Redis, Memcached, and HTTP interfaces. These resources have the following features:
- All are Network Interfaces
- Availability of these resources, uncontrollable connection speed and read Speed
- Layered mode: For the caller, it only determines whether the data can be read and whether the data is correct. For the resource provider, it is responsible for the specific data logic.
For resource callers, the following processing principles are recommended:
- Timeout mechanism: If the read resource is particularly slow, a read timeout mechanism should be available. For an application, an HTTP interface, if it takes 10 seconds to return data, is unacceptable.
- Retry Mechanism: If a resource is particularly important, for example, the resource cannot be obtained, but the application logic is heavily dependent on it, you can retry to read the resource to keep it available.
- Exception Handling Mechanism, that is, if the resource cannot be obtained, an exception should be thrown instead of a warning. due to historical reasons, PHP does not emphasize the exception mechanism, so many programs are actually incorrect. For example, when the HTTP interface times out, many developers think that the returned data is null, which is a serious logical error. In addition, timeout is also part of the exception.
This article mainly talks about the timeout mechanism for server programs to read HTTP interfaces. Why does it emphasize that server programs are mainly because the processing mechanism for client JavaScript to read HTTP interfaces is quite different (or the application scenario is different ).
Timeout value
Timeout can be divided into connection timeout and read timeout. The number of settings depends on two aspects. The first is the commitment of the HTTP interface. For example, the speed and availability requirements of the public platform interface should be extremely high, although it is not officially stated, I believe that the internal response speed of a single interface cannot exceed 1 second. The second is the user's consideration. For example, the read interface timeout of the queue program can be set to a higher value, while the corresponding timeout time of other programs cannot be set to a longer value, depending onNature and service capability of programs and Applications.
To put it bluntly, if the HTTP interface fails and the response is slow, but your program call timeout setting is large (if you add retry), the service availability of the HTTP interface will be further increased, the avalanche effect may be formed.
Default_socket_timeout
So how can I set the timeout? The PHP stream mechanism can be configured using the default_socket_timeout command.
Stream is an important feature in PHP. It can be said that in PHP, whether it is to read disk files or HTTP interfaces, can be considered as a stream (socket/stream ).
Note: The waiting time of socket/stream is not included in the maximum execution time of PHP.
For examplePHP.ini
Configuringmax_execution_time = 30
,max_execution_time = 20
The maximum processing time of this PHP program is 50 seconds.
Now, I thought it was important.If the timeout value is m seconds, the caller reports an error when the final response (including the network transmission time) of the access interface exceeds m seconds. This is not the case. As long as the data packet is being transmitted in m seconds, the calling program will not report an error.
The program is used to demonstrate the interface code first to simulate slow network transmission:
ob_implicit_flush(1);for($i=0; $i<6; $i++){ echo $i; echo str_repeat(' ',1024*64); sleep(1);}
Now let's look at the call code. It can be seen that although the last output of the interface takes 6 seconds, the Code does not report an error because the database package has been being transmitted.
ini_set("default_socket_timeout", 3);$url = "http://localhost/api.php";function e_filegetcontents() { global $url; var_dump(file_get_contents($url));}function e_fopenfgets(){ global $url; $context = stream_context_create(array('http'=> array( 'timeout' => 3.0, ))); $handle = fopen($url, "r",true,$context); if ($handle) { while (($buffer = fgets($handle, 4096)) !== false) { } fclose($handle); }}e_filegetcontents();e_fopenfgets();
Let's use cURL extension to handle timeout control.
For more precise processing timeout, use cURL extension, which can set connection timeout and read timeout (CURLOPT_TIMEOUT, CURLOPT_CONNECTTIMEOUT ).
If you want to control the returned HTTP interface in milliseconds, you can also use the CURLOPT_TIMEOUT_MS and CURLOPT_CONNECTTIMEOUT_M constants.
Note that if you use these two constants, you must setcurl_setopt($ch, CURLOPT_NOSIGNAL, 1);
The cURL extension mechanism is amazing,The program does not report an error.
Let's take a look at the Code:
function e_curl() { global $url; $ch = curl_init($url); curl_setopt($ch, CURLOPT_NOSIGNAL, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 3); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 1); $response = curl_exec($ch); if ($response === false) { $info = curl_getinfo($ch); if ($info['http_code'] === 0) { return false; } } return true;}e_curl();
Server simulation code:
<?phpob_implicit_flush(1);for($i=0; $i<60; $i++){ echo $i; echo str_repeat(' ',1024*64); sleep(1);}
The client reads the Code:
<? Php $ url = "https://api.sopans.com/stream.php"; ini_set ("default_socket_timeout", 3); // use file_get_contents to read function e_filegetcontents () {global $ url; var_dump (file_get_contents ($ url);} // use fopen to read function e_fopenfgets () {global $ url; $ context = stream_context_create (array ('http' => array ('timeout' => 3.0,); $ handle = fopen ($ url, "r", true, $ context); if ($ handle) {while ($ buffer = fgets ($ handle, 4096 ))! ==False) {} fclose ($ handle) ;}// use curl to read function e_curl () {global $ url; $ ch = curl_init ($ url ); curl_setopt ($ ch, CURLOPT_NOSIGNAL, 1); curl_setopt ($ ch, CURLOPT_TIMEOUT, 3); // set the maximum execution timeout value curl_setopt ($ ch, CURLOPT_CONNECTTIMEOUT, 1 ); // set the connection timeout value $ response = curl_exec ($ ch); if ($ response = false) {$ info = curl_getinfo ($ ch ); if ($ info ['HTTP _ Code'] = 0) {return false ;}} return true;} e_filegetcontents (); // timeout does not work // e_fopenfgets (); // when the data is being sent, the set timeout does not work // e_curl (); // timeout can work