Good stuff :-) There have been quite a few php app servers/workers appearing lately - you might want to have a look at some for inspiration, php-pm for instance :-)
Proof of concept: Application server - ~2400% laravel startup speed increase
A couple of years ago I started playing around making an application server and I just tested it with the basic Laravel hello world "Laravel 5" page:
Server Software: Apache/2.4.20
Server Hostname: localhost
Server Port: 80
Document Path: /laravel-default/public/
Document Length: 1023 bytes
Concurrency Level: 20
Time taken for tests: 5.015 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 2042146 bytes
HTML transferred: 1023000 bytes
Requests per second: 199.41 [#/sec] (mean)
Time per request: 100.294 [ms] (mean)
Time per request: 5.015 [ms] (mean, across all concurrent requests)
Transfer rate: 397.69 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.8 0 8
Processing: 44 99 27.5 99 268
Waiting: 44 99 27.0 99 268
Total: 44 100 27.5 99 268
With my Application server:
Server Software: Apache/2.4.20
Server Hostname: localhost
Server Port: 80
Document Path: /laravel-appserver/public/
Document Length: 1023 bytes
Concurrency Level: 20
Time taken for tests: 0.206 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 1393000 bytes
HTML transferred: 1023000 bytes
Requests per second: 4863.53 [#/sec] (mean)
Time per request: 4.112 [ms] (mean)
Time per request: 0.206 [ms] (mean, across all concurrent requests)
Transfer rate: 6616.11 [Kbytes/sec] received
The results are fairly self-evident. This was on a hex-core machine so the results are exaggerated, however even on a quad or dual core you should see a large performance increase. This requires a linux server and the extension=sysvmsg.so PHP extension uncommented in php.ini
The way this works is that the application is run in memory avoiding the need to bootstrap each request. Traditional PHP scripts need to do a lot of bootstrapping before processing the request: requireing dozens of files, loading configuration, connecting to the databasse, etc. By using an application server all this is done once. Then, each request connects to the application server and the appserver processes the request without needing to go through the entire bootstrap process.
N.b. This won't be a drop-in speed increase for real apps because developers are lazy and assume things will be destroyed, but it's something to consider
To try it, inside httpdocs or public_html firstly create two laravel installs for a comparison:
composer create-project laravel/laravel laravel-default
composer create-project laravel/laravel laravel-appserver
Now install the appserver:
cd laravel-appserver
composer require level-2/aphplication @dev
Create a appserver.php inside laravel-appserver, this is laravel's public/index.php as a server:
<?php
require 'vendor/autoload.php';
class MyApplication implements \Aphplication\Aphplication {
//Application state. This will be kept in memory when the application is closed
//This can even be MySQL connections and other resources
private $kernel;
public function __construct() {
$app = require_once __DIR__.'/bootstrap/app.php';
$this->kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
}
public function accept($appId, $sessionId, $get, $post, $server, $files, $cookie) {
$response = $this->kernel->handle(
$request = Illuminate\Http\Request::capture()
);
ob_start();
$response->send();
return ob_get_clean();
}
}
//Now create an instance of the server
$server = new \Aphplication\Server(new MyApplication());
//Check argv to allow starting and stopping the server
if (isset($argv[1]) && $argv[1] == 'stop') $server->shutdown();
else $server->start();
And then replace public/index.php with:
<?php
require_once '../vendor/level-2/aphplication/Aphplication/Client.php';
(There's no point in the overhead of an autoloader to load one file)
That's all the PHP stuff done. Start the server
php appserver.php
Once the server is started you can go to /laravel-appserver/public/index.php and should see the "Laravel 5" default page. Visiting /laravel-default/public/index.php will also display the page but not using the appserver.
Run some benchmarks:
ab -n 1000 -c 20 http://localhost/laravel-appserver/public/
ab -n 1000 -c 20 http://localhost/laravel-default/public/
And you'll notice a nice increase in speed as the bootstrapping logic is done once. This also allows keeping things like result sets in memory and running a proper application instead of one-shot scripts, but the performance increase alone is interesting :)
** This won't work with GET/POST/SERVER as I didn't want to mess about with Illuminate\Http\Request::capture() to not use superglobals but it would just be a matter of passing them in as args.**
Comments/suggestions are welcome! Laravel was one of the only frameworks that let me do this out of the box which is why I posted here :) I tried with a few others but they assume too much that the script will end or arbitrarily call exit() to make my test feasible.
Please or to participate in this conversation.