Explore la innovación del modelo de red Reactor en el campo de aplicación actual

Este artículo es compartido por Huawei Cloud Community " Controlling the Future of Network Technology: Explorating the Innovation of the Reactor Network Model in Today's Application Fields ", autor: Lion Long.

Este artículo presenta el modelo de red Reactor en el diseño de red de Linux y su importancia en las aplicaciones prácticas. El modelo Reactor es un patrón de diseño clásico basado en eventos ampliamente utilizado en la construcción de servidores web escalables de alto rendimiento. Exploraremos los fundamentos y los componentes del modelo Reactor, y detallaremos cómo se implementa el modelo Reactors en la programación de red de Linux.

1. Introducción a la programación del modelo de red de reactores

Reactor convierte la detección de IO en el procesamiento de eventos y es un mecanismo de eventos asíncronos. El reactor utilizará la multiplexación de E/S para la detección de E/S. Los multiplexores de E/S son generalmente: selección, sondeo, epoll.

La lógica general del reactor:

(1) socket() crea un socket, listenfd;

(2) bind(), listen() configure listenfd, bind y monitor;

(3) listenfd registra el evento de lectura, que es administrado por epoll;

(4) Disparador de evento de lectura, aceptación de devolución de llamada;

(5) El cliente se conecta a clientfd para formar un evento de lectura;

(6) Funciones de devolución de llamada relacionadas con eventos relacionados

1.1 Establecer una conexión

Recibir conexiones de clientes.

//...int 

epfd=epoll_create(1);//Crear objeto epoll 

//... 

int listenfd=socket(AF_INET,SOCK_STREAM,0);//Crear socket 

//... 

struct epoll_event ev; 

ev.events=EPOLLIN; 

epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev)//register Event 

//... 

// Cuando el evento de lectura de listenf d se activa, llame a accept para recibir la conexión 

struct sockaddr_in clientaddr; 

socklen_t len=sizeof(clientaddr); 

int clientfd=accept(listenfd,(struct sockaddr *)&clientaddr,&len); struct epoll_event ev; ev.events =EPOLLIN; epol 

l_ctl 

( 

epfd, EPOLL_CTL_ADD, clientfd, &ev)/ /Registrar el evento de lectura de la nueva conexión // 

...

Conéctese a servicios de terceros.

//... 

int epfd=epoll_create(1);//crear objeto epoll 

//... 

int fd=socket(AF_INET,SOCK_STREAM,0);//crear socket 

//... 

struct sockaddr_in clientaddr; 

socklen_t len=sizeof(clientaddr); 

connect(fd,(struct sockaddr *)&client addr,&len);//servicio de conexión//... struct epoll _event ev; ev.events 

= 

EPOLLOUT 

; 

epoll_ctl(epfd,EPOLL_CTL_ADD,fd,&ev)//registration event 

//... 

//Cuando se activa el evento de escritura de fd, la conexión se establece correctamente if 

(status==e_connecting && ev.events==EPOLLOUT) 

{ 

status=e_connected; 

e poll_ctl(epfd,EPOLL_CTL_DEL.fd ,NULO); 

} 

//...

1.2 Desconectar

//... 

if(ev.events & EPOLLRDHUP) 

{ 

// Cierra el servidor de lectura end 

close_read(fd); 

} 

if(ev.events & EPOLLHUP) 

{ 

// Cierra el servidor de lectura y escritura end 

close(fd); 

} 

//...

1.3 Llegada de datos

// ... 

if(ev.events & EPOLLIN) 

{ 

while(1) 

{ 

int n=recv(clientfd,buffer,buffer_size,0); 

if(n<0) 

{ 

if(errno==EINTR) 

continuar; 

if(errno==EWOULDBLOCK) 

romper; 

close(clientefd); 

} 

else if(n==0) 

{ 

close_read(); 

} 

else 

{ 

// 处理业务

} 

} 

// ... 

} 

// ...

1.4 Envío de datos

// ... 

if(ev.events & EPOLLOUT) 

{ 

int n=send(clientfd,buffer,buffer_size,0); 

if(n<0) 

{ 

if(errno==EINTR) 

continuar; 

if(errno==EWOULDBLOCK) 

{ 

struct epoll_event e; 

e.events=EPOLLOUT; 

epoll_ctl(epfd,EPOLL_CTL_ADD,clientfd,&e)//注册事件

return; 

// romper; 

} 

close(clientefd); 

} 

else if(n==buffer_size) 

{ 

epoll_ctl(epfd,EPOLL_CTL_DEL,clientfd,NULL); 

// epoll_ctl(epfd,EPOLL_CTL_MOD,clientfd,&e); 

} 

// ... 

} 

//...

1.5 Preguntas comunes sobre el reactor

1. grupo de choque epoll

¿Qué es "Grupo impactante"? La programación de redes a menudo usa modelos de subprocesos múltiples y procesos múltiples. Cada subproceso o proceso tiene un objeto epoll. El listenfd generado por socket(), bind() y listen() puede administrar múltiples objetos epoll. Cuando llega una aceptación, se notifican todas las epolls y todos los procesos o subprocesos responden a este evento al mismo tiempo. Sin embargo, solo una aceptación tiene éxito al final. Este es el "Grupo Impactante".

2. Gatillo horizontal y gatillo de borde

Disparador horizontal: cuando hay datos en el búfer de lectura, dispara hasta que se lean los datos.

Activador de borde: Activar una vez cuando llega un evento. Las operaciones de lectura y escritura generalmente necesitan cooperar con el ciclo para completar todas las operaciones de lectura y escritura.

3. ¿Por qué se debe usar el reactor con IO sin bloqueo?

Principalmente por tres motivos:

(1) En un entorno de subprocesos múltiples, un listenfd será administrado por múltiples objetos epoll (multiplexor de E/S). Cuando llega una conexión, se notificará a todos los epolls y todos responderán, pero al final solo se aceptará uno; si se usa el bloqueo, los demás epolls siempre se bloquearán. Por lo tanto, es mejor usar IO sin bloqueo para regresar a tiempo.

(2) Bajo el disparador de borde, el disparador de eventos leerá el evento, luego el búfer debe leerse vacío en un bucle de eventos; si se usa el modo de bloqueo, cuando se lean los datos en el búfer de lectura, siempre se bloquearán y no podrán regresar.

(3) Seleccionar errores. Cuando un nuevo segmento de datos llega a un búfer de recepción de socket, select informa que el descriptor de socket es legible, pero luego, la pila de protocolo verifica el error de suma de verificación del nuevo segmento y luego descarta el segmento. En este momento, no hay datos para leer cuando se llama a recv/read; si el socket no está configurado para no bloquear, este recv/read bloqueará el subproceso actual.

4. ¿Se debe combinar la multiplexación de E/S con E/S sin bloqueo?

No, también se puede utilizar el modo de bloqueo. Por ejemplo, MySQL usa select para recibir conexiones y luego usa un subproceso para procesar una conexión; también puede usar una llamada al sistema para obtener primero la cantidad de bytes en el búfer de lectura y luego leer los datos una vez, pero esto conduce a una eficiencia relativamente baja.

int n=EVBUFFER_MAX_READ_DEFAULT; 

ioctl(fd,FIONREAD,&n);//Obtener el número de bytes de datos en el búfer de lectura

Dos, escenarios de aplicación de reactores.

Escenarios que utilizan un solo reactor y escenarios que utilizan varios reactores. El uso de múltiples reactores tiene diferentes usos de subprocesos múltiples y procesamiento múltiple.

2.1, redis - usa un solo reactor

Redis es un componente de base de datos de red con una estructura de clave-valor, estructuras de datos enriquecidas y operaciones en la memoria. El procesamiento de comandos de Redis es de un solo subproceso.

2.1.1 ¿Por qué redis utiliza un solo reactor?

Para comprender por qué redis solo usa un único reactor, debe comprender que el procesamiento de comandos de redis es de subproceso único .

Redis proporciona estructuras de datos enriquecidas, y bloquear estas estructuras de datos es muy complicado, por lo que Redis usa un solo subproceso para el procesamiento; debido a que se usa un solo subproceso para el procesamiento de comandos, y la lógica comercial central es un solo subproceso, entonces no se puede usar ninguna cantidad de reactores para manejarlo; por lo tanto, redis usa un solo reactor.

Además, la complejidad temporal de los comandos específicos operativos de redis es relativamente baja y no es necesario utilizar varios reactores.

2.1.2, diagrama de bloques del reactor de procesamiento redis

cke_140.png

2.1.3, optimización redis del reactor

Optimicé la lógica comercial e introduje hilos IO:

Después de recibir los datos, envíe los datos al subproceso de IO para su procesamiento; antes de enviar los datos, coloque los datos empaquetados en el subproceso de IO para su procesamiento y luego envíelos. En referencia a la figura anterior, es poner (leer+decodificar) en el hilo para procesamiento, y poner (codificar+escribir) en el hilo para procesamiento.

razón:

Para un solo subproceso, cuando los datos recibidos o enviados son demasiado grandes, la carga del subproceso será demasiado grande y es necesario utilizar varios subprocesos para el procesamiento de datos de IO. Especialmente en el proceso de solución de protocolo, los datos son enormes y consumen mucho tiempo, y es necesario abrir un subproceso de E/S para su procesamiento.

Ejemplo de escenario:

El cliente carga registros de registro; el cliente obtiene registros de clasificación.

2.1.4 Mirando el código fuente de redis desde la perspectiva del reactor

Cree un objeto de encuesta electrónica:

cke_141.png

Cree un socket y vincule al oyente:

cke_142.png

poner listenfd en la gestión de epoll:

cke_143.png

Escuche los eventos:

cke_144.png

Manejar eventos:

cke_145.png

Registre eventos de lectura para clientfd:

cke_146.png

2.2, memcached: use múltiples reactores en modo de subprocesos múltiples

Memcached es una estructura clave-valor, un componente de base de datos de red que opera en la memoria. El procesamiento de comandos de Memcached es de subprocesos múltiples.

Memcached necesita libevent, que es una biblioteca basada en eventos, y memcached se basa en libevent para el uso de la red.

2.2.1 ¿Por qué memcached usa reactores múltiples?

A diferencia de redis, la estructura clave-valor de memcached admite estructuras de datos enriquecidas. La estructura de datos utilizada por su valor es relativamente simple y el bloqueo es relativamente fácil. Por lo tanto, se pueden introducir subprocesos múltiples para mejorar la eficiencia.

2.2.2 ¿Cómo maneja memcached el reactor?

El subproceso principal de memcached tendrá un reactor, que es el principal responsable de recibir las conexiones; después de recibir la conexión, después del equilibrio de carga, le indicará al reactor del subproceso secundario a través de la tubería (tubería) y entregará el fd del cliente al reactor del subproceso para su administración; cada subproceso maneja la lógica comercial correspondiente.

cke_147.png

2.2.3 Mirando el código fuente de Memcached desde la perspectiva del reactor

Descargue el último memcached de github .

Iniciar el análisis del código fuente:

Cree un socket y vincule al oyente:

Registre el evento de lectura listenfd:

Asigne clientfd a un hilo específico y agregue un evento de lectura:

cke_2420.png

2.3, nginx: use múltiples reactores en modo multiproceso

Nginx puede invertir el proxy y usar procesos múltiples para manejar negocios.

El maestro creará listenfd, enlazará y escuchará; bifurcará múltiples procesos, cada proceso tiene su propio objeto epoll y listenfd es administrado por múltiples objetos epoll. En este momento, habrá grupos impactantes que deben tratarse; los eventos se manejan a través del balanceo de carga.

2.3.1 Resolver el problema del "grupo chocante"

Método de bloqueo. Nginx abrirá una memoria compartida, colocará el candado en la memoria compartida y múltiples procesos competirán por el candado, y solo aquellos que compitan por el candado podrán aceptar la conexión.

2.3.2 Equilibrio de carga

Definir el número máximo de conexiones para un proceso, cuando el número de conexiones supere los 7/8 del número total de conexiones, el proceso suspenderá la aceptación de conexiones y dejará la oportunidad para otros procesos.

De esta forma, un proceso no tendrá demasiadas conexiones, mientras que otros procesos tendrán muy pocas conexiones; por lo tanto, el número de conexiones para cada proceso estará relativamente equilibrado.

Cuando la cantidad de conexiones aceptadas por todos los procesos alcanza los 7/8 de la cantidad total de conexiones, significa que nginx se volverá muy lento para aceptar conexiones.

Resumir

En este artículo, se explora en profundidad el modelo de red de Linux Reactor y se destaca su importancia y ventajas en las aplicaciones prácticas. El modelo Reactors es un patrón de diseño de red eficiente que se destaca en el manejo de conexiones simultáneas, lo que nos permite crear aplicaciones de red escalables y de alto rendimiento.

Primero, comprenda los principios básicos del modelo Reactores. Utiliza un enfoque basado en eventos para monitorear eventos de entrada a través de un bucle principal y, una vez que ocurre un evento, se llamará al controlador correspondiente. Este diseño sin bloqueo permite que el servidor maneje de manera eficiente una gran cantidad de conexiones simultáneas sin crear un hilo para cada conexión.

Luego, se discute la aplicación práctica del modelo Reactors en el diseño de redes Linux. También analiza en profundidad el procesamiento de eventos y el mecanismo de devolución de llamada para ayudar a los lectores a comprender cómo optimizar el diseño de las aplicaciones de red.

En comparación con el modelo tradicional de subprocesos múltiples o procesos múltiples, el modelo Reactors puede hacer un mejor uso de los recursos del sistema, reducir la sobrecarga del cambio de contexto y la creación de subprocesos y, por lo tanto, mejorar las capacidades de procesamiento concurrente de las aplicaciones.

Este artículo tiene como objetivo ayudar a los lectores a comprender completamente el modelo de red de Linux Reactor y alentarlos a usar este modelo en sus propias aplicaciones de red para crear aplicaciones de red más confiables y de mayor rendimiento. Dominar el conocimiento del modelo Reactors permitirá a los lectores navegar con más confianza por el futuro de la tecnología de red y enfrentar los desafíos en constante cambio.

Haga clic para seguir y conocer las nuevas tecnologías de Huawei Cloud por primera vez~

Los 8 lenguajes de programación más demandados en 2023: Fuerte PHP, baja demanda de C/C++ Notas del programador CherryTree 1.0.0.0 lanzado Proyecto CentOS declarado "abierto a todos" MySQL 8.1 y MySQL 8.0.34 lanzados oficialmente GPT-4 ¿cada vez más estúpido? La tasa de precisión cayó del 97,6 % al 2,4 %.Microsoft : mayores esfuerzos para usar Rust Meta en Windows 11 Acercar: lanzar el modelo de lenguaje grande de código abierto Llama 2, que es gratuito para uso comercial.El padre de C# y TypeScript anunció el último proyecto de código abierto: ¿TypeChat no quiere mover ladrillos, pero también quiere cumplir con los requisitos? Tal vez este proyecto de código abierto de GitHub de 5k estrellas pueda ayudar: el 25.° aniversario de MetaGPT Wireshark, el analizador de paquetes de red de código abierto más poderoso
{{o.nombre}}
{{m.nombre}}

Supongo que te gusta

Origin my.oschina.net/u/4526289/blog/10089919
Recomendado
Clasificación