Hi everyone, I'm Codemaster Pioneer.
In this era of high performance, programmers talk and laugh about high performance, as if the corner of the mouth does not hang "high performance" three words will appear to be very Low, which is well known Nginx is a representative of high performance. Some friends may not even understand what is high performance, in fact, high performance is a unit of time can handle more client requests, if you want to ask how many requests can be processed, this should be combined with hardware and software conditions to assess the friends interested in the qualitative conditions of the use of stress-testing tools to test their own programs.
As we all know, PHP-FPM is PHP's process manager. Each client request forwarded by Ngixn will be handled by a PHP-FPM child process, which can only handle one client request at the same time. If you want to handle more than one request at the same time, you need to start more than one child process, which is obviously not enough to satisfy the demand of a scenario where you need to handle a large number of requests in a second. If you want to handle multiple requests at the same time, then you need to start multiple child processes. In this case, we can only use Workerman or Swoole, PHP's high-performance communication framework to solve similar special scenarios of concurrency problems, but this time I share the content is mainly Workerman.
As mentioned in the title, Workerman is the foundation of its life, what is the foundation of its life? I think it's the epoll tool for IO multiplexing. epoll is the foundation of high-performance programs, and it's the sword that solves the C10K problem. Next I will analyze the use of epoll in the Workerman source code, but before we do that, we need to learn how to use Socket and Event together in PHP. Event can be understood as a high level encapsulation of epoll, the underlying use of epoll sharpener.
Looking at this code will help you understand the Workerman source code, because this code is a refinement of the principle of Workerman's implementation of the event loop. stream_socket_server function creates, binds, and listens to a single implementation, which makes the code look more concise, unlike the previous socket_create, socket_bind, and socket_listen, which make the three steps slightly more cumbersome. bind, and socket_listen, making the code look more concise, unlike the previous socket_create, socket_bind, and socket_listen functions, which made the three steps slightly more cumbersome. Because of the use of the event loop, so you need to set the socket into non-blocking mode, only when there is a read or write notification will call the corresponding callback function. There is also a need for additional attention, the need for the client Socket to create the Event needs to be defined as a static variable or global variable, otherwise it can not be persistent connection to memory, which will result in the client can not establish a connection to transmit the data, I see a lot of people on the Internet have stepped into this pit. Finally, start the event loop EventLoop since the opening of the Socket listener and event loop dual operation.
<?php
// Create a TCP server socket
$server = stream_socket_server("tcp://0.0.0.0:8080", $errno, $error); echo "listening on port 8080...
echo "Listening on port 8080..." . PHP_EOL.
// Set it to non-blocking so that it doesn't block the $server object when it has no data to read or write.
stream_set_blocking($server, 0); // Set to non-blocking.
// Create the event base object
stream_set_blocking($server, 0); // Create the event base object.
// Create an event base to listen for readable events on the server-side socket.
$event = new Event($event_base, $server, Event::READ | Event::PERSIST, function ($server) use ($event_base) {
// Get a new connection, and since it's set to non-blocking mode, it won't keep blocking even if there's no new connection here
$client = @stream_socket_accept($server, 0); if ($client) { // Get a new connection.
if ($client) {
echo "Client (" . $client . ") Connection established" . PHP_EOL.
// For incoming connections from the client, also set it to non-blocking mode
stream_set_blocking($client, 0); // Set the connection to non-blocking mode.
// Client connections are created to listen for readable events.
// Note that client events need to be defined as static or global variables.
static $client_event; $client_event = new
$client_event = new Event($event_base, $client, Event::READ | Event::PERSIST, function ($client) {
// Read data from the client connection, 1024 bytes at a time.
$buffer = fread($client, 1024);
// Close the client connection if no data is read or if the client is no longer a resource handle.
if ($buffer == false || !is_resource($client)) {
// Close the client connection
fclose($client); }
echo "Client(" . $client . ") connection closed" . PHP_EOL.
return; }
}
echo "Received client (" . $client . ") data: $buffer" . PHP_EOL.
// Write back data to the client
$msg = "HTTP/1.0 200 OK\r\nContent-Length: 10\r\n\r\nServerOK\r\n" ;
fwrite($client, $msg);
}, $client).
$client_event->add();
}
}, $server);
// Add the event
$event->add();
// Execute the event loop
$event_base->loop();
Use the CURL tool to accesshttp://127.0.0.1:8080 The result ServerOK is returned correctly, indicating that the event loop is ready for normal operation.
[manongsen@root php_event]$ curl -i http://127.0.0.1:8080
HTTP/1.0 200 OK
Content-Length: 10
ServerOK
Once you've read the code above, the rest of this will go a lot smoother. The following code is an example that leads to Workerman, which constructs an HTTP service from the Worker class. onMessage defines a callback function that is called when an event is notified, and then the user implements the subsequent processing logic. runAll starts the entire service, including the creation of the process, the event loop, etc. The runAll function starts the service as a whole, including the creation of the process, the
<?php
// quote Worker resemble
use Workerman\Worker;
// autoloading Composer
require_once __DIR__ . '/vendor/';
// define HTTP Service and listen 8081 ports
$http_worker = new Worker('http://0.0.0.0:8081');
// define回调函数
$http_worker->onMessage = function ($connection, $request) {
//$request->get();
//$request->post();
//$request->header();
//$request->cookie();
//$request->session();
//$request->uri();
//$request->path();
//$request->method();
// Send data to client
$connection->send("Hello World");
};
// Starting services
Worker::runAll();
At line 2367 of the file, the stream_socket_server function creates the server socket and binds and listens on port 8081.
// workerman/:2367
$this->_mainSocket = \stream_socket_server($local_socket, $errno, $errmsg, $flags, $this->_context);
On line 2394 of the file, use the stream_set_blocking function to set the server-side socket to non-blocking mode.
// workerman/:2394
\stream_set_blocking($this->_mainSocket, false);
At line 2417 of the file, add the server-side _mainSocket to the event sequence and set the callback function to acceptConnection.
// workerman/:2417
static::$globalEvent->add($this->_mainSocket, EventInterface::EV_READ, array($this, 'acceptConnection'));
At line 2561 of the file, the connection $new_socket from the client is received using stream_socket_accept, where this operation is performed in the acceptConnection return function.
// workerman/:2561
$new_socket = \stream_socket_accept($socket, 0, $remote_address);
In line 285 of the file, use the stream_set_blocking function to set the client _socket to non-blocking mode, where _socket is the same as new_socket above.
// workerman/Connection/:285
\stream_set_blocking($this->_socket, 0);
In line 290 of the file, add the client _socket to the event loop and set its callback function to baseRead.
// workerman/Connection/:290
Worker::$globalEvent->add($this->_socket, EventInterface::EV_READ, array($this, 'baseRead'));
At line 1638 of the file, start the event loop.
// workerman/:1638
static::$globalEvent->loop();
After starting the event loop, you can read the data when a client connects. So in line 583 of the file, use the fread function to read data from the client $socket.
// workerman/Connection/:583
$buffer = @\fread($socket, self::READ_BUFFER_SIZE);
In line 647 of the file, the parser::decode function is used to parse the buffer data read above into a $request object, and $this is the $connection object, and this $this->onMessage is the user-defined callback function at the beginning. The $this->onMessage is the user-defined callback function at the beginning. Eventually, through the call_user_func function, the $connection and $request parameters will be called back to the onMessage method.
// workerman/Connection/:647
\call_user_func($this->onMessage, $this, $parser::decode($one_request_buffer, $this));
Finally, we'll use the CURL utility to call thehttp://127.0.0.1:8081 With the returned data, you can see that the callback to the onMessage function is correct.
[manongsen@root workerman]$ curl -i http://127.0.0.1:8081
HTTP/1.1 200 OK
Server: workerman
Connection: keep-alive
Content-Type: text/html;charset=utf-8
Content-Length: 13
Hello World
See here I believe you have some understanding of the Workerman source code in the event loop, if you have time to practice the beginning of the case code, and then combined with the source code to see Workerman will be quite rewarding. Workerman's high performance is to stand on the shoulders of the giant epoll on the realization of the epoll is nothing without it. To reiterate, Event in PHP is an encapsulation of epoll, which is the underlying technology in Linux. We will not be directly exposed to epoll in our daily programming. Finally, to return to the theme of epoll technology is the foundation of Workerman's life.
Thanks for reading, personal opinion is for reference only, welcome to express different views in the comment section.
Welcome to follow, share, like, favorite, in the watch, I'm the author of WeChat public number "code farmer forefather".