Simon Frost
Senior Backend Magento Engineer, Magium Commerce
2x Magento Certified
@ProcessEight
http://github.com/ProcessEight
Note: Working with PHP for ten years, Magento for six
In synchronous programming:
@ul
- We issue a statement,
- We wait for it to complete...
- ..and then move on to the next statement.
@ulend
Note:
- Let's kick off with what we already know
- Therefore, the order of execution is predictable
In asynchronous programming:
@ul
- We issue a statement
- While we are waiting for it to complete, we can perform other tasks
- When it has finished, we run a callback function
@ulend
Note: This does mean the order of execution is not predictable
Note: Async programming takes advantage of this idea CPU cycles are measured in nanoseconds, whereas I/O cycles are measured in milliseconds
Note:
- PHP runs in a single thread, so there is no parallel execution.
- In the asynchronous model, tasks are not being run in parallel, instead, each task is being executed bit by bit, little by little.
- It may look like things run in parallel because the program continuously switches between different parts of tasks and processes them.
- Asynchronous programming allows us to continue execution whilst waiting for operations to complete
- Parallel execution/multi-threading means two or more operations can be processed at the same time
- Disadvantages of threads (See Learning Event-Driven PHP ebook p14)
@ul
-
Async programming gives us a different set of tools to work with
-
We need to adopt a different way of thinking about how we design programs
@ulend
Note:
- e.g. Avoiding blocking (I/O) operations
Note:
- These concepts are the tools we can use to build async programs
@ul
-
A Promise is a temporary placeholder used as a result whenever the result is not immediately available.
-
Once the result is ready, an event is emitted, which can be subscribed to and acted upon.
-
Promises are a way of managing callbacks and avoiding 'callback hell'
@ulend
Note:
- Instead of having multiple nested callbacks, Promises can only ever be one level deep
- They are still executed asynchronously
@ul
-
Co-routines are interruptible (or pausable) functions.
-
They can be used to wrap promises, so that code which calls co-routines can be written in a more synchronous fashion
@ulend
@ul
-
The event loop is used to subscribe to events and then act on them once they are dispatched.
-
The loop continues running until no more events are dispatched (i.e. There is nothing more to do).
@ulend
-
A 'blocking' operation is one which blocks program execution.
-
E.g. I/O is blocking. The program has to wait until the I/O operation has finished.
-
Therefore we must avoid blocking operations where possible.
-
Unavoidable 'blocking' operations, like file system access, can be wrapped in a Promise, or forked into a new child process which continues in the background
Note:
- Avoid blocking by using promises
- Or by forking the process using
exec
@ul
-
You don't need to learn a whole new language and ecosystem
-
No magic or special extensions required - this is the same PHP you're already using
-
No need to re-tool your whole stack and deployment framework
@ulend
Note:
- Use what you are already using (i.e. PHP, stack, deployment)
@ul
-
AmPHP
- Pros: Mature, well documented
- Cons: Requires extension
-
ReactPHP
- Pros: Mature, active project, no PHP extensions needed
- Cons: Limited feature set
@ulend
Note:
- Whilst there were a lot of libraries to implement async in PHP, these two are now the most up-to-date and frequently maintained
@ul
-
Event-driven programming implements the Reactor Pattern
-
It represents an application flow control that is determined by events or changes in state.
-
Therefore you cannot say exactly when anything in your program is going to happen.
-
Both AmPHP and ReactPHP implement the Reactor Pattern
@ulend
// Create the event loop
$loop = React\EventLoop\Factory::create();
// Create a new web server which will dispatch the following response
// for every request it receives
$server = new React\Http\Server(function (Psr\Http\Message\ServerRequestInterface $request) {
// Our generic response
return new React\Http\Response(
200,
array('Content-Type' => 'text/plain'),
"Hello World!\n"
);
});
// Start a new server listening on port 8080
$socket = new React\Socket\Server(8080, $loop);
$server->listen($socket);
echo "Server running at http://127.0.0.1:8080\n";
// Start the loop; Run the program
$loop->run();
# Start the server in one window
$ php -f react-hello-world-server.php
Server running at http://127.0.0.1:8080
@[02] @[03]
# Send a request to it in another
$ curl http://127.0.0.1:8080
Hello World!
@[02] @[03]
@ul
-
The sync way:
- Process images in sequence. Finish one before starting another.
-
The async way:
- Fork a new process for each image resize or upload operation.
@ulend
Note:
- Forking processes is recommended when working with the filesystem in async programs, because of blocking issues
- Fork the process so it runs in the background
- Using the ChildProcess React component
@ul
-
The sync way:
- Load the whole resultset into memory, then process it
-
The async way
- Load each row of the result into memory one at a time, then process it
$stream = $connection->queryStream('SELECT * FROM user');
$stream->on('data', function ($row) {
echo $row['name'] . PHP_EOL;
});
$stream->on('end', function () {
echo 'Completed.';
});
@ulend
Note:
- The
react/mysql
library was used here
@ul
-
The sync way:
- Load the whole file/dataset into memory, then start processing it
-
The async way:
- Use the
ReactPHP Stream
component to read and write large files/datasets asynchronously, i.e. One chunk at a time
- Use the
@ulend
Note:
- How does this differ from Async Bulk API community project?
@ul
- All the async code is located outside Magento
- The microservice communicates with Magento via the API
@ulend
Note:
- More of an all-or-nothing approach
@ul
- Async code is only used in isolated locations
@ulend
Note:
- Not all to take advantage of all async advantages
- A compromise
@ul
- Using asynchronous programming libraries like ReactPHP, we can drastically cut down on the amount of memory our scripts consume
- Which also can massively improve performance
@ulend
<?= $questions ?? exit(0); ?>