Browse Source

Add Laravel-swoole and Lumen-swoole benchmarks (#4090)

* Add Laravel-swoole and Lumen-swoole benchmarks

* add lumen-swoole and laravel-swoole. combine -swoole mutations into lumen and laravel projects.
Brion Finlay 6 years ago
parent
commit
aeff04810e

+ 47 - 26
frameworks/PHP/laravel/benchmark_config.json

@@ -1,30 +1,51 @@
 {
 	"framework": "laravel",
-	"tests": [
-		{
-			"default": {
-				"json_url": "/json",
-				"db_url": "/db",
-				"query_url": "/queries/",
-				"fortune_url": "/fortunes",
-				"update_url": "/updates/",
-				"plaintext_url": "/plaintext",
-				"port": 8080,
-				"approach": "Realistic",
-				"classification": "Fullstack",
-				"database": "MySQL",
-				"framework": "laravel",
-				"language": "PHP",
-				"flavor": "PHP7",
-				"orm": "Full",
-				"platform": "None",
-				"webserver": "nginx",
-				"os": "Linux",
-				"database_os": "Linux",
-				"display_name": "Laravel 5.6",
-				"notes": "",
-				"versus": "php"
-			}
+	"tests": [{
+		"default": {
+			"json_url": "/json",
+			"db_url": "/db",
+			"query_url": "/queries/",
+			"fortune_url": "/fortunes",
+			"update_url": "/updates/",
+			"plaintext_url": "/plaintext",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Fullstack",
+			"database": "MySQL",
+			"framework": "laravel",
+			"language": "PHP",
+			"flavor": "PHP7",
+			"orm": "Full",
+			"platform": "None",
+			"webserver": "nginx",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "Laravel 5.6",
+			"notes": "",
+			"versus": "php"
+		},
+		"swoole": {
+			"json_url": "/json",
+			"db_url": "/db",
+			"query_url": "/queries/",
+			"fortune_url": "/fortunes",
+			"update_url": "/updates/",
+			"plaintext_url": "/plaintext",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Fullstack",
+			"database": "MySQL",
+			"framework": "laravel",
+			"language": "PHP",
+			"flavor": "None",
+			"orm": "Full",
+			"platform": "None",
+			"webserver": "swoole",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "laravel-swoole",
+			"notes": "",
+			"versus": "swoole"
 		}
-	]
+	}]
 }

+ 41 - 0
frameworks/PHP/laravel/config/swoole_http.php

@@ -0,0 +1,41 @@
+<?php
+
+if (env('APP_SWOOLE', false)) {
+    return [
+        /*
+        |--------------------------------------------------------------------------
+        | HTTP server configurations.
+        |--------------------------------------------------------------------------
+        |
+        | @see https://wiki.swoole.com/wiki/page/274.html
+        |
+        */
+        'server' => [
+            'host' => env('SWOOLE_HTTP_HOST', '0.0.0.0'),
+            'port' => env('SWOOLE_HTTP_PORT', '8080'),
+            'options' => [
+                'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('swoole_http.pid')),
+                // 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')),
+                'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', 0),
+                'handle_static_files' => env('SWOOLE_HTTP_STATIC', 0),
+                'public_path' => storage_path('app/public'),
+                'ob_output' => env('SWOOLE_HTTP_OB_OUTPUT', 0),
+                // Normally this value should be 1~4 times lager according to your cpu cores
+                'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num() * 2),
+                'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num() * 2),
+                'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num() * 2),
+                // This value should be larger than `post_max_size` and `upload_max_filesize` in `php.ini`.
+                // This equals to 10 MB
+                'package_max_length' => 10 * 1024 * 1024,
+                'buffer_output_size' => 10 * 1024 * 1024,
+                // Max buffer size for socket connections
+                'socket_buffer_size' => 128 * 1024 * 1024,
+                // Worker will restart after processing this number of request
+                'max_request' => 3000,
+            ],
+        ],
+        'providers' => [
+            // App\Providers\AuthServiceProvider::class,
+        ]
+    ];
+}

+ 61 - 0
frameworks/PHP/laravel/deploy/swoole/composer.json

@@ -0,0 +1,61 @@
+{
+    "name": "laravel/laravel",
+    "description": "The Laravel Framework.",
+    "keywords": ["framework", "laravel"],
+    "license": "MIT",
+    "type": "project",
+    "require": {
+        "php": ">=7.1.3",
+        "fideloper/proxy": "~4.0",
+        "laravel/framework": "5.6.*",
+        "laravel/tinker": "~1.0",
+        "swooletw/laravel-swoole": "v2.5.0"
+    },
+    "require-dev": {
+        "filp/whoops": "~2.0",
+        "fzaninotto/faker": "~1.4",
+        "mockery/mockery": "~1.0",
+        "nunomaduro/collision": "~2.0",
+        "phpunit/phpunit": "~7.0",
+        "symfony/thanks": "^1.0"
+    },
+    "autoload": {
+        "classmap": [
+            "database/seeds",
+            "database/factories"
+        ],
+        "psr-4": {
+            "App\\": "app/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\": "tests/"
+        }
+    },
+    "extra": {
+        "laravel": {
+            "dont-discover": [
+            ]
+        }
+    },
+    "scripts": {
+        "post-root-package-install": [
+            "@php -r \"file_exists('.env') || copy('.env.example', '.env');\""
+        ],
+        "post-create-project-cmd": [
+            "@php artisan key:generate"
+        ],
+        "post-autoload-dump": [
+            "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
+            "@php artisan package:discover"
+        ]
+    },
+    "config": {
+        "preferred-install": "dist",
+        "sort-packages": true,
+        "optimize-autoloader": true
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true
+}

+ 17 - 0
frameworks/PHP/laravel/deploy/swoole/install-composer.sh

@@ -0,0 +1,17 @@
+#!/bin/sh
+
+EXPECTED_SIGNATURE="$(curl -s https://composer.github.io/installer.sig)"
+php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ACTUAL_SIGNATURE="$(php -r "echo hash_file('SHA384', 'composer-setup.php');")"
+
+if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
+then
+    >&2 echo 'ERROR: Invalid installer signature'
+    rm composer-setup.php
+    exit 1
+fi
+
+php composer-setup.php --quiet
+RESULT=$?
+rm composer-setup.php
+exit $RESULT

+ 2 - 0
frameworks/PHP/laravel/deploy/swoole/php.ini

@@ -0,0 +1,2 @@
+opcache.enable_cli=1
+opcache.validate_timestamps=0

+ 41 - 0
frameworks/PHP/laravel/laravel-swoole.dockerfile

@@ -0,0 +1,41 @@
+FROM php:7.2
+
+ENV SWOOLE_VERSION=4.2.1
+
+RUN cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+        && cd swoole-src-${SWOOLE_VERSION} \
+        && phpize && ./configure > /dev/null && make > /dev/null && make install > /dev/null \
+        && docker-php-ext-enable swoole
+
+RUN docker-php-ext-install pdo_mysql > /dev/null
+
+ADD ./ /laravel
+WORKDIR /laravel
+COPY deploy/swoole/php.ini /usr/local/etc/php/
+
+RUN mkdir -p /laravel/bootstrap/cache
+RUN mkdir -p /laravel/storage/framework/sessions
+RUN mkdir -p /laravel/storage/framework/views
+RUN mkdir -p /laravel/storage/framework/cache
+
+RUN chmod -R 777 /laravel
+
+RUN echo "APP_SWOOLE=true" >> .env
+
+# Install composer using the installation method documented at https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md
+# This method was chosen because composer is not part of the apt repositories that are in the default PHP 7.2 docker image
+# Adding alternate apt php repos can potentially cause problems with extension compatibility between the php build from the docker image and the alternate php build
+# An additional benefit of this method is that the correct version of composer will be used for the environment and version of the php system in the docker image
+RUN deploy/swoole/install-composer.sh
+
+RUN apt-get update -yqq  > /dev/null
+RUN apt-get install -yqq git unzip > /dev/null
+COPY deploy/swoole/composer* ./
+RUN php composer.phar install --quiet
+
+RUN php artisan config:cache
+RUN php artisan route:cache
+
+RUN chmod -R 777 /laravel
+
+CMD php artisan swoole:http start

+ 58 - 0
frameworks/PHP/laravel/readme.md

@@ -57,3 +57,61 @@ If you discover a security vulnerability within Laravel, please send an e-mail t
 ## License
 
 The Laravel framework is open-sourced software licensed under the [MIT license](https://opensource.org/licenses/MIT).
+
+# laravel-swoole Benchmarking Test
+
+The laravel-swoole test is a benchmark test of Laravel running on Swoole.
+
+Swoole is an asynchronous PHP webserver that runs as a PECL extension to PHP.  
+
+Traditional PHP servers use php-fpm to run PHP software.  On each request, php-fpm initializes a new instance of the php framework, processes the request, returns the response, and terminates the php framework.  
+This results in decreased performance relative to other technologies like Java or node.js based frameworks which intialize only once and then process multiple requests without terminating in between.
+
+Swoole provides this capability to PHP.  When Swoole starts, it initializes the framework once and handles requests without terminating the framework between requests.  Also, like nginx, netty, node.js, Swoole is an asynchronous event-loop based server.
+
+Laravel-swoole is an adapter layer between Swoole and Laravel/Lumen.  It provides facades for http requests and PDO database connections.  It launches Laravel worker instances for each cpu core to handle incoming requests. 
+
+Also because Laravel was written under php-fpm environment where the framework is reset between each request, sometimes state changes are not re-initialized between requests since it isn't necessary in an environment where the framework is terminated after each request.
+To handle this, Laravel-swoole creates a sandbox for each request with a copy of initial framework state so that any changes made by the request do not impact the state of other incoming requests.
+
+Brion Finlay 10/3/2018  
+
+### Test Type Implementation Source Code
+
+* [JSON](Relative/Path/To/Your/Source/File)
+* [PLAINTEXT](Relative/Path/To/Your/Source/File)
+* [DB](Relative/Path/To/Your/Source/File)
+* [QUERY](Relative/Path/To/Your/Source/File)
+* [UPDATE](Relative/Path/To/Your/Source/File)
+* [FORTUNES](Relative/Path/To/Your/Source/File)
+
+## Important Libraries
+The tests were run with:
+* [Swoole](https://www.swoole.co.uk/)
+* [laravel-swoole](https://github.com/swooletw/laravel-swoole/wiki)
+* [Laravel](https://laravel.com/)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries/[count]
+
+### UPDATE
+
+http://localhost:8080/updates/[count]
+
+### FORTUNES
+
+http://localhost:8080/fortunes

+ 1 - 1
frameworks/PHP/lumen/app/Http/Controllers/Controller.php

@@ -37,7 +37,7 @@ class Controller extends BaseController {
 		$rows->add($insert);
 		$rows = $rows->sortBy("message");
 
-		return view("fortunes", ["rows" => $rows]);
+		return response()->make(view("fortunes", ["rows" => $rows]), 200, ['Content-Type' => 'text/html; charset=UTF-8']);
 	}
 
 	public function updates($queries = 1) {

+ 47 - 27
frameworks/PHP/lumen/benchmark_config.json

@@ -1,31 +1,51 @@
 {
 	"framework": "lumen",
-	"tests": [
-		{
-			"default": {
-				"setup_file": "setup",
-				"json_url": "/json",
-				"db_url": "/db",
-				"query_url": "/queries/",
-				"fortune_url": "/fortunes",
-				"update_url": "/updates/",
-				"plaintext_url": "/plaintext",
-				"port": 8080,
-				"approach": "Realistic",
-				"classification": "Micro",
-				"database": "MySQL",
-				"framework": "lumen",
-				"language": "PHP",
-				"flavor": "PHP7",
-				"orm": "Full",
-				"platform": "None",
-				"webserver": "nginx",
-				"os": "Linux",
-				"database_os": "Linux",
-				"display_name": "Lumen",
-				"notes": "",
-				"versus": "php"
-			}
+	"tests": [{
+		"default": {
+			"setup_file": "setup",
+			"json_url": "/json",
+			"db_url": "/db",
+			"query_url": "/queries/",
+			"fortune_url": "/fortunes",
+			"update_url": "/updates/",
+			"plaintext_url": "/plaintext",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Micro",
+			"database": "MySQL",
+			"framework": "lumen",
+			"language": "PHP",
+			"flavor": "PHP7",
+			"orm": "Full",
+			"platform": "None",
+			"webserver": "nginx",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "Lumen",
+			"notes": "",
+			"versus": "php"
+		},
+		"swoole": {
+			"json_url": "/json",
+			"db_url": "/db",
+			"query_url": "/queries/",
+			"fortune_url": "/fortunes",
+			"update_url": "/updates/",
+			"plaintext_url": "/plaintext",
+			"port": 8080,
+			"approach": "Realistic",
+			"classification": "Micro",
+			"database": "MySQL",
+			"framework": "lumen",
+			"language": "PHP",
+			"flavor": "None",
+			"orm": "Full",
+			"platform": "None",
+			"webserver": "swoole",
+			"os": "Linux",
+			"database_os": "Linux",
+			"display_name": "lumen-swoole",
+			"versus": "swoole"
 		}
-	]
+	}]
 }

+ 5 - 0
frameworks/PHP/lumen/bootstrap/app.php

@@ -82,6 +82,11 @@ $app->singleton(
 // $app->register(App\Providers\AuthServiceProvider::class);
 // $app->register(App\Providers\EventServiceProvider::class);
 
+if (env('APP_SWOOLE',false)) {
+	$app->register(SwooleTW\Http\LumenServiceProvider::class);
+	$app->configure('swoole_http');
+}
+
 /*
 |--------------------------------------------------------------------------
 | Load The Application Routes

+ 39 - 0
frameworks/PHP/lumen/config/swoole_http.php

@@ -0,0 +1,39 @@
+<?php
+
+return [
+    /*
+    |--------------------------------------------------------------------------
+    | HTTP server configurations.
+    |--------------------------------------------------------------------------
+    |
+    | @see https://wiki.swoole.com/wiki/page/274.html
+    |
+    */
+    'server' => [
+        'host' => env('SWOOLE_HTTP_HOST', '0.0.0.0'),
+        'port' => env('SWOOLE_HTTP_PORT', '8080'),
+        'options' => [
+            'pid_file' => env('SWOOLE_HTTP_PID_FILE', base_path('swoole_http.pid')),
+            // 'log_file' => env('SWOOLE_HTTP_LOG_FILE', base_path('storage/logs/swoole_http.log')),
+            'daemonize' => env('SWOOLE_HTTP_DAEMONIZE', 0),
+            'handle_static_files' => env('SWOOLE_HTTP_STATIC', 0),
+            'public_path' => storage_path('app/public'),
+            'ob_output' => env('SWOOLE_HTTP_OB_OUTPUT', 0),
+            // Normally this value should be 1~4 times lager according to your cpu cores
+            'reactor_num' => env('SWOOLE_HTTP_REACTOR_NUM', swoole_cpu_num() * 2),
+            'worker_num' => env('SWOOLE_HTTP_WORKER_NUM', swoole_cpu_num() * 2),
+            'task_worker_num' => env('SWOOLE_HTTP_TASK_WORKER_NUM', swoole_cpu_num() * 2),
+            // This value should be larger than `post_max_size` and `upload_max_filesize` in `php.ini`.
+            // This equals to 10 MB
+            'package_max_length' => 10 * 1024 * 1024,
+            'buffer_output_size' => 10 * 1024 * 1024,
+            // Max buffer size for socket connections
+            'socket_buffer_size' => 128 * 1024 * 1024,
+            // Worker will restart after processing this number of request
+            'max_request' => 3000,
+        ],
+    ],
+    'providers' => [
+        // App\Providers\AuthServiceProvider::class,
+    ]
+];

+ 39 - 0
frameworks/PHP/lumen/deploy/swoole/composer.json

@@ -0,0 +1,39 @@
+{
+    "name": "laravel/lumen",
+    "description": "The Laravel Lumen Framework.",
+    "keywords": ["framework", "laravel", "lumen"],
+    "license": "MIT",
+    "type": "project",
+    "require": {
+        "php": ">=7.1.3",
+        "laravel/lumen-framework": "5.6.*",
+        "vlucas/phpdotenv": "~2.2",
+        "swooletw/laravel-swoole": "v2.5.0"
+    },
+    "require-dev": {
+        "fzaninotto/faker": "~1.4",
+        "phpunit/phpunit": "~7.0",
+        "mockery/mockery": "~1.0"
+    },
+    "autoload": {
+        "psr-4": {
+            "App\\": "app/"
+        }
+    },
+    "autoload-dev": {
+        "classmap": [
+            "tests/",
+            "database/"
+        ]
+    },
+    "scripts": {
+        "post-root-package-install": [
+            "php -r \"copy('.env.example', '.env');\""
+        ]
+    },
+    "minimum-stability": "dev",
+    "prefer-stable": true,
+    "config": {
+        "optimize-autoloader": true
+    }
+}

+ 17 - 0
frameworks/PHP/lumen/deploy/swoole/install-composer.sh

@@ -0,0 +1,17 @@
+#!/bin/sh
+
+EXPECTED_SIGNATURE="$(curl -s https://composer.github.io/installer.sig)"
+php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
+ACTUAL_SIGNATURE="$(php -r "echo hash_file('SHA384', 'composer-setup.php');")"
+
+if [ "$EXPECTED_SIGNATURE" != "$ACTUAL_SIGNATURE" ]
+then
+    >&2 echo 'ERROR: Invalid installer signature'
+    rm composer-setup.php
+    exit 1
+fi
+
+php composer-setup.php --quiet
+RESULT=$?
+rm composer-setup.php
+exit $RESULT

+ 2 - 0
frameworks/PHP/lumen/deploy/swoole/php.ini

@@ -0,0 +1,2 @@
+opcache.enable_cli=1
+opcache.validate_timestamps=0

+ 37 - 0
frameworks/PHP/lumen/lumen-swoole.dockerfile

@@ -0,0 +1,37 @@
+FROM php:7.2
+
+ENV SWOOLE_VERSION=4.2.1
+
+RUN cd /tmp && curl -sSL "https://github.com/swoole/swoole-src/archive/v${SWOOLE_VERSION}.tar.gz" | tar xzf - \
+        && cd swoole-src-${SWOOLE_VERSION} \
+        && phpize && ./configure > /dev/null && make > /dev/null && make install > /dev/null \
+        && docker-php-ext-enable swoole
+
+RUN docker-php-ext-install pdo_mysql > /dev/null
+
+ADD ./ /lumen
+WORKDIR /lumen
+COPY deploy/swoole/php.ini /usr/local/etc/php/
+
+RUN mkdir -p /lumen/storage/framework/sessions
+RUN mkdir -p /lumen/storage/framework/views
+RUN mkdir -p /lumen/storage/framework/cache
+
+RUN chmod -R 777 /lumen
+
+# Install composer using the installation method documented at https://getcomposer.org/doc/faqs/how-to-install-composer-programmatically.md
+# This method was chosen because composer is not part of the apt repositories that are in the default PHP 7.2 docker image
+# Adding alternate apt php repos can potentially cause problems with extension compatibility between the php build from the docker image and the alternate php build
+# An additional benefit of this method is that the correct version of composer will be used for the environment and version of the php system in the docker image
+RUN deploy/swoole/install-composer.sh
+
+RUN apt-get update -yqq  > /dev/null
+RUN apt-get install -yqq git unzip > /dev/null
+COPY deploy/swoole/composer* ./
+RUN php composer.phar install --quiet
+
+RUN echo "APP_SWOOLE=true" >> .env
+
+RUN chmod -R 777 /lumen
+
+CMD php artisan swoole:http start

+ 58 - 0
frameworks/PHP/lumen/readme.md

@@ -19,3 +19,61 @@ If you discover a security vulnerability within Lumen, please send an e-mail to
 ## License
 
 The Lumen framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT)
+
+# lumen-swoole Benchmarking Test
+
+The lumen-swoole test is a benchmark test of Lumen running on Swoole.
+
+Swoole is an asynchronous PHP webserver that runs as a PECL extension to PHP.  
+
+Traditional PHP servers use php-fpm to run PHP software.  On each request, php-fpm initializes a new instance of the php framework, processes the request, returns the response, and terminates the php framework.  
+This results in decreased performance relative to other technologies like Java or node.js based frameworks which intialize only once and then process multiple requests without terminating in between.
+
+Swoole provides this capability to PHP.  When Swoole starts, it initializes the framework once and handles requests without terminating the framework between requests.  Also, like nginx, netty, node.js, Swoole is an asynchronous event-loop based server.
+
+Laravel-swoole is an adapter layer between Swoole and Laravel/Lumen.  It provides facades for http requests and PDO database connections.  It launches Laravel worker instances for each cpu core to handle incoming requests. 
+
+Also because Laravel was written under php-fpm environment where the framework is reset between each request, sometimes state changes are not re-initialized between requests since it isn't necessary in an environment where the framework is terminated after each request.
+To handle this, Laravel-swoole creates a sandbox for each request with a copy of initial framework state so that any changes made by the request do not impact the state of other incoming requests.
+
+Brion Finlay 10/3/2018  
+
+### Test Type Implementation Source Code
+
+* [JSON](Relative/Path/To/Your/Source/File)
+* [PLAINTEXT](Relative/Path/To/Your/Source/File)
+* [DB](Relative/Path/To/Your/Source/File)
+* [QUERY](Relative/Path/To/Your/Source/File)
+* [UPDATE](Relative/Path/To/Your/Source/File)
+* [FORTUNES](Relative/Path/To/Your/Source/File)
+
+## Important Libraries
+The tests were run with:
+* [Swoole](https://www.swoole.co.uk/)
+* [laravel-swoole](https://github.com/swooletw/laravel-swoole/wiki)
+* [Lumen](https://lumen.laravel.com/)
+
+## Test URLs
+### JSON
+
+http://localhost:8080/json
+
+### PLAINTEXT
+
+http://localhost:8080/plaintext
+
+### DB
+
+http://localhost:8080/db
+
+### QUERY
+
+http://localhost:8080/queries/[count]
+
+### UPDATE
+
+http://localhost:8080/updates/[count]
+
+### FORTUNES
+
+http://localhost:8080/fortunes