├── .artillery └── profile.yaml ├── .roadrunner ├── .rr.yaml └── worker.php ├── README.md ├── composer.json └── src ├── Application ├── ApplicationInterface.php ├── MagentoAppWrapper.php ├── Request │ ├── Registry.php │ └── RegistryInterface.php └── Response │ ├── ResponseConverter.php │ └── ResponseConverterInterface.php ├── Framework ├── App │ └── Request │ │ └── Http.php ├── RequestTrait.php └── Webapi │ ├── Request.php │ └── Rest │ └── Request.php ├── Plugin ├── App │ └── State │ │ └── MuteAreaException.php └── Webapi │ └── Controller │ └── Rest │ └── InputParamsResolver │ └── RemoveRouteState.php ├── etc ├── di.xml └── module.xml └── registration.php /.artillery/profile.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | environments: 3 | apache: 4 | target: "http://rr.local" 5 | roadrunner: 6 | target: "http://0.0.0.0:8086" 7 | defaults: 8 | headers: 9 | Authorization: "Bearer ee5n7bh0ru4se3rhqo5hshy0yfapznpq" 10 | phases: 11 | - duration: 1 12 | arrivalRate: 1 13 | name: "Main phase" 14 | variables: 15 | prefix: 16 | - "S6-AP-" 17 | scenarios: 18 | - name: "S1. Create category" 19 | flow: 20 | - loop: 21 | - post: 22 | url: "/rest/V1/categories" 23 | json: 24 | category: 25 | name: "name-{{prefix}}-{{ $loopCount }}" 26 | parent_id: 2 27 | is_active: true 28 | count: 100 29 | - name: "S2. Countries list" 30 | flow: 31 | - loop: 32 | - get: 33 | url: "/rest/V1/directory/countries" 34 | count: 100 35 | - name: "S3. Product types list" 36 | flow: 37 | - loop: 38 | - get: 39 | url: "/rest/V1/products/types" 40 | count: 100 41 | - name: "S4. Product attribute sets list" 42 | flow: 43 | - loop: 44 | - get: 45 | url: "/rest/V1/products/attribute-sets/sets/list?searchCriteria" 46 | count: 100 47 | - name: "S5. Category get" 48 | flow: 49 | - loop: 50 | - get: 51 | url: "/rest/V1/categories/2" 52 | count: 100 53 | - name: "S6. Create product" 54 | flow: 55 | - loop: 56 | - post: 57 | url: '/rest/V1/products' 58 | json: 59 | product: 60 | sku: "sku-{{prefix}}-{{ $loopCount }}" 61 | name: "name-{{prefix}}-{{ $loopCount }}" 62 | attribute_set_id: 4 63 | price: 100 64 | type_id: "simple" 65 | count: 100 66 | - name: "S7. Get product list" 67 | flow: 68 | - loop: 69 | - get: 70 | url: "/rest/V1/products?searchCriteria[pageSize]=20" 71 | count: 100 72 | 73 | -------------------------------------------------------------------------------- /.roadrunner/.rr.yaml: -------------------------------------------------------------------------------- 1 | http: 2 | address: 0.0.0.0:8086 3 | workers: 4 | command: "php worker.php" 5 | pool: 6 | numWorkers: 1 7 | -------------------------------------------------------------------------------- /.roadrunner/worker.php: -------------------------------------------------------------------------------- 1 | createApplication(\Magento\Framework\App\Http::class); 19 | 20 | /** @var ApplicationInterface $psr7Application */ 21 | $psr7Application = $bootstrap->getObjectManager()->create( 22 | \Isxam\M2RoadRunner\Application\MagentoAppWrapper::class, 23 | [ 24 | 'magentoApp' => $app 25 | ] 26 | ); 27 | 28 | while ($request = $psr7->acceptRequest()) { 29 | try { 30 | $response = $psr7Application->handle($request); 31 | 32 | $psr7->respond($response); 33 | } catch (\Throwable $e) { 34 | $psr7->getWorker()->error((string)$e); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Magento2 RoadRunner 2 | 3 | ## How to run 4 | 5 | 1. Install composer package . 6 | 2. Enable Magento2 module `Isxam_M2RoadRunner`. 7 | 3. Copy `.roadrunner/worker.php` to Magento2 root. 8 | 3. Copy `.roadrunner/.rr.yaml.php` to Magento2 root. 9 | 4. Download RoadRunner Server binary `./vendor/bin/rr get`. 10 | 5. Run RoadRunner `./rr serve -v -d`. 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "isxam/magento2-roadrunner-performance", 3 | "description": "N/A", 4 | "version": "0.1.0", 5 | "require": { 6 | "php": "~7.1.3||~7.2.0", 7 | "magento/framework": "*", 8 | "spiral/roadrunner": "^1.4" 9 | }, 10 | "type": "magento2-module", 11 | "license": [], 12 | "autoload": { 13 | "files": [ 14 | "src/registration.php" 15 | ], 16 | "psr-4": { 17 | "Isxam\\M2RoadRunner\\": "src/" 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/Application/ApplicationInterface.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 43 | $this->magentoApp = $magentoApp; 44 | $this->responseConverter = $responseConverter; 45 | } 46 | 47 | /** 48 | * @inheritdoc 49 | */ 50 | public function handle(ServerRequestInterface $request): ResponseInterface 51 | { 52 | $this->registry->setCurrentRequest($request); 53 | 54 | try { 55 | $magentoResponse = $this->magentoApp->launch(); 56 | 57 | $response = $this->responseConverter->convert($magentoResponse); 58 | } catch (\Exception $e) { 59 | $response = $this->buildExceptionResponse($e); 60 | } 61 | 62 | return $response; 63 | } 64 | 65 | /** 66 | * Build exception response. 67 | * 68 | * @param \Exception $e 69 | * @return ResponseInterface 70 | */ 71 | private function buildExceptionResponse(\Exception $e): ResponseInterface 72 | { 73 | return new Response( 74 | 500, 75 | [], 76 | $e->getMessage() 77 | ); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Application/Request/Registry.php: -------------------------------------------------------------------------------- 1 | currentRequest = $request; 23 | } 24 | 25 | /** 26 | * @inheritdoc 27 | */ 28 | public function getCurrentRequest(): ?ServerRequestInterface 29 | { 30 | return $this->currentRequest; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Application/Request/RegistryInterface.php: -------------------------------------------------------------------------------- 1 | getHttpResponseCode(), 26 | $response->getHeaders()->toArray(), 27 | $response->getBody() 28 | ); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Application/Response/ResponseConverterInterface.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 50 | parent::__construct( 51 | $cookieReader, 52 | $converter, 53 | $routeConfig, 54 | $pathInfoProcessor, 55 | $objectManager, 56 | $uri, 57 | $directFrontNames, 58 | $pathInfoService 59 | ); 60 | } 61 | 62 | /** 63 | * @inheritdoc 64 | */ 65 | protected function getRegistry(): RegistryInterface 66 | { 67 | return $this->registry; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Framework/RequestTrait.php: -------------------------------------------------------------------------------- 1 | getRegistry()->getCurrentRequest()) { 22 | return parent::getMethod(); 23 | } 24 | 25 | return $this->getRegistry()->getCurrentRequest()->getMethod(); 26 | } 27 | 28 | /** 29 | * @inheritdoc 30 | */ 31 | public function getRequestUri() 32 | { 33 | if (!$this->getRegistry()->getCurrentRequest()) { 34 | return parent::getRequestUri(); 35 | } 36 | 37 | return $this->getRegistry()->getCurrentRequest()->getUri()->getPath(); 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function getHeaders($name = null, $default = false) 44 | { 45 | if (!$this->getRegistry()->getCurrentRequest()) { 46 | return parent::getHeaders($name, $default); 47 | } 48 | 49 | $request = $this->getRegistry()->getCurrentRequest(); 50 | 51 | $headers = new Headers(); 52 | $headers->addHeaders($request->getHeaders()); 53 | 54 | if ($name === null) { 55 | return $headers; 56 | } 57 | 58 | if($headers->has($name)) { 59 | return $headers->get($name); 60 | } 61 | 62 | return $default; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | public function getContent() 69 | { 70 | if (!$this->getRegistry()->getCurrentRequest()) { 71 | return parent::getContent(); 72 | } 73 | 74 | return (string)$this->getRegistry()->getCurrentRequest()->getBody(); 75 | } 76 | 77 | /** 78 | * @inheritdoc 79 | */ 80 | public function getQuery($name = null, $default = null) 81 | { 82 | if (!$this->getRegistry()->getCurrentRequest()) { 83 | return parent::getQuery($name, $default); 84 | } 85 | 86 | $request = $this->getRegistry()->getCurrentRequest(); 87 | 88 | $queryParams = (new Parameters()); 89 | $queryParams->fromArray($request->getQueryParams()); 90 | 91 | if ($name === null) { 92 | return $queryParams; 93 | } 94 | 95 | return $queryParams->get($name, $default); 96 | } 97 | 98 | /** 99 | * @inheritdoc 100 | */ 101 | public function getPost($name = null, $default = null) 102 | { 103 | if (!$this->getRegistry()->getCurrentRequest()) { 104 | return parent::getPost($name, $default); 105 | } 106 | 107 | $request = $this->getRegistry()->getCurrentRequest(); 108 | 109 | $postParams = new Parameters(); 110 | $postParams->fromArray($request->getParsedBody() ?: []); 111 | 112 | if ($name === null) { 113 | return $postParams; 114 | } 115 | 116 | return $postParams->get($name, $default); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/Framework/Webapi/Request.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 52 | $this->areaList = $areaList; 53 | $this->configScope = $configScope; 54 | parent::__construct($cookieReader, $converter, $areaList, $configScope, $uri); 55 | } 56 | 57 | /** 58 | * @inheritdoc 59 | */ 60 | protected function getRegistry(): RegistryInterface 61 | { 62 | return $this->registry; 63 | } 64 | 65 | /** 66 | * @inheritdoc 67 | */ 68 | public function getPathInfo() 69 | { 70 | $pathInfo = $this->getRequestUri(); 71 | /** Remove base url and area from path */ 72 | $areaFrontName = $this->areaList->getFrontName($this->configScope->getCurrentScope()); 73 | $pathInfo = preg_replace("#.*?/{$areaFrontName}/?#", '/', $pathInfo); 74 | /** Remove GET parameters from path */ 75 | $pathInfo = preg_replace('#\?.*#', '', $pathInfo); 76 | 77 | return $pathInfo; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/Framework/Webapi/Rest/Request.php: -------------------------------------------------------------------------------- 1 | registry = $registry; 54 | parent::__construct($cookieReader, $converter, $areaList, $configScope, $deserializerFactory, $uri); 55 | $this->areaList = $areaList; 56 | $this->configScope = $configScope; 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | protected function getRegistry(): RegistryInterface 63 | { 64 | return $this->registry; 65 | } 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | public function getHttpMethod() 71 | { 72 | return $this->getMethod(); 73 | } 74 | 75 | /** 76 | * @inheritdoc 77 | */ 78 | public function getPathInfo() 79 | { 80 | $pathInfo = $this->getRequestUri(); 81 | /** Remove base url and area from path */ 82 | $areaFrontName = $this->areaList->getFrontName($this->configScope->getCurrentScope()); 83 | $pathInfo = preg_replace("#.*?/{$areaFrontName}/?#", '/', $pathInfo); 84 | /** Remove GET parameters from path */ 85 | $pathInfo = preg_replace('#\?.*#', '', $pathInfo); 86 | 87 | return $pathInfo; 88 | } 89 | 90 | /** 91 | * @inheritdoc 92 | */ 93 | public function getBodyParams() 94 | { 95 | $content = (string)$this->getContent(); 96 | 97 | return $content ? (array)$this->_getDeserializer()->deserialize($content) : []; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/Plugin/App/State/MuteAreaException.php: -------------------------------------------------------------------------------- 1 | getAreaCode(); 22 | } catch (\Exception $e) { 23 | $proceed($code); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Plugin/Webapi/Controller/Rest/InputParamsResolver/RemoveRouteState.php: -------------------------------------------------------------------------------- 1 | router = $router; 34 | $this->request = $request; 35 | } 36 | 37 | /** 38 | * @param InputParamsResolver $subject 39 | * @param \Closure $proceed 40 | * @return Route 41 | * @throws Exception 42 | */ 43 | public function aroundGetRoute(InputParamsResolver $subject, \Closure $proceed) 44 | { 45 | return $this->router->match($this->request); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/etc/di.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 7 | 8 | 10 | 12 | 14 | 15 | 16 | 17 | Isxam\M2RoadRunner\Framework\Webapi\Rest\Request\Proxy 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/etc/module.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/registration.php: -------------------------------------------------------------------------------- 1 |