Technical Lead, Monstarlab Bangladesh
1. Problem Statement
2. Asynchronous Concepts
3. Asynchronous Programming
4. Evolution of PHP
5. PHP State of Affairs
6. PHP Async Libraries and Frameworks
7. Laravel Octane
8. Follow up
More than 5 billions of people now use the internet as of 2021.
Web applications need to be shipped out fast and efficiently that ensures best user experience.
Developer experience is top priority.
Utilising the advance of self hosted or cloud hardware capabilities.
Client side and server side
Client side need to load fast and be responsive.
Server side must be available and return quick responses, handle many requests at the same time, utilize hardware etc.
Full Details are here: https://www.jetbrains.com/lp/php-25/
Do you need asynchronous PHP?
No
"exceptional cuisine that is worth a special journey"
"excellent cooking that is worth a detour"
"a very good restaurant"
exceptional "pattern" that is worth a special journey
excellent "pattern" that is worth a detour
a very good "pattern"
Links
The resources used, inspired from, and great reads for further reading will be pasted here at the bottom
Task: Fish and Chips in 10 mins.
Task: Fish and Chips in 10 mins.
1 Chef, 3 items to make, 1 pan + 1 oven available = 17 mins
3 Chef, 9 items to make, 3 pan + 3 oven available = 17 mins
One might argue 17 / 3 = 6 mins for one.
1 Chef, 3 items to make, 1 pan + 1 oven available = 10 mins
Link:
https://www.youtube.com/watch?v=HrNLvCO2tE4
3 Chef, 9 items to make, 3 pan + 3 oven available = 10 mins
One might argue 10 / 3 = 3 mins for one.
Understanding what blocks him from working.
Instead of doing all his tasks' serially, he plans his tasks.
He do multiple things at the same time , and never sits idle.
A thread is basically a single process that a program can use to complete tasks. Each thread can only do a single task at once.
Link::
https://www.youtube.com/watch?v=SpmnVDYwr_g&t=223s
Each task will be run sequentially; a task has to complete before the next one can be started.
Parallel execution is executing of items are carried out simultaneously.
Each task will be run sequentially; a task has to complete before the next one can be started.
Multi-threading is the execution of multiple parts of a program at the same time.
We can multi-thread on the same CPU core, or we can multi-thread on different CPU cores.
Access memory alone
Can access same memory concurrently
Happen in real-time, waits and then proceeds
Over a period of time.
Uses single thread but handles blocking I/Os .
Processor sits idle as we wait for file to be read etc.
Our OS can handle that.
But what about our server request handling, what can that process do while it is sitting idle?
Understanding what blocks the process from working.
Instead of doing all his tasks serially, there should be some planning,
Multiple things at the same time will ensure server serves more at a given time.
One approach to asynchronous programming is to make functions that perform a slow action take an extra argument, a callback function.
The action is started, and when it finishes, the callback function is called with the result.
Links:
https://eloquentjavascript.net/11_async.html
https://nodejs.org/en/knowledge/getting-started/control-flow/what-are-callbacks/
When you depend on result of an async call, and that in turns depends on another async call and ...
Links:
https://www.freecodecamp.org/news/how-to-deal-with-nested-callbacks-and-avoid-callback-hell-1bc8dc4a2012/
Solution to callback hell
A promise represents the eventual result of an asynchronous operation.
The primary way of interacting with a promise is through its then() method, which registers callbacks to receive either a promise's eventual value or the reason why the promise cannot be fulfilled.
Links:
https://jeromejaglale.com/doc/php/laravel_asynchronous_guzzle_requests_using_promises
Not in PHP or in any upcoming RFC.
Brings synchronicity to async model.
But in Guzzle's Promise you can synchronously force promises to complete using a promise's wait method. When creating a promise, you can provide a wait function that is used to synchronously force a promise to complete.
Generators are like the normal functions in PHP but instead of returning a value, they yields as many values as it needs to. So, whichever function that contains “yield” is a generator.
Functions to be paused and then resumed again.
Link:
https://eloquentjavascript.net/11_async.html
Ability to send values back to the generator.
This is useful in asynchronous applications, as the coroutine can suspend while waiting for some task to complete or information to arrive, and the CPU is free to perform other tasks.
Lightweight thread of execution
It maintains its own stack (variables and state), that can be started, suspended, or terminated cooperatively by the main code and the Fiber.
These are also known as coroutines or green-threads.
Links:
https://www.zend.com/blog/swoole
https://php.watch/versions/8.1/fibers
Fibers are a lightweight threads but not threads, but called green threads.
Green threads are "user-level threads". They are scheduled by an "ordinary" user-level process, not by the kernel.
That means fibers are cooperatively scheduled. One fiber yield itself to allow another fiber to run.
Threads are preemptively scheduled, i.e a scheduling technique that works by dividing time slots of CPU to a given process.
Links:
https://graphitemaster.github.io/fibers/
Full Details are here: https://www.jetbrains.com/lp/php-25/
How PHP compiler works?
How PHP Servers works?
How we traditionally code async?
Traditional Model
https://www.zend.com/blog/exploring-new-php-jit-compiler
Now with JIT
Just In Time compiler transforms Opcodes into an architecture-specific machine code using DynASM
(C Library).
CPU-intensive task that will benefit the most.
https://stitcher.io/blog/php-jithttps://www.zend.com/blog/exploring-new-php-jit-compiler
https://thephp.website/en/issue/php-8-jit/
Outdated now.
HHVM compiles Hack into an intermediate bytecode. This bytecode is then translated into x64 machine code dynamically at runtime by a just-in-time (JIT) compiler.
JIT
Execution (Native)
https://www.keycdn.com/blog/php-7-vs-hhvm
Traditional PHP model, is the FastCGI server implementation of PHP, like Apache, Nginx.
https://geekflare.com/php-fpm-optimization/
https://www.programmersought.com/article/79284336180/https://tsh.io/blog/php-pm-guide-getting-started-with-the-process-manager/
PHP Process Manager uses ReactPHP and Symfony components.
PHP-PM basically spawns several PHP instances as worker bootstraping your application (eg. the whole Symfony Kernel) and hold it in the memory to be prepared for every incoming request.
https://web.archive.org/web/20190103202024/http://marcjschmidt.de/2014/02/08/php-high-performance/
https://github.com/php-pm/php-pm
Runs in CLI mode, like Nodejs.
Enables the application to keep on running in memory.
PHP servers runs in a stateful manner.
Master process is the entry
Reactor threads handle client and network I/O
Manager process decides how many wokers and task workers to spwan
Task workers are use for offloaded blocking I/Os.
https://www.zend.com/blog/exploring-new-php-jit-compiler
Written in Golang. It runs php application in the form of workers.
Roadrunner works by creating a HTTP server with golang’s excellent net/http package, and using Goridge as a bridge between golang and PHP to pass PSR7 Request and Responses between PHP and golang
https://roadrunner.dev/docs/intro-about
Jobs/ Queues
I\O bound work to queue workers
We can use curl_multi_exec() to ensure we don't have to wait for the async calls. Guzzle library is pretty much good at this.
Though we have process Forking with PHP using pcntl_fork or pthreads, but all of this can't be used in webservers, this are only while using php command line client.
Guzzle
https://docs.guzzlephp.org/en/stable/
Spatie
https://github.com/spatie/async
ReactPHP
https://reactphp.org/
Amp PHP
https://amphp.org/amp/
Swoole
https://www.swoole.co.uk/how-it-works
Octane
https://github.com/laravel/octane
Framework X [Upcoming]
https://github.com/clue/framework-x/
https://www.zend.com/blog/swoole
Swoole provides the powerful CSP (Communicating sequential processes) programming model with three keywords: go, chan, defer.
CSP is an alternative concurrent programming model to the actor model in Erlang or Akka. It is well known because it is adopted in Golang
Keeps the bootstrapped application in memory
Utilizes this server: Swoole, Roadrunner
Concurrent Tasks
Processed by Octane utilize Swoole's "task workers", and execute within an entirely different process than the incoming request.
Cache
When using Swoole, you may leverage the Octane cache driver, which provides read and write speeds of up to 2 million operations per second. And it is interval based.
Table
Define and interact with your own arbitrary Swoole tables. Swoole tables provide extreme performance throughput and the data in these tables can be accessed by all workers on the server.
https://laravel.com/docs/8.x/octane
Dependency Injections
Octane will automatically handle resetting any first-party framework state between requests. However, Octane does not always know how to reset the global state created by your application.
Must avoid injecting the application service container, HTTP request instance and configuration repository instance into the constructors of other objects.
Memory Leaks
eg. Adding data to a statically maintained array will result in a memory leak.
Understand use cases for super charged applications
Making laravel packages Laravel Octane friendly
exceptional "pattern" that is worth a special journey
excellent "pattern" that is worth a detour
a very good "pattern"
solves what it aims at consistently
Impressive track record for the last few years in production grade environment, and community is growing immensely
Twitter: https://twitter.com/saadbinamjad
Github: https://github.com/saad-amjad
LinkedIn: https://www.linkedin.com/in/saadbinamjad