Overclocking Magento Rest API con RoadRunner

Acelera Magento con RoadRunner
PHP creado para morir. Y todo estaría bien, pero no se le ha dado la oportunidad de hacerlo recientemente. Hace un año, en el centro, tuvo lugar el anuncio de la herramienta RoadRunner, lo que obligó al proceso PHP a abandonar el círculo interminable de muerte y resurrección.


El principio del trabajo de RoadRunner es mantener el proceso en ejecución y enviar las solicitudes entrantes, lo que permite, según los desarrolladores, aumentar el rendimiento de la aplicación (a veces incluso 40 veces).


Como he estado trabajando con Magento durante mucho tiempo, me pareció una gran idea probar la herramienta no en un marco mítico, sino en una aplicación real, para la cual Magento Open Source funcionó muy bien.


Costo de inicialización de la aplicación Magento


La forma de acelerar la aplicación RoadRunner implica reducir el tiempo de respuesta (después de un inicio de calentamiento) al reducir la sobrecarga de inicializar la aplicación.


Captura de pantalla de la presentación de Anton Tsitou "Diseño de aplicaciones híbridas Go / PHP utilizando RoadRunner"
Captura de pantalla de la presentación de Anton Tsitou "Diseño de aplicaciones híbridas Go / PHP utilizando RoadRunner"


En el caso de Magento, el tiempo principal dedicado al inicio recae en:


  • autocargador de compositores
  • bootstrapping

La carga automática de Composer no llama la atención, ya que es estándar para una aplicación PHP.


Perfiles de resultados relacionados con Composer.
Perfiles de resultados relacionados con Composer.


El arranque de la aplicación Magento incluye inicializar un controlador de errores, verificar el estado de la aplicación, etc.


La parte más difícil es la inicialización del contenedor IoC ("ObjectManager" en términos de Magento) y la creación recursiva de instancias de dependencia a través de él para obtener el objeto de la aplicación.


Resultados de perfiles relacionados con bootstraping.
Resultados de perfiles relacionados con bootstraping.


Implementación de RoadRunner


Para iniciar RoadRunner, debe crear un trabajador que contendrá un ciclo para aceptar solicitudes entrantes y enviar respuestas. Además, la herramienta funciona con solicitudes y respuestas implementando PSR-7. De la documentación oficial, se ve más o menos así:


while ($req = $psr7->acceptRequest()) { $resp = new \Zend\Diactoros\Response(); $resp->getBody()->write("hello world"); $psr7->respond($resp); } 

Magento y PSR-7


Magento aún no ha implementado el PSR-7 y utiliza sus implementaciones de solicitud y respuesta, los enfoques en los que se basan principalmente en la versión anterior.


Para implementar RoadRunner, debe encontrar un punto de entrada que acepte la solicitud de alguna forma y devuelva una respuesta ( ejemplo de Symfony ).


Existe un punto en Magento , \ Magento \ Framework \ AppInterface , solo hay un problema, esta interfaz no está diseñada para aceptar la solicitud. Pero espera, ¿de dónde viene la aplicación? Vale la pena volver al principio y al mantra: PHP nace para morir . En consecuencia, la gran mayoría de las bibliotecas, paquetes y marcos, al diseñar y dividir en capas, simplemente no asumen que la solicitud resulta ser más de una global.


La capa de transporte de Magento se basa en el mismo principio. Aunque la documentación describe las diferencias entre los objetos inyectables / renovables , de hecho, tenemos el uso de la solicitud como un servicio global con estado que se inicializa a partir de variables globales ($ _GET, $ _POST). Además de todo esto, la inyección de este servicio se puede ver en todos los niveles de la aplicación en el núcleo mismo, y mucho menos en la calidad de los módulos de terceros.


Con base en lo anterior, se gastó la esperanza de implementar RoadRunner solo a través de la conversión de solicitudes de estilo PSR-7 a estilo Magento.


Implementar el adaptador PSR-7


Formulamos el problema, teniendo en cuenta la información recibida.
Me gustaría tener una determinada interfaz de aplicación que acepte una solicitud PSR-7 y devuelva una respuesta PSR-7. También es necesario crear una implementación de la interfaz creada que adapte este formato de interacción a la aplicación Magento.


Adaptador PSR-7
Adaptador PSR-7


Como se mencionó anteriormente, la aplicación magento ya devuelve una respuesta, por lo que solo necesitamos convertirla al formato PSR-7.


Para la solicitud, necesita una clase que represente todas las llamadas al objeto de solicitud actual, que colocamos en un registro especial (sí, los dioses de la arquitectura perdonarán esta perversión). Además, se encontró que la clase de solicitud no se usa una, sino 3, por lo que requiere que se vuelvan a registrar a través de la configuración de IoC del contenedor.


El conjunto de clases que solía funcionar con consultas en Magento
El conjunto de clases que solía funcionar con consultas en Magento


Problemas de una aplicación PHP inmortal


Una aplicación lanzada a través de RoadRunner tiene los mismos problemas que cualquier proceso php de larga duración, se describen en la documentación ( https://roadrunner.dev/docs/usage-production )


Los principales problemas del nivel de aplicación que el desarrollador debe monitorear son:



Se debe prestar especial atención en el contexto de Magento a la gestión del estado, ya que tanto en el núcleo como en los módulos de terceros, el almacenamiento en caché del usuario / producto / categoría actual dentro del servicio es un enfoque muy común.


  protected function getCustomer(): ?CustomerInterface { if (!$this->customer) { if ($this->customerSession->isLoggedIn()) { $this->customer = $this->customerRepository->getById($this->customerSession->getCustomerId()); } else { return null; } } return $this->customer; } 

Un ejemplo de un método del núcleo que usa el estado de un objeto.


Ejecución del servidor API Magento Rest a través de RoadRunner


Dados los posibles problemas con el estado global, basados ​​en la experiencia de desarrollar la parte frontal de Magento, se eligió la parte WebApi más adecuada e indolora para su lanzamiento.


Lo primero que debe hacer es crear a nuestro trabajador, que correrá a través de RoadRunner y vivirá sin fin (casi). Para hacer esto, tome un fragmento de código de las guías RoadRunner y agregue nuestra aplicación allí, envuelta en un adaptador PSR-7.


 $relay = new StreamRelay(STDIN, STDOUT); $psr7 = new PSR7Client(new Worker($relay)); $bootstrap = \Magento\Framework\App\Bootstrap::create(BP, []); /** @var \Magento\Framework\App\Http $app */ $app = $bootstrap->createApplication(\Magento\Framework\App\Http::class); /** @var ApplicationInterface $psr7Application */ $psr7Application = $bootstrap->getObjectManager()->create( \Isxam\M2RoadRunner\Application\MagentoAppWrapper::class, [ 'magentoApp' => $app ] ); while ($request = $psr7->acceptRequest()) { try { $response = $psr7Application->handle($request); $psr7->respond($response); } catch (\Throwable $e) { $psr7->getWorker()->error((string)$e); } } 

El código antes del ciclo while se ejecutará al inicio del trabajador, todo lo que está dentro del ciclo, con cada nueva solicitud.


Cuando nuestro trabajador está listo, procedemos a configurar el servidor RoadRunner escrito en Go. Aquí todo es bueno y rápido, solo el paquete del compositor que descarga el archivo ejecutable no necesita ser instalado, lo que no puede ser agradable. Creamos la configuración de nuestro servidor; el más simple se parece a esto.


 http: address: 0.0.0.0:8086 workers: command: "php worker.php" pool: numWorkers: 1 

Configuración de RoadRunner.


La documentación contiene una gran cantidad de configuraciones que le permiten configurar de manera flexible el servidor para que el deseo de compilar su binario no se produzca exactamente.


 ./rr serve -v -d 

Inicio del servidor


Prueba de solución


Las herramientas


Para pruebas convenientes, tomamos algo simple, por ejemplo artillery.io.


Probamos el rendimiento con la ayuda de un usuario que ejecuta consultas secuencialmente (RoadRunner también admite la ejecución de consultas en varios subprocesos, dejaremos esta pregunta a otros investigadores)


En la entrada, tenemos el archivo de configuración de artillería con dos entornos: Apache y RoadRunner. Ambos trabajan con la misma instancia de Magento, por lo que aquí están en igualdad de condiciones.


Escenarios de prueba


Los siguientes escenarios se utilizaron para medir el rendimiento de las dos soluciones.


Escenario 1. Crear una categoría
  - name: "S1. Create category" flow: - loop: - post: url: "/rest/V1/categories" json: category: name: "name-{{prefix}}-{{ $loopCount }}" parent_id: 2 is_active: true count: 100 

Escenario 2. Obtener una lista de países
  - name: "S2. Countries list" flow: - loop: - get: url: "/rest/V1/directory/countries" count: 100 

Escenario 3: Listado de tipos de productos
  - name: "S3. Product types list" flow: - loop: - get: url: "/rest/V1/products/types" count: 100 

Escenario 4. Obtener una lista de conjuntos de atributos
  - name: "S4. Product attribute sets list" flow: - loop: - get: url: "/rest/V1/products/attribute-sets/sets/list?searchCriteria" count: 100 

Escenario 5. Obtener una categoría
  - name: "S5. Category get" flow: - loop: - get: url: "/rest/V1/categories/2" count: 100 

Escenario 6. Creación de producto
  - name: "S6. Create product" flow: - loop: - post: url: '/rest/V1/products' json: product: sku: "sku-{{prefix}}-{{ $loopCount }}" name: "name-{{prefix}}-{{ $loopCount }}" attribute_set_id: 4 price: 100 type_id: "simple" count: 100 

Escenario 7. Recuperación de una lista de productos
  - name: "S7. Get product list" flow: - loop: - get: url: "/rest/V1/products?searchCriteria[pageSize]=20" count: 100 

Resultado


Después de ejecutar todos los scripts alternativamente a través de RoadRunner y Apache, se obtuvieron medianas de la duración de la consulta. Según las medianas, la velocidad de todos los escenarios difiere en aproximadamente el mismo valor de ~ 50 ms.


Resultado de la prueba de rendimiento.
Resultado de la prueba de rendimiento.


Resumen


Un experimento práctico confirmó las suposiciones sobre la constancia de la ganancia de rendimiento de RoadRunner en una aplicación específica. El uso de esta herramienta le permite acelerar el procesamiento de solicitudes de aplicaciones para una constante igual al tiempo de inicialización del entorno y las dependencias.


En los controladores ligeros, esto le permite acelerar la aplicación a veces, en el caso pesado, casi no da un efecto tangible. Si su código es lento, lo más probable es que el plátano no lo ayude.


Si su aplicación está bien escrita, lo más probable es que no haya problemas con su funcionamiento a través de RoadRunner, pero si la aplicación requiere adaptación para su uso con RoadRunner, lo más probable es que hubiera requerido lo mismo sin RoadRunner para observar más claramente las capas de la arquitectura. y siguiendo los estándares de desarrollo en el campo.


Magento Open Source es generalmente adecuado para el lanzamiento en el entorno dado, sin embargo, requiere mejoras en la capa de transporte y la corrección de la lógica empresarial para evitar un comportamiento incorrecto durante las solicitudes repetidas dentro del mismo proceso. Además, el uso de RoadRunner impone ciertas restricciones a los enfoques de desarrollo, sin embargo, no contradicen las prácticas bien establecidas.


Finalmente una buena captura de pantalla. ¿Cuándo verás las solicitudes de Magento con este tiempo de respuesta?

Choque


Referencias


  1. Ejemplo del articulo
  2. Sitio web oficial de RoadRunner

Source: https://habr.com/ru/post/466105/


All Articles