It took us a while to build a high-performance socket service that can handle a large number of connections at the same time, but without specific business.
If we enable a single process server, but the inside of a business takes 1 seconds, then in this 1 seconds is blocked, subsequent requests will wait, if the concurrent three requests, then three requests of the execution time will be Chang 1 seconds, 2 seconds, 3 seconds. There are several ways to increase concurrency:
1: Multiple START process, increase the number of concurrent
2: Optimize the business, reduce time consuming is equivalent to reduce blocking time, increase the number of concurrent
3: Asynchronous programming, avoid blocking, increase the number of concurrent
Here we will focus on the third method to access the third party HTTP as an example.
The code is as follows:
<?php
Synchronous read
function get_data_blocking () {
$socket = Stream_socket_client ("tcp://test.raventech.cn:80", $errno, $errstr, 6);
Fwrite ($socket, "get/sleep1.php http/1.0\r\nhost:test.raventech.cn\r\naccept: */*\r\n\r\n");
$str = "";
while (!feof ($socket)) {
$str. = Fgets ($socket, 1024);
}
Fclose ($socket);
return $str;
}
Asynchronous read
function get_data_unblocking () {
$socket = Stream_socket_client ("tcp://test.raventech.cn:80", $errno, $errstr, 6);
Stream_set_blocking ($socket, 0);
Fwrite ($socket, "get/sleep1.php http/1.0\r\nhost:test.raventech.cn\r\naccept: */*\r\n\r\n");
$write = NULL;
$except = NULL;
while ($socket) {
$read = Array ($socket);
$num _changed_streams = Stream_select ($read, $write, $except, 0);
if ($num _changed_streams > 0) {
foreach ($read as $r) {
$str = Fread ($r, 2048);
Fclose ($socket);
$socket = false;
return $str;
}
}
Usleep (100);
}
}
True asynchronous reading-using server IO multiplexing events to improve concurrency
Class get_data_event{
public $onMessage = null;
Private $str = ';
function __construct (& $server) {
$socket = Stream_socket_client ("tcp://test.xtgxiso.cn:80", $errno, $errstr, 6);
Stream_set_blocking ($socket, 0);
Fwrite ($socket, "get/sleep1.php http/1.0\r\nhost:test.xtgxiso.cn\r\naccept: */*\r\n\r\n");
$server->add_socket ($socket, Array ($this, ' read '));
}
Public function Read ($socket) {
while (1) {
$buffer = Fread ($socket, 1024);
if ($buffer = = = ' | | $buffer = = FALSE) {
Break
}
$this->str. = $buffer;
}
if ($this->onmessage && $this->str) {
Call_user_func ($this->onmessage, $this->str);
}
$this->str = ';
return false;
}
}
/**
* Single Process IO multiplexing Select
*/
Class Xtgxiso_server
{
Public $socket = false;
Public $master = Array ();
public $onConnect = null;
public $onMessage = null;
Public $other _socket_callback = Array ();
function __construct ($host = "0.0.0.0", $port =1215)
{
$this->socket = Stream_socket_server ("tcp://". $host ":". $port, $errno, $ERRSTR);
if (! $this->socket) die ($errstr.) -". $errno);
Stream_set_blocking ($this->socket,0);
$id = (int) $this->socket;
$this->master[$id] = $this->socket;
}
Public Function Add_socket ($socket, $callback) {
$id = (int) $socket;
$this->master[$id] = $socket;
$this->other_socket_callback[$id] = $callback;
}
Public Function run () {
$read = $this->master;
$receive = Array ();
echo "Start run...\n";
while (1) {
$read = $this->master;
echo "waiting...\n";
$mod _fd = @stream_select ($read, $_w = null, $_e = NULL, 60);
if ($mod _fd = = FALSE) {
Break
}
foreach ($read as $k => $v) {
$id = (int) $v;
if ($v = = = $this->socket) {
echo "New conn\n";
$conn = stream_socket_accept ($this->socket);
if ($this->onconnect) {
Call_user_func ($this->onconnect, $conn);
}
$id = (int) $conn;
$this->master[$id] = $conn;
else if (@ $this->other_socket_callback[$id]) {
Call_user_func_array ($this->other_socket_callback[$id], Array ($v));
} else {
echo "Read data\n";
if (!isset ($receive [$k])) {
$receive [$k]= "";
}
$buffer = Fread ($v, 1024);
echo $buffer. " \ n ";
if (strlen ($buffer) = = 0) {
if ($this->onclose) {
Call_user_func ($this->onclose, $v);
}
Fclose ($v);
$id = (int) $v;
unset ($this->master[$id]);
else if ($buffer = = FALSE) {
if ($this->onclose) {
Call_user_func ($this->onclose, $this->master[$key _to_del]);
}
Fclose ($v);
$id = (int) $v;
unset ($this->master[$id]);
} else {
$pos = Strpos ($buffer, "\r\n\r\n");
if ($pos = = False) {
$receive [$k]. = $buffer;
echo "Received:". $buffer. "; Not all package,continue recdiveing\n ";
}else{
$receive [$k]. = Trim (substr ($buffer, 0, $pos +4));
$buffer = substr ($buffer, $pos +4);
if ($this->onmessage) {
Call_user_func ($this->onmessage, $v, $receive [$k]);
}
$receive [$k]= ';
}
}
}
}
Usleep (10000);
}
}
}
$server = new Xtgxiso_server ();
$server->onconnect = function ($conn) {
echo "OnConnect--accepted". Stream_socket_get_name ($conn, True). "\ n";
};
$server->onmessage = function ($conn, $msg) use ($server) {
/*
$respone = "";//Response content
$respone = "http/1.1 ok\r\n";
$respone. = "server:openresty\r\n";
$respone. = "content-type:text/html; Charset=utf-8\r\n ";
$body = Time (). rand (111111,999999);
$len = strlen ($body);
$respone. = "Content-length: $len \ r \ n";
$respone. = "connection:close\r\n";
$respone. = "\r\n$body\r\n\r\n";
echo "OnMessage--". $msg. "\ n";
*/
Synchronous read
$respone = Get_data_blocking ();
Fwrite ($conn, $respone);
Asynchronous read
$respone = Get_data_unblocking ();
Fwrite ($conn, $respone);
Truly asynchronous
$data = new Get_data_event ($server);
$data->onmessage = function ($str) use ($conn) {
Fwrite ($conn, $STR);
};
};
$server->onclose = function ($conn) {
echo "OnClose--". "\ n";
};
$server->run ();
Third-party services sleep1.php code is relatively simple
<?php
Sleep (1);//Simulation time consuming
echo "OK";
With the code example above, we are commenting on the concurrency of the server by running synchronous reads, asynchronous reads, and truly asynchronous. The test method can write a test.html to simulate three concurrent.
<script src= "http://127.0.0.1:1215/?id=1" ></SCRIPT>
<script src= "http://127.0.0.1:1215/?id= 2 "></script>
<script src=" http://127.0.0.1:1215/?id=3 "></SCRIPT>
The test found that the real asynchronous is concurrent, each request takes 1 seconds, so we can finally understand what is really non-blocking asynchronous programming, the key is in the shared IO multiplexing.