Estrategias relacionadas con la optimización del rendimiento de la red Linux

Este artículo presenta la estrategia de optimización del rendimiento de la red Linux de abajo hacia arriba "

00

Optimización de la configuración de la tarjeta de red

Comenzar desde 0 es la cualidad básica de los productores de código

Configuración de la función de la tarjeta de red

En términos generales, para completar la misma función, el rendimiento del hardware supera con creces al del software. Con el desarrollo de hardware, se admiten cada vez más funciones. Por tanto, debemos intentar descargar la función al hardware

Utilice ethtool -k para ver la lista de funciones admitidas por la tarjeta de red y el estado actual. El siguiente es el resultado de una máquina virtual del autor.
Inserte la descripción de la imagen aquí

Nota: La salida de diferentes tarjetas de red es diferente y la salida de diferentes versiones del kernel será ligeramente diferente.

Generalmente, es necesario habilitar las siguientes funciones:

  1. rx-checksumming: verifica la suma de control del mensaje recibido.

  2. tx-checksumming: calcula la suma de comprobación de los mensajes enviados.

  3. dispersión-recopilación: admite el modo de memoria dispersada-recopilación, es decir, la memoria de la parte de datos del mensaje enviado puede ser discontinua y dispersa en varias páginas.

  4. tcp-segmento-descarga: admite la segmentación de paquetes grandes de TCP.

  5. udp-fragmentation-offload: admite la fragmentación automática de paquetes UDP.

  6. genérico-segmento-descarga: cuando se utiliza TSO y UFO, esta función generalmente está activada. Tanto TSO como UFO son compatibles con hardware de tarjeta de red, mientras que GSO se implementa principalmente mediante software en la capa del controlador en Linux. Para el reenvío de equipos, personalmente recomiendo no habilitar GSO. Se ha probado antes que encender GSO aumentará el retraso de reenvío.

  7. rx-vlan-offload: se habilita cuando se implementa en un entorno de red vlan.

  8. tx-vlan-offload: Igual que arriba

  9. recepción-hash: si utiliza la función de software RPS / RFS, habilítela.

Puede utilizar ethtool -K para habilitar funciones específicas.

[Beneficios del artículo] Materiales de aprendizaje para arquitectos de servidores C / C ++ Linux más el grupo 812855908 (datos que incluyen C / C ++, Linux, tecnología golang, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, medios de transmisión, CDN, P2P, K8S, Docker, TCP / IP, corrutina, DPDK, ffmpeg, etc.)
Inserte la descripción de la imagen aquí

Configuración del búfer de anillo de la tarjeta de red

El búfer de anillo predeterminado del controlador de la tarjeta de red generalmente no es grande. Tome la máquina virtual del autor como ejemplo.
Inserte la descripción de la imagen aquí

El tamaño de recepción y envío es de 256 bytes, que obviamente es pequeño. Al encontrar tráfico en ráfagas, puede causar que el búfer de anillo de recepción de la tarjeta de red esté lleno y se pierdan paquetes.
En servidores o dispositivos de reenvío de alto rendimiento y tráfico, generalmente está configurado para ser 2048 o superior. Y, si el autor recuerda correctamente, en el controlador de la tarjeta de red de Intel, se recomienda establecer el tamaño del búfer de envío al doble del tamaño del búfer de recepción; no se especifica el motivo.
Use ethtool -G para establecer el tamaño del búfer de anillo de la tarjeta de red. El autor generalmente lo establece en 2048 y 4096. Si es un dispositivo de reenvío, puede configurarse más grande.

Interrumpir configuración

La mayoría de las tarjetas de red actuales son tarjetas de red de colas múltiples y cada cola tiene una interrupción independiente. Para mejorar las capacidades de procesamiento concurrente, necesitamos distribuir diferentes interrupciones a diferentes núcleos de CPU.
Compruebe el estado de las interrupciones duras mediante cat / proc / interrupts.
Inserte la descripción de la imagen aquí

En la imagen de arriba, las interrupciones de la tarjeta de red de la máquina virtual del autor están distribuidas de manera relativamente uniforme en diferentes núcleos de CPU.
Ver la afinidad de la CPU de la interrupción correspondiente
Inserte la descripción de la imagen aquí

Las interrupciones correspondientes a diferentes colas de recepción / envío se asignan a CPU0 ~ 7.

Por defecto, el smp_affinity correspondiente a la interrupción generalmente se establece en ff, es decir, la interrupción se puede distribuir a todos los núcleos. En este momento, parece que todas las interrupciones de cola se pueden distribuir a cualquier núcleo, lo que en teoría parece ser mejor que el núcleo especificado anteriormente. Sin embargo, el efecto real a menudo no es el caso. Esto depende de la implementación del hardware y del sistema operativo Según la experiencia del autor, no he encontrado una situación en la que la carga de interrupción forzada esté muy equilibrada después de que smp_affinity se establezca en ff. Generalmente, se distribuyen a unos pocos núcleos designados, mientras que otros núcleos reciben solo unas pocas interrupciones.

Por lo tanto, en general, asignamos diferentes colas de recepción de la tarjeta de red a diferentes CPU en orden. En ese momento, surgió un problema. ¿Cómo decide la tarjeta de red en qué cola colocar el mensaje?

Configuración de RSS de la tarjeta de red

La tarjeta de red también usa la operación hash para determinar en qué cola de recepción colocar el mensaje. Aunque no podemos cambiar el algoritmo hash, podemos establecer la clave hash, que se calcula por qué campo del mensaje, que afecta el resultado final.
Utilice ethtool --show-tuple para ver el protocolo especificado.
Las diferentes tarjetas de red tienen diferentes capacidades de RSS, y los protocolos admitidos y los campos que se pueden configurar también son diferentes. Pero lo extraño es que la clave predeterminada del protocolo UDP es diferente de TCP, solo IP de origen + IP de destino. De esta forma, al hacer pruebas de rendimiento UDP, debemos prestar especial atención a usar el mismo dispositivo que el cliente, y los paquetes UDP generados solo se distribuirán a una cola, lo que resultará en una sola interrupción del procesamiento de la CPU en el servidor, lo que afectar los resultados de la prueba.
Por lo tanto, generalmente tenemos que cambiar la clave de RSS de UDP a través de ethtool --config-tuple para que sea similar a TCP.

01
-

Estrategia óptima para recibir dirección

Ahora comience a ingresar a la estrategia de optimización del campo del software.

Mecanismo NAPI

Los controladores de dispositivos de red modernos de Linux generalmente admiten el mecanismo NAPI, que integra interrupciones y sondeos. Una interrupción puede sondear el dispositivo varias veces. Esto puede tener las ventajas de interrumpir y sondear. —— Por supuesto, para equipos de reenvío puro, el sondeo se puede utilizar directamente. Entonces, ¿cuántas encuestas tienes que sondear para detectar una interrupción? Se puede configurar a través de / proc / sys / net / core / netdev_budget, el valor predeterminado es 300. Esto lo comparten todos los dispositivos. Al recibir el procesamiento de interrupciones suaves, es posible que varios dispositivos de red hayan disparado la interrupción, que se agrega a la lista NAPI. Entonces, el presupuesto compartido por estos dispositivos es 300, y cada dispositivo tiene una llamada NAPI, y el número máximo de sondeos generalmente se escribe directamente en el controlador, generalmente 64. Aquí, ya sea 64 o 300, se refiere al número máximo de sondeos.Si no hay mensajes preparados en el hardware, aunque no se alcance el número de presupuestos, saldrá. Si el dispositivo tiene mensajes todo el tiempo, la interrupción suave que recibe siempre recopilará los mensajes hasta el número de presupuesto.
Cuando la interrupción suave ocupa una gran cantidad de CPU, hará que la aplicación en esta CPU no se programe. Por lo tanto, el valor presupuestario debe seleccionarse de acuerdo con el negocio. Debido a que el tiempo de procesamiento de diferentes paquetes de protocolo es diferente y no es intuitivo controlar el uso de la CPU para recibir interrupciones suaves estableciendo el número de presupuesto. Por lo tanto, en la nueva versión del kernel, se introduce un nuevo parámetro netdev_budget_usecs para controlar el tiempo máximo de CPU para recibir interrupciones suaves.

RPS y RFS
en la era en la que no hay una tarjeta de red de múltiples colas, la tarjeta de red solo puede generar una interrupción y enviarla a una CPU. En este momento, ¿cómo utilizar el núcleo múltiple para mejorar la capacidad de procesamiento en paralelo? RPS nació para solucionar este problema. RPS es similar al RSS de la tarjeta de red, excepto que la CPU calcula un valor hash de acuerdo con el protocolo de mensajes y luego usa este valor hash para seleccionar una CPU y almacenar el mensaje en la cola de recepción de la CPU. Y envíe una interrupción IPI a la CPU de destino para notificarle que procese. En este caso, incluso si solo una CPU recibe la interrupción, RPS puede distribuir el mensaje a varias CPU nuevamente.
Al escribir en el archivo / sys / class / net / ethx / queues / rx-0 / rps_cpus, puede establecer a qué CPU puede distribuir la cola de recepción de la tarjeta de red.
RFS es similar a RPS, excepto por la letra del medio, la primera es Flow y la segunda es Packet. Esto también explica su principio de realización. RPS se distribuye completamente de acuerdo con las características del mensaje actual, mientras que RFS tiene en cuenta el flujo; el flujo aquí no es un flujo simple, sino que considera el comportamiento de la "aplicación", es decir, qué núcleo de CPU procesó este flujo la última vez. La CPU es la CPU de destino.
Ahora que hay tarjetas de red de múltiples colas y puede configurar una ntupla personalizada para afectar el algoritmo hash, RPS no tiene mucha utilidad.
Entonces, ¿RFS también entra en el polvo de la historia? Personalmente creo que es negativo. Imagine, en el siguiente escenario, que un servicio S se implementa en un servidor de 8 núcleos, y sus 6 subprocesos de trabajo ocupan la CPU 0-5, y la CPU 6-7 restante es responsable de procesar otros servicios. Debido a que hay 8 núcleos de CPU, la cola de la tarjeta de red generalmente se establece en 8. Suponiendo que estas 8 colas corresponden a CPU0 ~ 7, entonces surge el problema. El mensaje de servicio del servicio S es recibido por la tarjeta de red, y después del cálculo de RSS, se coloca en la cola 6. La interrupción correspondiente también se envía a CPU6, pero el subproceso del servicio S no se está ejecutando en CPU6, y los datos el mensaje se agrega a 6. El conector en un hilo de trabajo recibe el búfer. Por un lado, puede haber una relación de competencia con la operación de lectura del hilo de trabajo en ejecución. Por otro lado, cuando el hilo de trabajo correspondiente lee el mensaje, los datos del mensaje deben volver a leerse en la memoria caché del hilo de trabajo correspondiente. UPC. RSS puede resolver este problema.Cuando un hilo de trabajo procesa un mensaje de socket, el kernel registrará que el mensaje es procesado por una determinada CPU y guardará esta relación de mapeo en una tabla de flujo. De esta forma, incluso si la CPU6 recibe una interrupción, encuentra la entrada correspondiente en la tabla de flujo de acuerdo con las características del protocolo del mensaje, y el mensaje debe ser procesado por la CPU3. En este momento, el mensaje se almacenará en la cola de trabajos pendientes de CPU3, evitando los problemas anteriores.

XPS
RPS y RFS se utilizan para establecer la relación entre la cola de recepción y la CPU de procesamiento, y XPS se puede utilizar no solo para establecer la relación entre la cola de envío y la CPU de procesamiento, sino también para establecer la relación entre la cola de recepción y la cola de envío. El primero es cuando se completa el envío, su trabajo es completado por la CPU designada, y el segundo es seleccionar la cola de envío a través de la cola de recepción.

netfilter y nf_conntrack
netfilter es la implementación de la herramienta iptables en el kernel, y su rendimiento es promedio, especialmente cuando hay una gran cantidad de reglas o cuando se utilizan condiciones de coincidencia extendidas. Use esto de acuerdo a su situación. Y nf_conntrack es la función de seguimiento de conexiones requerida por netfilter como firewall con estado. En la primera versión de Linux, la tabla de sesión usaba un gran bloqueo global, lo que perjudicaba el rendimiento. En un entorno de producción, generalmente no se recomienda cargar este módulo, por lo que no se puede usar firewall con estado, NAT, synproxy, etc.

Early_demux switch
Los estudiantes que están familiarizados con el kernel de Linux saben que después de que Linux recibe un mensaje, buscará en la tabla de enrutamiento para determinar si el mensaje se envía a la máquina o se reenvía. Si se determina que se envía a la máquina local, es necesario encontrar a qué socket se envía de acuerdo con el protocolo de 4 capas. Aquí están involucradas dos búsquedas, y para TCP establecido y algunos UDP, la "conexión" se ha completado y la ruta puede considerarse "inmutable", por lo que la información de enrutamiento de la "conexión" se puede almacenar en caché. Después de abrir / proc / sys / net / ipv4 / tcp_early_demux o udp_early_demux, las dos búsquedas anteriores pueden fusionarse en una. Una vez que el kernel recibe el mensaje, si el protocolo de capa 4 habilita early_demux, buscará el socket con anticipación y, si lo encuentra, usará directamente el resultado de enrutamiento almacenado en caché en el socket. Para el dispositivo de reenvío, este conmutador no necesita estar encendido, porque el dispositivo de reenvío es principalmente reenvío y no tiene un programa de servicio nativo.

Habilitación de busy_poll
busy_poll se denominó en primer lugar Sockets de baja latencia para mejorar el problema de retraso de los paquetes de procesamiento del kernel. La idea principal es que cuando se realizan llamadas al sistema de socket, como operaciones de lectura, la capa de socket llama directamente al método de la capa del controlador para sondear los mensajes leídos dentro de un tiempo específico, lo que puede aumentar la capacidad de procesamiento de PPS varias veces.
Hay dos configuraciones a nivel de sistema para busy_poll: la primera es / proc / sys / net / core / busy_poll, que establece el tiempo de espera para la ejecución de la encuesta ocupada durante las llamadas al sistema de selección y encuesta, en nosotros. El segundo es / proc / sys / net / core / busy_read, que establece el tiempo de espera de busy_poll durante las operaciones de lectura, y la unidad también es us.
A partir del resultado de la prueba, el efecto de busy_poll es obvio, pero también tiene limitaciones. Solo cuando la cola de recepción de cada tarjeta de red tiene y solo una aplicación puede leerla, se puede mejorar el rendimiento. Si varias aplicaciones están realizando encuestas de ocupado en una cola de recepción al mismo tiempo, es necesario introducir un planificador para tomar una decisión, lo que aumentará el consumo en vano.

Tamaño del búfer de recepción El búfer de recepción del
socket Linux tiene dos configuraciones, una es el tamaño predeterminado y la otra es el tamaño máximo. Puede obtenerse usando sysctl -a | grep rmem_default / max, o leyendo / proc / sys / net / core / rmem_default / max. En términos generales, los valores predeterminados de estas dos configuraciones de Linux son algo pequeños para el programa de servicio (unos pocos cientos de k). Luego, podemos aumentar el tamaño predeterminado y el valor máximo del búfer de recepción a través de sysctl o escribiendo directamente en el archivo proc anterior, para evitar que las aplicaciones de tráfico de ráfagas lleguen demasiado tarde para lidiar con la situación de que el búfer de recepción está lleno y la pérdida de paquetes .

Parámetro de configuración TCP
/ proc / sys / net / ipv4 / tcp_abort_overflow: controla el comportamiento cuando se establece una conexión TCP pero la cola de trabajos pendientes está llena. El valor predeterminado es generalmente 0, y el comportamiento es retransmitir syn + ack, por lo que el par retransmitirá ack. Cuando el valor es 1, RST se enviará directamente. El primero es un tratamiento relativamente suave, pero no es fácil exponer el problema de la acumulación total, puede establecer un valor adecuado a su negocio.

/ proc / sys / net / ipv4 / tcp_allowed_congestion_control: muestra el algoritmo de control de flujo TCP admitido por el sistema actual

/ proc / sys / net / ipv4 / tcp_congestion_control: Configure el algoritmo de control de flujo TCP utilizado por el sistema actual, que debe ser el algoritmo que se muestra arriba.

/ proc / sys / net / ipv4 / tcp_app_win: se utiliza para ajustar el tamaño de la caché en la capa de aplicación y la asignación de la ventana TCP.

/ proc / sys / net / ipv4 / tcp_dsack: si se habilita Duplicar SACK.

/ proc / sys / net / ipv4 / tcp_fast_open: si habilitar la extensión TCP Fast Open. Esta extensión puede mejorar el tiempo de respuesta de las comunicaciones de larga distancia.

/ proc / sys / net / ipv4 / tcp_fin_timeout: Se usa para controlar el tiempo de espera del paquete FIN del extremo opuesto después de que el extremo local se apaga activamente. Se usa para evitar ataques de DOS, en segundos.

/ proc / sys / net / ipv4 / tcp_init_cwnd: Tamaño de la ventana de congestión inicial. Puede establecer un valor mayor según sea necesario para mejorar la eficiencia de la transmisión.

/ proc / sys / net / ipv4 / tcp_keepalive_intvl: el intervalo para enviar paquetes keepalive.

/ proc / sys / net / ipv4 / tcp_keepalive_probes: No se recibe ninguna respuesta de mensaje keepalive, el número máximo de keepalives enviado.

/ proc / sys / net / ipv4 / tcp_keepalive_time: el tiempo de inactividad para que la conexión TCP envíe keepalive.

/ proc / sys / net / ipv4 / tcp_max_syn_backlog: la longitud de la cola del protocolo de enlace de tres vías TCP que no recibió el reconocimiento del cliente. Para el servidor, debe ajustarse a un valor mayor.

/ proc / sys / net / ipv4 / tcp_max_tw_buckets: el número de sockets cuyo TCP está en el estado TIME_WAIT, usado para defenderse de ataques simples de DOS. Una vez superado este número, el enchufe se cerrará directamente.

/ proc / sys / net / ipv4 / tcp_sack: establezca si desea habilitar SACK, está habilitado de forma predeterminada.

/ proc / sys / net / ipv4 / tcp_syncookies: Se utiliza para evitar ataques de inundación de sincronización. Cuando la cola de sincronización está llena, se utilizará syncookie para verificar el cliente.

/ proc / sys / net / ipv4 / tcp_window_scaling: establezca si desea habilitar la función de extensión de escala de ventana TCP. Puede notificar a la otra parte de una ventana de recepción más grande para mejorar la eficiencia de transmisión. Habilitado por defecto.

Opción de socket de uso común
SO_KEEPALIVE: si habilitar KEEPALIVE.

SO_LINGER: Establece el período de tiempo de espera para el "cierre elegante" del socket (mi nombre personal). Cuando la opción LINGER está habilitada, cuando se llama a cerrar o apagar, si hay datos en el búfer de envío del socket, no regresará inmediatamente, sino que esperará a que se envíe el mensaje o hasta el tiempo de espera de LINGER. Hay una situación especial aquí, LINGER está habilitado, pero el tiempo de LINGER es 0, ¿qué pasará? Enviará RST directamente al extremo opuesto.

SO_RCVBUFF: establece el tamaño del búfer de recepción del socket.

SO_RCVTIMEO: Establece el tiempo de espera para recibir datos. Para el programa de servicio, generalmente es sin bloqueo, es decir, establecido en 0.

SO_REUSEADDR: si verificar que la dirección vinculada y el puerto están en conflicto. Por ejemplo, si un puerto se ha vinculado con ANY_ADDR, no se puede usar ninguna dirección local más adelante para vincular el mismo puerto. Para el programa de servicio, se recomienda abrirlo para evitar la falla de la dirección de enlace cuando se reinicia el programa.

SO_REUSEPORT: Permite vincular exactamente la misma dirección y puerto. Más importante aún, cuando el mensaje recibido por el kernel puede coincidir con varios sockets con la misma dirección y puerto, el kernel cambiará automáticamente entre estos sockets. Logra el equilibrio de carga.

Otros parámetros del sistema
Número máximo de descriptores de archivo: para los programas de servicio TCP, cada conexión ocupa un descriptor de archivo, por lo que el número máximo predeterminado de descriptores de archivo está lejos de ser suficiente. Necesitamos aumentar el límite máximo de descriptores del sistema y el proceso al mismo tiempo. El primero puede usar / proc / sys / fs / file-max,
puede usar / proc / sys / fs / file-max o sysctl -n fs.file-max = xxxxxx settings. Este último se puede configurar usando ulimit -n o setrlimit.
Vincular CPU: cada subproceso del programa de servicio está vinculado a la CPU especificada. Puede usar los comandos taskset o cgroup para vincular un subproceso de servicio específico a una CPU específica o un conjunto de CPU. También puede llamar a pthread_setaffinity_np para lograr. Al vincular el subproceso especificado a la CPU, por un lado, se puede garantizar la popularidad de la caché (alto impacto) y, por otro lado, se puede lograr la distribución de la carga de la CPU en línea con el negocio.

03
-

núcleo de bypass

El método anterior era principalmente optimizar el rendimiento de la red de Linux ajustando los parámetros del kernel, pero para el programa de servicio de la capa de aplicación, todavía hay varios problemas que no se pueden evitar, como la copia de datos dentro y fuera del kernel. Así nació el esquema del kernel de bypass, como dpdk, netmap, pfring, etc., entre los cuales dpdk es el más utilizado. En comparación con el kernel, tiene tres ventajas: 1. Evita la copia de datos dentro y fuera del kernel, 2. Usa páginas grandes para mejorar la tasa de aciertos de TLB, 3. Usa la encuesta de forma predeterminada para mejorar el rendimiento de la red.

Sin embargo, estas herramientas para enviar y recibir paquetes aún no pueden contener una pila de protocolos completa y herramientas de red como el kernel. ——Por supuesto, DPDK ya tiene muchas bibliotecas y herramientas.

Para el equipo de reenvío de red, básicamente solo se procesan los paquetes de la segunda y tercera capa, lo que no requiere grandes pilas de protocolos. Para el programa de servicio, se requiere una pila de protocolos más completa. Actualmente existen soluciones DPDK + mtcp, DPDK + fstack y DPDK + Nginx.

Dado que este artículo se centra en la mejora del rendimiento de la red Linux, el esquema de derivación es solo una introducción.

¡Preste atención a la cuenta oficial y comparta más contenido de tecnología de Internet que le interese!
Inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/qq_40989769/article/details/111281977
Recomendado
Clasificación