SO_REUSEPORT_LB es una opción de socket pensada para distribuir conexiones entrantes entre varios procesos que escuchan en la misma dirección y puerto. En plataformas compatibles, el sistema operativo realiza el reparto en el kernel, de modo que cada worker puede mantener su propio socket de escucha. Esto puede reducir la contención, aprovechar mejor varios núcleos y simplificar ciertos servidores de alto rendimiento.
Qué hace SO_REUSEPORT_LB
La constante aparece en el módulo socket cuando la versión de Python y el sistema operativo ofrecen soporte. Está relacionada con SO_REUSEPORT, pero no conviene asumir que ambas tienen semántica idéntica en todas las plataformas. El objetivo práctico es permitir varios listeners activos y distribuir nuevas conexiones entre ellos.
import socket
if hasattr(socket, "SO_REUSEPORT_LB"):
print("compatible")
else:
print("no disponible")La detección es obligatoria. Una versión reciente de Python no garantiza que el kernel implemente la opción.
Configuración básica
El orden habitual es crear el socket, activar la opción antes de bind(), asociar la dirección y comenzar a escuchar.
import socket
HOST = "127.0.0.1"
PORT = 9000
servidor = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT_LB, 1)
servidor.bind((HOST, PORT))
servidor.listen()
print(f"Escuchando en {HOST}:{PORT}")Definir la opción después de bind() puede no tener efecto o provocar un error. Por eso debe configurarse antes de vincular el puerto.
Cuándo utilizarla
Es útil en servidores multiproceso donde cada proceso ejecuta su propio bucle de aceptación. Algunos casos son servicios HTTP personalizados, gateways internos, colectores de métricas, servidores de baja latencia y aplicaciones que desean usar varios núcleos sin compartir un único descriptor de escucha.
También puede participar en reinicios graduales, pero no sustituye health checks, drenaje de conexiones, observabilidad ni una estrategia de despliegue bien definida.
Diferencia con SO_REUSEADDR
SO_REUSEADDR suele facilitar la reutilización de una dirección después de reiniciar un servicio, especialmente cuando quedan conexiones en estados como TIME_WAIT. SO_REUSEPORT_LB se centra en varios listeners simultáneos y en la distribución de carga entre ellos.
Las reglas exactas cambian según el sistema. Prueba siempre la combinación real de Python, kernel y configuración de red.
Varios workers en el mismo puerto
Un patrón común inicia varios procesos con la misma función de servidor. Cada worker crea su socket, activa la opción y hace bind sobre el mismo endpoint.
from multiprocessing import Process
import socket
HOST = "127.0.0.1"
PORT = 9000
def worker(numero):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as servidor:
servidor.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT_LB, 1)
servidor.bind((HOST, PORT))
servidor.listen()
while True:
conexion, direccion = servidor.accept()
with conexion:
conexion.sendall(f"worker {numero}\n".encode())
if __name__ == "__main__":
procesos = [Process(target=worker, args=(i,)) for i in range(4)]
for proceso in procesos:
proceso.start()
for proceso in procesos:
proceso.join()El ejemplo es didáctico. En producción se necesitan timeouts, señales, límites de recursos, registros estructurados, recuperación de errores y cierre ordenado.
El balanceo no es perfectamente uniforme
El kernel utiliza reglas propias. Cuatro workers no reciben necesariamente exactamente el veinticinco por ciento de las conexiones. La duración de las sesiones, el patrón de tráfico, el hashing y otros detalles pueden producir diferencias.
Mide conexiones aceptadas por proceso, latencia, bytes procesados, errores, CPU y conexiones activas. Lo importante es un equilibrio operativo suficiente.
Cómo probarlo
Una prueba útil crea muchas conexiones independientes. Una única conexión persistente no demuestra distribución porque permanece asociada al worker que la aceptó.
import socket
from collections import Counter
resultados = Counter()
for _ in range(200):
with socket.create_connection(("127.0.0.1", 9000), timeout=2) as cliente:
respuesta = cliente.recv(100).decode().strip()
resultados[respuesta] += 1
print(resultados)Repite la prueba y añade tráfico con keep-alive si el servicio real usa conexiones persistentes.
Portabilidad y alternativas
La portabilidad es la principal limitación. Referenciar directamente la constante puede fallar en sistemas sin soporte.
opcion = getattr(socket, "SO_REUSEPORT_LB", None)
if opcion is None:
raise RuntimeError("SO_REUSEPORT_LB no está soportado")Como alternativas puedes usar un listener único compartido, paso de descriptores, un servidor de aplicaciones que administre workers, un proxy inverso o un balanceador externo.
Seguridad
Todos los procesos que participan en el grupo de listeners deben ejecutarse con identidades y permisos controlados. No permitas que procesos no confiables se unan al mismo puerto. Vincula una interfaz específica cuando el servicio no deba ser público.
La opción no ofrece cifrado, autenticación, validación ni aislamiento. TLS, límites de entrada, control de acceso y protección de recursos siguen siendo necesarios.
Cierre ordenado
Durante un despliegue, un worker debe dejar de aceptar conexiones nuevas, terminar las activas y cerrar su socket. Los demás listeners pueden continuar atendiendo. Las conexiones persistentes permanecen ligadas al proceso original hasta finalizar.
Gestiona señales de terminación, plazos de cierre y métricas de conexiones activas. Evita reiniciar todos los workers al mismo tiempo.
Integración con selectors y asyncio
El socket ya configurado puede registrarse en selectors o integrarse con infraestructura asíncrona. En asyncio, revisa si la API permite pasar un socket preconfigurado para conservar el control del bind.
Como lectura relacionada, consulta asyncio en Python, HTTPSServer en Python, os.timerfd_create en Python y concurrent.interpreters en Python.
Buenas prácticas
Detecta soporte, activa la opción antes de bind, registra qué worker acepta cada conexión, usa timeouts, maneja señales y prueba sobre el sistema objetivo. Documenta la plataforma mínima y conserva una ruta alternativa.
Supervisa límites de archivos abiertos, backlog, errores de accept, memoria y CPU. Más workers no siempre significan más rendimiento.
Errores comunes
Los fallos habituales son activar la opción después de bind, confundirla con SO_REUSEADDR, asumir soporte universal, iniciar demasiados procesos, probar una sola conexión e ignorar el cierre ordenado. También es un error pensar que reemplaza la observabilidad o la planificación de capacidad.
Referencias
Consulta la documentación oficial de socket y la documentación de setsockopt del sistema. Comprueba siempre la versión exacta usada en producción.
Conclusión
SO_REUSEPORT_LB puede ofrecer un modelo claro para listeners multiproceso y repartir nuevas conexiones entre workers independientes. Su utilidad depende de soporte real, medición, cierre ordenado y una alternativa probada. Debe tratarse como una herramienta específica de arquitectura de red, no como una solución automática para todos los problemas de escalabilidad.







