Browse Source

Add symfony-swoole (#5237)

Jérémy Derussé 5 years ago
parent
commit
062105536f

+ 23 - 0
frameworks/PHP/symfony/benchmark_config.json

@@ -43,6 +43,29 @@
       "display_name": "symfony",
       "notes": "",
       "versus": "php"
+    },
+    "swoole": {
+      "plaintext_url": "/plaintext",
+      "json_url": "/json",
+      "db_url": "/db",
+      "update_url": "/updates?queries=",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MySQL",
+      "framework": "symfony",
+      "language": "PHP",
+      "flavor": "None",
+      "orm": "Full",
+      "platform": "swoole",
+      "webserver": "none",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "symfony-swoole",
+      "notes": "",
+      "versus": "swoole"
     }
   }]
 }

+ 1 - 0
frameworks/PHP/symfony/config/bundles.php

@@ -4,4 +4,5 @@ return [
     Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
     Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
     Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
+    K911\Swoole\Bridge\Symfony\Bundle\SwooleBundle::class => ['swoole' => true],
 ];

+ 0 - 10
frameworks/PHP/symfony/config/packages/prod/doctrine.yaml

@@ -7,16 +7,8 @@ doctrine:
         query_cache_driver:
             type: service
             id: doctrine.system_cache_provider
-        result_cache_driver:
-            type: service
-            id: doctrine.result_cache_provider
 
 services:
-    doctrine.result_cache_provider:
-        class: Symfony\Component\Cache\DoctrineProvider
-        public: false
-        arguments:
-            - '@doctrine.result_cache_pool'
     doctrine.system_cache_provider:
         class: Symfony\Component\Cache\DoctrineProvider
         public: false
@@ -26,7 +18,5 @@ services:
 framework:
     cache:
         pools:
-            doctrine.result_cache_pool:
-                adapter: cache.app
             doctrine.system_cache_pool:
                 adapter: cache.system

+ 22 - 0
frameworks/PHP/symfony/config/packages/swoole/doctrine.yaml

@@ -0,0 +1,22 @@
+doctrine:
+    orm:
+        auto_generate_proxy_classes: false
+        metadata_cache_driver:
+            type: service
+            id: doctrine.system_cache_provider
+        query_cache_driver:
+            type: service
+            id: doctrine.system_cache_provider
+
+services:
+    doctrine.system_cache_provider:
+        class: Symfony\Component\Cache\DoctrineProvider
+        public: false
+        arguments:
+        - '@doctrine.system_cache_pool'
+
+framework:
+    cache:
+        pools:
+            doctrine.system_cache_pool:
+                adapter: cache.system

+ 29 - 0
frameworks/PHP/symfony/config/packages/swoole/swoole.php

@@ -0,0 +1,29 @@
+<?php
+
+$container->loadFromExtension('swoole', [
+    'http_server' => [
+        'port' => '8080',
+        'host' => '0.0.0.0',
+        'hmr' => false,
+        'static' => [
+            'strategy' => false,
+        ],
+        'api' => false,
+        'services' => [
+            'cloudfront_proto_header_handler' => false,
+            'trust_all_proxies_handler' => false,
+            'debug_handler' => false,
+            'entity_manager_handler' => false,
+        ],
+        'settings' => [
+             'reactor_count' => swoole_cpu_num() * 2,
+             'worker_count' => swoole_cpu_num() * 2,
+             'task_worker_count' => swoole_cpu_num() * 2,
+
+            'log_file' => '/dev/null',
+            'log_level' => 'error',
+
+            'buffer_output_size' => 10 * 1024 * 1024,
+        ],
+    ],
+]);

+ 3 - 0
frameworks/PHP/symfony/deploy/swoole/php.ini

@@ -0,0 +1,3 @@
+opcache.enable_cli=1
+opcache.validate_timestamps=0
+memory_limit = 512M

+ 32 - 14
frameworks/PHP/symfony/src/Controller/DbController.php

@@ -26,9 +26,7 @@ class DbController
      */
     public function db(): JsonResponse
     {
-        $world = $this->worldRepository->find(mt_rand(1, 10000));
-
-        return new JsonResponse($world);
+        return new JsonResponse($this->worldRepository->find(mt_rand(1, 10000)));
     }
 
     /**
@@ -41,9 +39,10 @@ class DbController
 
         // possibility for enhancement is the use of SplFixedArray -> http://php.net/manual/de/class.splfixedarray.php
         $worlds = [];
-
-        for ($i = 0; $i < $queries; ++$i) {
-            $worlds[] = $this->worldRepository->find(mt_rand(1, 10000));
+        // Numbers must be unique, otherwise there is a chance to fetch twice the same number, Doctrine will then re-use
+        // the same object and won't perform a second request which is forbidden
+        foreach ($this->getUniqueRandomNumbers($queries) as $id) {
+            $worlds[] = $this->worldRepository->find($id);
         }
 
         return new JsonResponse($worlds);
@@ -58,18 +57,37 @@ class DbController
         $queries = min(500, max(1, $queries));
 
         $worlds = [];
+        // Numbers must be unique, otherwise there is a chance to fetch twice the same number, Doctrine will then re-use
+        // the same object and won't perform a second request which is forbidden
+        $ids = $this->getUniqueRandomNumbers($queries);
+        // Numbers must be ordered to avoid deadlock when 2 process will update the same ids in a random order
+        sort($ids);
+        foreach ($ids as $id) {
+            $worlds[] = $world = $this->worldRepository->find($id);
 
-        for ($i = 0; $i < $queries; ++$i) {
-            $world = $this->worldRepository->find(mt_rand(1, 10000));
-            if ($world) {
-                $randomNumber = mt_rand(1, 10000);
-                $world->setRandomNumber($randomNumber);
-                $worlds[] = $world;
-            }
+            // The new value have to be different from the previous. Otherwise Doctrine won't execute the update query
+            // which is forbidden
+            $oldId = $world->getRandomNumber();
+            do {
+                $newId = mt_rand(1, 10000);
+            } while($oldId === $newId);
+            $world->setRandomNumber($newId);
         }
-
         $this->entityManager->flush();
 
         return new JsonResponse($worlds);
     }
+
+    private function getUniqueRandomNumbers($count)
+    {
+        $res = [];
+        $current = 0;
+        do {
+            for ($i=$current; $i<$count; $i++) {
+                $res[mt_rand(1, 10000)] = 1;
+            }
+        } while (($current = count($res)) < $count);
+
+        return array_keys($res);
+    }
 }

+ 30 - 1
frameworks/PHP/symfony/src/Kernel.php

@@ -2,14 +2,20 @@
 
 namespace App;
 
+use App\Swoole\EntityManagerHandler;
+use App\Swoole\SrandStartHandler;
+use K911\Swoole\Server\RequestHandler\RequestHandlerInterface;
+use K911\Swoole\Server\WorkerHandler\WorkerStartHandlerInterface;
 use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
 use Symfony\Component\Config\Loader\LoaderInterface;
 use Symfony\Component\Config\Resource\FileResource;
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpKernel\Kernel as BaseKernel;
 use Symfony\Component\Routing\RouteCollectionBuilder;
 
-class Kernel extends BaseKernel
+class Kernel extends BaseKernel implements CompilerPassInterface
 {
     use MicroKernelTrait;
 
@@ -50,4 +56,27 @@ class Kernel extends BaseKernel
         $routes->import($confDir.'/{routes}/*'.self::CONFIG_EXTS, '/', 'glob');
         $routes->import($confDir.'/{routes}'.self::CONFIG_EXTS, '/', 'glob');
     }
+
+    public function process(ContainerBuilder $container)
+    {
+        if ($this->environment !== 'swoole') {
+            return;
+        }
+
+        $container->register(EntityManagerHandler::class, EntityManagerHandler::class)
+            ->addArgument(new Reference(EntityManagerHandler::class.'.inner'))
+            ->setAutowired(true)
+            ->setAutoconfigured(true)
+            ->setPublic(false)
+            ->setDecoratedService(RequestHandlerInterface::class, null, -20)
+        ;
+
+        $container->register(SrandStartHandler::class, SrandStartHandler::class)
+            ->addArgument(new Reference(SrandStartHandler::class.'.inner'))
+            ->setAutowired(true)
+            ->setAutoconfigured(true)
+            ->setPublic(false)
+            ->setDecoratedService(WorkerStartHandlerInterface::class)
+        ;
+    }
 }

+ 34 - 0
frameworks/PHP/symfony/src/Swoole/EntityManagerHandler.php

@@ -0,0 +1,34 @@
+<?php
+
+namespace App\Swoole;
+
+use Doctrine\ORM\EntityManagerInterface;
+use K911\Swoole\Server\RequestHandler\RequestHandlerInterface;
+use Swoole\Http\Request;
+use Swoole\Http\Response;
+
+final class EntityManagerHandler implements RequestHandlerInterface
+{
+    private $decorated;
+    private $entityManager;
+
+    public function __construct(RequestHandlerInterface $decorated, EntityManagerInterface $entityManager)
+    {
+        $this->decorated = $decorated;
+        $this->entityManager = $entityManager;
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function handle(Request $request, Response $response): void
+    {
+        try {
+            $this->decorated->handle($request, $response);
+        } finally {
+            // Swoole handle several request in a raw. We clear the entityManager between 2 call, to avoid Doctrine
+            // to re-use the same objects without fetching it from the database.
+            $this->entityManager->clear();
+        }
+    }
+}

+ 28 - 0
frameworks/PHP/symfony/src/Swoole/SrandStartHandler.php

@@ -0,0 +1,28 @@
+<?php
+
+namespace App\Swoole;
+
+use K911\Swoole\Server\WorkerHandler\WorkerStartHandlerInterface;
+use Swoole\Http\Request;
+use Swoole\Http\Response;
+use Swoole\Server;
+
+final class SrandStartHandler implements WorkerStartHandlerInterface
+{
+    private $decorated;
+
+    public function __construct(?WorkerStartHandlerInterface $decorated)
+    {
+        $this->decorated = $decorated;
+    }
+
+    public function handle(Server $worker, int $workerId): void
+    {
+        if ($this->decorated) {
+            $this->decorated->handle($worker, $workerId);
+        }
+
+        // Seed the random generator
+        \mt_srand();
+    }
+}

+ 35 - 0
frameworks/PHP/symfony/symfony-swoole.dockerfile

@@ -0,0 +1,35 @@
+FROM php:7.3
+
+RUN pecl install swoole > /dev/null && \
+    docker-php-ext-enable swoole
+
+RUN pecl install apcu > /dev/null && \
+    docker-php-ext-enable apcu
+
+RUN docker-php-ext-install pdo pdo_mysql opcache  > /dev/null
+
+RUN apt-get update -yqq && \
+    apt-get install -yqq git unzip
+
+RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
+
+COPY deploy/swoole/php.ini /usr/local/etc/php/
+WORKDIR /symfony
+ADD ./composer.json ./composer.json /symfony/
+RUN mkdir -m 777 -p /symfony/var/cache/{dev,swoole} /symfony/var/log
+RUN COMPOSER_ALLOW_SUPERUSER=1 composer install --no-dev
+RUN COMPOSER_ALLOW_SUPERUSER=1 composer require k911/swoole-bundle --no-scripts
+ADD . /symfony
+RUN COMPOSER_ALLOW_SUPERUSER=1 composer dump-autoload --classmap-authoritative
+RUN COMPOSER_ALLOW_SUPERUSER=1 composer dump-env swoole
+
+# removes hardcoded option `ATTR_STATEMENT_CLASS` conflicting with `ATTR_PERSISTENT`. Hack not needed when upgrading to Doctrine 3
+# see https://github.com/doctrine/dbal/issues/2315
+RUN sed -i '/PDO::ATTR_STATEMENT_CLASS/d' ./vendor/doctrine/dbal/lib/Doctrine/DBAL/Driver/PDOConnection.php
+
+RUN php bin/console cache:clear
+
+ENV APP_DEBUG=0 \
+    APP_ENV=swoole
+
+CMD php bin/console swoole:server:run