Programación concurrente de PHP IO

El problema de la E / S simultánea siempre ha sido un desafío técnico en la programación de back-end, desde el primer proceso de bloqueo de sincronización sincrónica, a multiproceso / multiproceso, a la E / S asincrónica actual, corutina. Debido a que los programadores de PHP tienen un potente marco de trabajo LAMP, saben muy poco sobre el conocimiento subyacente. El propósito de este artículo es presentar en detalle los diversos intentos de PHP para la programación concurrente de E / S, y finalmente introducir el uso de Swoole para comprender el problema de la E / S concurrente.

Bloqueo síncrono multiproceso / multiproceso

Los primeros programas del lado del servidor utilizaron múltiples procesos y subprocesos para resolver el problema de las E / S concurrentes. El modelo de proceso apareció por primera vez, y el concepto de proceso existe desde el nacimiento del sistema Unix. Los primeros programas del lado del servidor generalmente aceptaron Aceptar para crear un proceso después de una conexión de cliente, y luego el proceso secundario ingresó al bucle para bloquear e interactuar sincrónicamente con la conexión del cliente, enviar y recibir datos.

El modo de subprocesos múltiples aparece más tarde, los subprocesos son más livianos que los procesos y los subprocesos comparten pilas de memoria, por lo que la interacción entre diferentes subprocesos es muy fácil de lograr. Para programas como salas de chat, las conexiones de los clientes pueden interactuar, y los jugadores en la sala de chat pueden enviar mensajes a cualquier otra persona. Es muy sencillo de implementar en modo multihilo, puede leer y escribir directamente una conexión de cliente en el hilo. El modelo multiproceso requiere el uso de tuberías, colas de mensajes y memoria compartida para lograr la interacción de datos, y puede realizarse mediante una tecnología compleja llamada comunicación entre procesos (IPC).

Ejemplo de código:

El proceso del modelo multiproceso / hilo es

  1. Cree un socket, enlace el puerto del servidor (bind), escuche el puerto (listen), use PHP stream_socket_server, una función para completar los tres pasos anteriores, por supuesto, también puede usar la extensión de sockets php para lograr por separado.
  2. Ingrese el ciclo while, bloquee la operación de aceptación y espere a que entre la conexión del cliente. En este punto, el programa entrará en un estado de suspensión hasta que un nuevo cliente inicie una conexión con el servidor y el sistema operativo activará el proceso. La función de aceptación devuelve el socket al que se conecta el cliente
  3. El proceso principal utiliza la bifurcación (php: pcntl_fork) para crear procesos secundarios en el modelo multiproceso, y pthread_create (php: new Thread) para crear subprocesos secundarios en el modelo multiproceso. Si no hay una declaración especial a continuación, el proceso se utilizará para significar proceso / hilo.
  4. Después de que el proceso secundario se crea con éxito, ingresa un ciclo while, bloqueando la llamada recv (php: fread), esperando que el cliente envíe datos al servidor. Después de recibir los datos, el programa del servidor los procesa y luego usa send (php: fwrite) para enviar una respuesta al cliente. Los servicios de conexión larga continuarán interactuando con los clientes, mientras que los servicios de conexión corta generalmente se cierran cuando reciben una respuesta.
  5. Cuando se cierra la conexión del cliente, el proceso secundario sale y destruye todos los recursos. El proceso principal reciclará este proceso hijo.

 

El mayor problema con este modelo es que el costo del proceso / creación y destrucción de subprocesos es alto. Por lo tanto, el patrón anterior no se puede aplicar a programas de servidor muy ocupados. La versión mejorada correspondiente resuelve este problema, que es el modelo clásico Leader-Follower .

Ejemplo de código:

Su característica es que se crearán N procesos después de que comience el programa. Cada proceso secundario ingresa Aceptar, esperando que ingrese una nueva conexión. Cuando el cliente se conecta al servidor, se activará uno de los procesos secundarios para comenzar a procesar las solicitudes del cliente y ya no aceptará nuevas conexiones TCP. Cuando se cierra esta conexión, el proceso secundario se liberará, volverá a ingresar Aceptar y participará en el procesamiento de la nueva conexión.

La ventaja de este modelo es que puede reutilizar completamente los procesos sin consumo adicional, y el rendimiento es muy bueno. Muchos programas de servidor comunes se basan en este modelo, como Apache, PHP-FPM.

El modelo multiproceso también tiene algunas desventajas.

  1. Este modelo depende en gran medida del número de procesos para resolver el problema de concurrencia. Una conexión de cliente necesita ocupar un proceso, y el número de procesos de trabajo tiene tantas capacidades de procesamiento concurrente. El número de procesos que puede crear el sistema operativo es limitado.
  2. Iniciar una gran cantidad de procesos causará costos adicionales de programación de procesos. Cuando cientos de procesos pueden consumir menos del 1% de la CPU en la programación de cambio de contexto del proceso, puede ignorarse.Si se inician miles o incluso decenas de miles de procesos, el consumo aumentará linealmente. El consumo de programación puede representar decenas o incluso el 100% de la CPU.

Además, hay algunos escenarios en los que el modelo multiproceso no se puede resolver, por ejemplo, un programa de chat instantáneo (IM), donde un servidor necesita mantener decenas de miles o incluso cientos de miles de millones de conexiones al mismo tiempo (el clásico problema C10K), el modelo multiproceso no puede manejarlo.

Otro escenario es la debilidad del modelo multiproceso. Por lo general, el servidor web inicia 100 procesos. Si una solicitud consume 100 ms, 100 procesos pueden proporcionar 1000 qps. Esta capacidad de procesamiento no es mala. Sin embargo, si desea llamar a la interfaz Http externa en la solicitud, como QQ y el inicio de sesión de Weibo, tomará mucho tiempo. Una solicitud demora 10 segundos. Ese proceso solo puede procesar 0.1 solicitudes por segundo, y 100 procesos solo pueden alcanzar 10qps, lo cual es demasiado pobre.

¿Existe una tecnología que pueda manejar todas las IO simultáneas en un solo proceso? La respuesta es sí, esta es la tecnología de multiplexación IO.

Multiplexación de E / S / bucle de eventos / sin bloqueo asincrónico

De hecho, el historial de reutilización de E / S es tan largo como multiproceso. Linux ha proporcionado una llamada de sistema selecta muy temprano, que puede mantener 1024 conexiones en un proceso. Más tarde, se agregó la llamada al sistema de sondeo, y sondeo realizó algunas mejoras para resolver el problema del límite de 1024, que puede mantener cualquier número de conexiones. Pero otro problema con select / poll es que necesita un bucle para detectar si hay un evento en la conexión. Aquí es donde surge el problema: si el servidor tiene 1 millón de conexiones y solo una conexión envía datos al servidor a la vez, select / poll necesita realizar un bucle 1 millón de veces, de las cuales solo 1 es alcanzado y los 990,000 restantes 9999 veces no son válidos, desperdiciando recursos de la CPU en vano.

Hasta que el kernel de Linux 2.6 proporcione una nueva llamada al sistema epoll, que puede mantener un número ilimitado de conexiones, y sin sondeo, esto realmente resuelve el problema de C10K. Ahora, todo tipo de programas de servidor IO asíncrono de alta concurrencia se basan en epoll, como Nginx, Node.js, Erlang, Golang. Los programas de un solo proceso de un solo subproceso como Node.js pueden mantener más de 1 millón de conexiones TCP, todo gracias a la tecnología epoll.

Los programas asincrónicos sin bloqueo multiplexados IO utilizan el modelo clásico Reactor . Reactor, como su nombre lo indica, significa el reactor. No maneja ningún envío y recepción de datos. Solo es posible monitorear los cambios de evento de un controlador de socket.

Reactor tiene 4 operaciones principales:

  1. agregar agregar socket para escuchar el reactor, que puede ser socket de escucha o socket de cliente, también puede ser pipe, eventfd, señal, etc.
  2. establecer modificar la supervisión de eventos, puede establecer el tipo de supervisión, como legible y grabable. Es legible y fácil de entender. Para el socket de escucha, debe aceptarse una nueva conexión de cliente. Para que la conexión del cliente sea recibir datos, se requiere recv. Los eventos de escritura son más difíciles de entender. Un SOCKET tiene un área de caché. Si desea enviar 2 millones de datos a la conexión del cliente, no puede enviarse al mismo tiempo. El área de caché TCP predeterminada del sistema operativo es de solo 256K. Solo puede enviar 256K a la vez. Una vez que el búfer esté lleno, el envío devolverá un error EAGAIN. En este momento, es necesario monitorear el evento de escritura. En la programación puramente asincrónica, debe monitorear la escritura para asegurarse de que la operación de envío no bloquee por completo.
  3. del se retira del reactor y ya no escucha eventos
  4. la devolución de llamada es la lógica de procesamiento correspondiente después del evento, que generalmente se formula en add / set. El lenguaje C se implementa con punteros de función, JS puede usar funciones anónimas, PHP puede usar funciones anónimas, matrices de métodos de objetos y nombres de funciones de cadena.

Reactor es solo un generador de eventos. Las operaciones reales en los controladores de socket, como conectar / aceptar, enviar / recibir y cerrar, se realizan en la devolución de llamada. La codificación específica puede referirse al siguiente pseudocódigo:

El modelo Reactor también se puede utilizar junto con multiproceso y multiproceso, tanto para lograr E / S asincrónicas sin bloqueo, como también para usar varios núcleos. Los actuales programas de servidor asíncrono populares son todos de esta manera: como

  • Nginx: Reactor multiproceso
  • Nginx + Lua: Reactor multiproceso + corutina
  • Golang: Reactor de un solo hilo + corutina de múltiples hilos
  • Swoole: Reactor multiproceso + Trabajador multiproceso

¿Qué es la corutina?

La rutina es en realidad un modelo asíncrono de IO Reactor desde la perspectiva de la tecnología subyacente. La capa de aplicación implementa la programación de tareas por sí misma y usa Reactor para cambiar cada hilo de modo de usuario que se está ejecutando actualmente, pero el código de usuario desconoce por completo la existencia de Reactor.

Práctica de programación IO concurrente de PHP

Extensiones relacionadas con PHP

  • Stream: paquete de socket proporcionado por el kernel PHP
  • Sockets: encapsulación de la API de Socket subyacente
  • Libevent: encapsulación de la biblioteca libevent
  • Evento: basado en la encapsulación más avanzada de Libevent, proporciona soporte para interfaces orientadas a objetos, temporizadores y procesamiento de señales
  • Pcntl / Posix: multiproceso, señal, soporte de gestión de procesos
  • Pthread: Multithreading, gestión de hilos, soporte de bloqueo
  • PHP también tiene extensiones relacionadas para memoria compartida, semáforos y colas de mensajes
  • PECL: la biblioteca extendida de PHP, que incluye la parte inferior del sistema, análisis de datos, algoritmos, controladores, computación científica, gráficos, etc. Si no se encuentra en la biblioteca estándar de PHP, puede encontrar la función deseada en PECL.

Ventajas y desventajas del lenguaje PHP

Ventajas de PHP:

  1. El primero es simple. PHP es más simple que cualquier otro lenguaje. PHP realmente se puede iniciar en una semana. C ++ tiene un libro llamado "Estudio en profundidad de 21 días de C ++". De hecho, es imposible aprender en 21 días. Incluso se puede decir que C ++ no se puede dominar en profundidad en 3-5 años. Pero PHP definitivamente puede comenzar en 7 días. Por lo tanto, la cantidad de programadores PHP es muy grande, y el reclutamiento es más fácil que en otros lenguajes.
  2. La función de PHP es muy poderosa, porque la biblioteca estándar oficial y la biblioteca de extensiones de PHP proporcionan el 99% de las cosas que se pueden usar para la programación del servidor. Biblioteca de extensiones PHP PECL cualquier función que desee.

Además, PHP tiene una historia de más de 20 años, el ecosistema es muy grande y se puede encontrar mucho código en Github.

Desventajas de PHP:

  1. El rendimiento es relativamente pobre, porque es un script dinámico después de todo, y no es adecuado para operaciones intensivas. Si también está escrito en PHP y luego escrito en c ++, la versión de PHP es cien veces peor.
  2. La convención de nomenclatura de funciones es deficiente. Todos lo saben. PHP es más práctico y no tiene especificaciones. El nombramiento de algunas funciones es muy confuso, por lo que cada vez que tiene que pasar por el manual de PHP.
  3. La granularidad de la interfaz de las estructuras y funciones de datos proporcionadas es relativamente gruesa. PHP solo tiene una estructura de datos de matriz, y la capa inferior se basa en HashTable. PHP's Array es una colección de funciones como Map, Set, Vector, Queue, Stack, Heap y otras estructuras de datos. Además, PHP tiene un SPL que proporciona la encapsulación de clase de otras estructuras de datos.

Entonces PHP

  1. PHP es más adecuado para programas prácticos a nivel de aplicación, una herramienta para el desarrollo empresarial y la implementación rápida
  2. PHP no es adecuado para desarrollar software de bajo nivel
  3. Utilice C / C ++, JAVA, Golang y otros lenguajes compilados estáticos como complemento de PHP, combinando dinámicos y estáticos.
  4. Uso de herramientas IDE para lograr la finalización automática, indicaciones gramaticales  

Extensión PHP Swoole

Basado en las extensiones anteriores, el uso de PHP puro puede implementar completamente el servidor web asíncrono y los programas cliente. Pero si desea implementar un subproceso multi-IO, todavía hay muchas tareas de programación tediosas por hacer, que incluyen cómo administrar la conexión, cómo garantizar el principio de transmisión y recepción de datos y el procesamiento de protocolos de red. Además, el rendimiento del código PHP en la parte de procesamiento del protocolo es relativamente pobre, por lo que comencé un nuevo proyecto de código abierto Swoole, usando lenguaje C y PHP para completar este trabajo. El módulo empresarial flexible utiliza PHP para desarrollarse con alta eficiencia, y la parte inferior subyacente y la parte de procesamiento de protocolo se implementan en lenguaje C, lo que garantiza un alto rendimiento. Se carga en PHP de manera extendida, proporcionando un marco de comunicación de red completo y luego un código PHP para escribir algunos negocios. Su modelo se basa en el Reactor multiproceso + Trabajador multiproceso, que admite tanto asíncrono como semisincrónico y semisincrónico.

Algunas características de Swoole:

  • Aceptar hilo para resolver Aceptar cuellos de botella de rendimiento y problemas de grupos de choque
  • Múltiples hilos de E / S pueden hacer un mejor uso de múltiples núcleos
  • Proporciona dos modos: totalmente asíncrono y semi-síncrono y semi-asíncrono.
  • El modo asincrónico se usa para partes con alta IO concurrente
  • Modo síncrono para lógica empresarial compleja
  • La capa inferior permite atravesar todas las conexiones, enviar datos entre sí, fusionar y dividir automáticamente paquetes de datos, y la atomicidad de transmisión de datos.

Proceso de Swoole / modelo de hilo:

Flujo de ejecución del programa Swoole:

Uso de la extensión PHP + Swoole para realizar una programación de comunicación asincrónica

El código de ejemplo se puede ver en la https://github.com/swoole/swoole-src página de inicio.

Servidor TCP y cliente

Servidor TCP asincrónico :

 

Aquí el nuevo objeto swoole_server, luego los parámetros se pasan al HOST de escucha y al PUERTO, y luego configuran tres funciones de devolución de llamada, respectivamente, onConnect tiene una nueva conexión para ingresar, onReceive recibió los datos de un cliente, onClose un cliente cerró la conexión . Finalmente, llame a start para iniciar el programa del servidor. La capa inferior de swoole iniciará un número correspondiente de subprocesos de Reactor y procesos de Trabajador de acuerdo con cuántos núcleos de CPU tiene la máquina actual.

Cliente asincrónico:

El método de uso del cliente es similar al servidor, excepto que hay 4 eventos de devolución de llamada. OnConnect se conecta con éxito al servidor y luego puede enviar datos al servidor. onError no pudo conectarse al servidor. El servidor onReceive envía datos a la conexión del cliente. Conexión cerrada cerrada.

Después de configurar la devolución de llamada del evento, inicie una conexión al servidor, los parámetros son la IP, el PUERTO y el tiempo de espera del servidor.

 

Cliente de sincronización:

El cliente de sincronización no necesita establecer ninguna devolución de llamada de evento, no tiene monitoreo de Reactor y está bloqueando el serial. Espere a que se complete IO antes de continuar con el siguiente paso.

Tareas asincrónicas:

La función de tarea asincrónica se utiliza para ejecutar una función de bloqueo o que consume mucho tiempo en un programa de servidor puramente asincrónico. La implementación subyacente utiliza un grupo de procesos, onFinish se activará después de que se complete la tarea, y los resultados del procesamiento de la tarea se pueden obtener en el programa. Por ejemplo, es necesario transmitir un IM. Si se transmite directamente en código asincrónico, puede afectar el procesamiento de otros eventos. Además, la lectura y escritura de archivos también se puede lograr mediante tareas asincrónicas, ya que los identificadores de archivos no pueden usar la supervisión de Reactor como sockets. Debido a que el identificador de archivo siempre es legible, leer el archivo directamente puede bloquear el programa del servidor, y usar tareas asincrónicas es una muy buena opción.

Temporizador asíncrono de milisegundos

Estas dos interfaces implementan las funciones setInterval y setTimeout similares a JS, y se pueden configurar para implementar una función en intervalos de n milisegundos o ejecutar una función después de n milisegundos.

Cliente asincrónico de MySQL

swoole también proporciona un cliente asíncrono MySQL con un grupo de conexiones incorporado, puede establecer el número máximo de conexiones MySQL. Las solicitudes SQL simultáneas pueden reutilizar estas conexiones en lugar de crearlas repetidamente, lo que protege a MySQL de ser agotado por los recursos de conexión.

Cliente Redis asincrónico

Programa web asincrónico

La lógica del programa es leer datos de Redis y luego mostrar la página HTML. El rendimiento de la medición de presión usando ab es el siguiente:

Los resultados de la prueba de rendimiento de la misma lógica en php-fpm son los siguientes:

Programa WebSocket

Swoole tiene un servidor websocket incorporado, que puede implementar la función de impulsar activamente páginas web, como WebIM. Hay un proyecto de código abierto que se puede utilizar como referencia. https://github.com/matyhtf/php-webim

PHP + Swoole coroutine

La programación asincrónica generalmente usa el método de devolución de llamada. Si encuentra una lógica muy complicada, puede anidar funciones de devolución de llamada capa por capa. Las rutinas pueden resolver este problema. Puede escribir código secuencialmente, pero el tiempo de ejecución es asíncrono y sin bloqueo. Los ingenieros de Tencent se basan en la extensión Swoole y la sintaxis de rendimiento / generador de PHP5.5 para lograr rutinas similares a Golang, el nombre del proyecto es TSF (Tencent Server Framework), dirección de proyecto de código abierto: https://github.com/tencent-php/tsf . En la actualidad, existen aplicaciones a gran escala en QQ empresarial de Tencent, el proyecto de cuenta pública QQ y el proyecto ilegal de descuidar las ruedas.

El uso de TSF también es muy simple: las siguientes llamadas son 3 operaciones IO, que son completamente de escritura en serie. Pero en realidad se ejecuta de forma asincrónica y sin bloqueo. El planificador inferior de TSF asume la ejecución del programa y continuará ejecutándose después de que se complete el IO correspondiente.

Use PHP + Swoole en Raspberry Pi

Tanto PHP como Swoole se pueden compilar y ejecutar en la plataforma ARM, por lo que PHP + Swoole también se puede usar en sistemas Raspberry Pi para desarrollar programas de comunicación en red.

Publicado 23 artículos originales · ganado elogios 2 · Vistas 5240

Supongo que te gusta

Origin blog.csdn.net/bianlitongcn/article/details/104990539
Recomendado
Clasificación