Pool de proxies dentro de la aplicación: selección de IP, health-check, cuarentena y sticky-sessions
Contenido del artículo
- Por qué una lista de direcciones en un archivo de texto se desmorona bajo carga real
- Fundamentos: pool, alquiler de dirección para una tarea y qué considerar una sesión
- Inmersión profunda: ciclo de vida de una dirección en el pool
- Estrategias de selección de dirección: desde round-robin hasta hash por clave
- Health-check: verificaciones de salud pasivas y activas
- Cuarentena y retorno al servicio: espera, límites y protección del pool
- Almacenamiento del estado: memoria del proceso vs. almacenamiento compartido
- Observabilidad: métricas por cada dirección y cómo distinguir una ip mala de un sitio malo
- Práctica: esqueleto de pool en python y node.js
- Errores típicos: lo que no se debe hacer
- Herramientas y recursos para la implementación
- Casos prácticos y resultados de aplicación
- Preguntas frecuentes
- Conclusión y siguientes pasos
Imagina la escena. Es viernes por la noche, tu parser o sistema de gestión de cuentas finalmente se ha puesto en marcha. Los primeros minutos todo vuela. Pero luego empieza lo extraño: algunas solicitudes fallan, aparece un captcha, una cuenta de repente pide verificación de identidad, y los logs se convierten en un revoltijo de errores de conexión. ¿Te suena? En nueve de cada diez casos, la raíz del problema no está en el sitio objetivo ni en tu lógica de negocio. Está en cómo la aplicación gestiona sus direcciones IP.
La mayoría de los proyectos comienzan con algo simple: un archivo de texto con una lista de direcciones, lectura línea por línea, selección aleatoria de una línea. Y funciona. Hasta el momento en que la carga se vuelve real. Entonces la lista ingenua se desmorona y obtienes fallos intermitentes que son imposibles de reproducir con una sola solicitud en la consola.
Este artículo es una guía completa sobre el diseño de un pool de proxies dentro de la aplicación. Analizaremos cómo seleccionar una dirección para una tarea específica, cómo verificar la salud de las direcciones, cómo poner las IP problemáticas en cuarentena y devolverlas, y cómo mantener una sticky-session para una misma cuenta. Es un tema de ingeniería, pero lo abordaremos en un lenguaje accesible, con código, listas de verificación y análisis de errores reales.
Establezcamos los límites de inmediato. No hablamos de tiempos de espera ni políticas de reintentos para una sola solicitud: ese es un tema aparte y extenso. Tampoco abordamos cómo se cambia físicamente la dirección en un módem o equipo: eso es nivel de infraestructura, no de aplicación. Nuestro enfoque es la lógica del pool en tu código.
Por qué una lista de direcciones en un archivo de texto se desmorona bajo carga real
Seamos honestos sobre una implementación inicial típica. Hay un archivo con cien líneas de direcciones. La aplicación lo lee, coloca las líneas en un array y para cada solicitud toma un elemento aleatorio. Simple, claro, funciona en una demostración. ¿Por qué se rompe?
Primer problema: no hay memoria del estado de la dirección
La selección aleatoria de un array no sabe nada de lo que ocurrió con la dirección un segundo antes. Si la dirección número cuarenta y siete acaba de devolver cinco errores seguidos y claramente no está sana, el algoritmo aleatorio la seleccionará con la misma probabilidad nuevamente. Golpearás una y otra vez una dirección muerta, perdiendo tiempo, reintentos y, lo que es más importante, la confianza del sitio objetivo en tus acciones.
Segundo problema: no hay vinculación de la tarea con la dirección
Cuando trabajas con cuentas, es crítico que una misma cuenta salga a la red desde la misma dirección. Los sistemas antifraude de las plataformas notan cuando la sesión de un usuario salta en pocos minutos entre una docena de subredes de diferentes zonas geográficas. Eso no es natural. Una persona real no se comporta así. La selección aleatoria de un archivo garantiza este caos.
Tercer problema: condiciones de carrera con múltiples workers
En cuanto ejecutas varios procesos o hilos en paralelo, un simple array en memoria se convierte en una fuente de condiciones de carrera. Dos workers leen el mismo índice, ambos toman la misma dirección, ambos la cargan el doble. No hay coordinación. Los contadores, si los hay, se pierden entre procesos.
Cuarto problema: falta de observabilidad
Un archivo de texto no habla. No te dirá qué dirección está dando un ochenta por ciento de captchas, cuál se ha vuelto lenta o cuál lleva un día muerta. Trabajas a ciegas y te enteras de los problemas solo por síntomas indirectos en las métricas de negocio, cuando ya es tarde.
La conclusión es simple. Una lista de direcciones son datos. Gestionar direcciones bajo carga es un sistema. La diferencia entre ambos es el tema de este artículo. A continuación, construiremos este sistema ladrillo a ladrillo.
Fundamentos: pool, alquiler de dirección para una tarea y qué considerar una sesión
Empecemos por lo básico. Si ya eres un ingeniero experimentado, igual te recomiendo leerlo: aquí fijamos la terminología que usaremos hasta el final del artículo.
¿Qué es un pool de direcciones?
Un pool no es solo una lista de direcciones, sino un objeto con comportamiento. Almacena un conjunto de direcciones junto con su estado y proporciona dos operaciones principales: entregar una dirección para una tarea y devolverla. Una analogía clásica es una biblioteca. Tienes un estante con libros, pero entre tú y el estante hay un bibliotecario. Él sabe qué libros están prestados, cuáles están dañados y enviados a reparación, y cuáles están disponibles. Tú no buscas en el estante tú mismo: le pides al bibliotecario y él te entrega un libro adecuado.
Alquiler de una dirección para una tarea
El alquiler es la asignación temporal de una dirección a una tarea específica. El pool entrega la dirección, la marca como ocupada o registra que tiene nueva carga, y al finalizar la tarea se devuelve la dirección. Por eso en la interfaz del pool aparecen dos métodos que serán nuestros caballos de batalla: acquire (obtener dirección) y release (devolverla con un informe del resultado).
El informe al devolver es un detalle clave. Cuando la tarea devuelve la dirección, le comunica al pool cómo terminó: éxito, error de conexión, captcha, bloqueo. Esta información alimenta toda la lógica posterior de salud y cuarentena.
Vinculación sticky vs. selección aleatoria
Aquí hay una frontera muy importante. La selección aleatoria es cuando en cada solicitud el pool da una dirección arbitraria. Este modo es bueno para tareas donde no existe el concepto de sesión continua: recolección masiva de datos públicos desde páginas independientes, donde cada solicitud es autónoma.
La vinculación sticky es cuando una clave determinada, por ejemplo el identificador de una cuenta, recibe siempre la misma dirección. Esto es crítico cuando la continuidad de la identidad a lo largo del tiempo es importante. Trabajar con una cuenta es un ejemplo típico. La cuenta debe verse ante la plataforma como un usuario consistente, no como un enjambre de entidades saltando por la red.
¿Qué considerar una sesión a nivel de tarea de negocio?
La palabra sesión está sobrecargada. A nivel de HTTP es una cosa, a nivel de TCP es otra. Pero a nosotros nos interesa la sesión de negocio. Es una secuencia de acciones lógicamente relacionadas que, desde el punto de vista de la plataforma, deberían provenir del mismo usuario desde la misma dirección.
Ejemplos de sesiones de negocio:
- Inicio de sesión en una cuenta, serie de acciones dentro de ella, cierre de sesión: todo desde la misma dirección.
- Un escenario de varios pasos: abrir una ficha, agregar al carrito, finalizar compra: todos los pasos están relacionados.
- Trabajar durante la jornada laboral bajo un mismo perfil, donde cambiar de dirección parecería un traslado sospechoso.
Definir los límites de una sesión es una decisión de diseño tuya, no un hecho técnico. Tú decides si la sesión dura quince minutos, una hora o hasta un cierre de sesión explícito. De esta definición depende cuánto tiempo el pool mantiene la vinculación de la clave con la dirección. Recordemos esto: volveremos al tema de las sticky-sessions en varias ocasiones.
Inmersión profunda: ciclo de vida de una dirección en el pool
Antes de analizar estrategias individuales, es útil ver el panorama general. Cada dirección en un pool bien diseñado tiene un ciclo de vida, un conjunto de estados entre los que transita.
Estados de la dirección
- Healthy (sana) – la dirección está disponible para ser entregada, las métricas son normales.
- Degraded (degradada) – la dirección aún se entrega, pero con un peso reducido porque sus indicadores han empeorado.
- Quarantined (en cuarentena) – la dirección se excluye temporalmente de la entrega, está en período de espera.
- Probing (en prueba) – la dirección está siendo sometida a una verificación activa antes de regresar al servicio.
- Dead (muerta) – la dirección se considera no operativa por un tiempo prolongado o para siempre.
Las transiciones entre estados son precisamente el sistema que estamos construyendo. Una dirección sana, al acumular errores, se degrada. Una degradada, al alcanzar un umbral, entra en cuarentena. Desde la cuarentena, tras la espera, pasa a prueba. Si pasa la prueba, vuelve a estar sana. Si no la pasa, regresa a cuarentena con una espera mayor. Varios fallos seguidos y la dirección se declara muerta.
Por qué es necesaria una capa de abstracción
Información clave: la aplicación no debe conocer los estados de la dirección. El código de aplicación simplemente pide una dirección y la devuelve con un resultado. Toda la complejidad del ciclo de vida está oculta dentro del pool. Este es el principio de encapsulación, y se amortiza cien veces. Cuando quieras cambiar la estrategia de cuarentena, modificas un solo módulo, no decenas de lugares en la lógica de negocio.
Modelo mental de la carga
Es bueno tener en mente tres ejes a lo largo de los cuales vive una dirección:
- Actualidad – cuánto tiempo ha pasado desde la última vez que verificamos su salud.
- Carga – cuántas tareas están actualmente asignadas a ella.
- Reputación – historial acumulado de éxitos y fracasos.
Cualquier estrategia de selección, en esencia, combina estos tres ejes en una evaluación única. A continuación, analizaremos estrategias concretas.
Estrategias de selección de dirección: desde round-robin hasta hash por clave
El corazón del pool es el algoritmo que decide qué dirección entregar en cada solicitud acquire. No hay una única respuesta correcta. La elección de la estrategia viene dictada por la tarea. Analicemos cuatro enfoques básicos y expliquemos cuándo es apropiado cada uno.
Round-robin: por turnos
Round-robin es el algoritmo justo más simple. Las direcciones se ordenan en un círculo y un puntero avanza de a una por cada solicitud. Cuando se completa el círculo, se empieza de nuevo. Ventaja: distribución perfectamente uniforme de la carga si las direcciones son iguales. Desventaja: el algoritmo es ciego a las diferencias entre direcciones. Una rápida y una lenta reciben la misma proporción de tráfico.
Cuándo usarlo: pool homogéneo, tareas sin sesiones, cuando todas las direcciones son aproximadamente iguales en calidad y capacidad.
Selección ponderada
La selección ponderada es una evolución del round-robin donde a cada dirección se le asigna un peso. Una dirección con mayor peso recibe más tráfico. El peso puede asignarse de forma estática, según la capacidad conocida, o dinámica, recalculándolo en función de métricas en vivo: tasa de éxito y latencia.
El peso dinámico es una herramienta poderosa. ¿Una dirección empezó a devolver más captchas? Reducimos su peso, recibe menos tráfico pero no desaparece por completo. ¿Los indicadores se recuperaron? El peso vuelve a subir. Es una autorregulación suave, más amable que una salida abrupta a cuarentena.
Una fórmula simple para el peso: peso = tasa de éxito dividida por la latencia normalizada. Cuanto mayor el éxito y menor la latencia, mayor el peso. Recálculo en una ventana deslizante, por ejemplo, sobre las últimas cincuenta solicitudes.
Least-connections: la menos cargada
Least-connections entrega la dirección que tenga menos tareas activas en ese momento. Esto funciona de maravilla cuando las tareas varían mucho en duración. Round-robin en esa situación podría sobrecargar una dirección con tareas largas mientras otra permanece ociosa. Least-connections equilibra la carga real, no el número de solicitudes entregadas.
Para implementarlo se necesita un contador de alquileres activos por dirección. Aumenta con acquire y disminuye con release. Se selecciona la dirección con el contador más bajo. Atención: este contador es estado compartido, y con varios workers debe vivir en un almacenamiento común. Volveremos a esto en la sección sobre estado.
Hash por clave: una cuenta, siempre la misma dirección
Y ahora lo principal para trabajar con cuentas. Hash por clave es una estrategia donde la dirección se selecciona no al azar, sino de forma determinista, basándose en la clave de la tarea. Tomamos el identificador de la cuenta, calculamos su hash, tomamos el resto de la división entre el número de direcciones y obtenemos un índice. La misma cuenta siempre dará el mismo índice y, por lo tanto, la misma dirección.
¿Por qué esto no es solo conveniente sino fundamental? Aquí está la idea clave de todo el artículo.
Por qué el hash determinista es más importante que el azar para el antifraude
Los sistemas antifraude de las plataformas construyen un perfil de comportamiento del usuario. Una de las señales de confianza más fuertes es la estabilidad del entorno de red. Una persona real sale a la red día tras día aproximadamente desde el mismo conjunto de direcciones. Su proveedor cambia raramente, la geografía es estable.
Ahora imagina que una cuenta muestra una dirección nueva de otra subred en cada acción. Para el sistema, esto parece un comportamiento imposible: una persona no puede estar físicamente en diez lugares en un minuto. La selección aleatoria de dirección garantiza generar exactamente esa bandera roja.
El hash determinista resuelve el problema de raíz. La cuenta queda vinculada a la dirección matemáticamente, sin almacenar estado. Incluso si la aplicación se reinicia y pierde toda la memoria, la misma cuenta volverá a calcular la misma dirección. Es una vinculación auto-recuperable. Elegante, ¿verdad?
Piedra angular: cambio en el tamaño del pool
El hash ingenuo por resto de división tiene una debilidad traicionera. Si el número de direcciones cambia – se agrega una o se retira una a cuarentena – el resto de la división cambia para casi todas las claves. Casi todas las cuentas se mudan repentinamente a nuevas direcciones. Eso es exactamente la catástrofe que intentábamos evitar.
La solución es el hashing consistente. Esta técnica hace que agregar o eliminar una dirección reasigne solo una pequeña fracción de las claves, no todas. Las direcciones y las claves se colocan en un anillo imaginario; la clave recorre el anillo hasta la dirección más cercana. Si se elimina una dirección, solo se reubican sus claves; las demás permanecen en su lugar. El hashing consistente es la base correcta para la vinculación sticky en un pool vivo, donde las direcciones entran y salen.
Combinación de estrategias
En una aplicación real, a menudo se combinan estrategias. Un escenario avanzado típico: para tareas con clave de cuenta usamos hash consistente, y dentro del grupo de direcciones que manejan la reserva, aplicamos least-connections. Para recolección de datos sin sesión, round-robin ponderado. El pool puede mantener varias estrategias y elegir la adecuada según el tipo de tarea.
Lista de verificación para elegir estrategia
- ¿Hay concepto de sesión o vinculación a una cuenta? Usa hash por clave, mejor consistente.
- ¿Las tareas son independientes y homogéneas? Round-robin.
- ¿Las direcciones tienen diferente calidad? Selección ponderada con peso dinámico.
- ¿Las tareas varían mucho en duración? Least-connections.
- ¿Carga mixta? Combinación con división del pool en subgrupos.
Health-check: verificaciones de salud pasivas y activas
Un pool es tan bueno como lo que sabe sobre el estado de sus direcciones. De ahí el mecanismo de verificación de salud, health-check. Hay dos enfoques complementarios: pasivo y activo. Se necesitan ambos.
Health-check pasivo a través de errores de tráfico
La verificación pasiva no hace solicitudes separadas. Observa el tráfico real que ya circula por la dirección. Cada llamada a release trae un resultado, y el pool actualiza la reputación de la dirección en función de él. Es gratuito: ya hiciste la solicitud por una razón, solo que además registras su resultado.
¿Qué considerar como señal de deterioro en la observación pasiva?
- Errores de conexión: la dirección no responde, se corta la comunicación.
- Respuestas típicas de bloqueo a nivel de red.
- Un aumento de captchas: señal indirecta pero importante de caída de reputación de la dirección.
- Un aumento brusco de la latencia respecto a la norma histórica de la dirección.
Ventaja del enfoque pasivo: actualidad y cero carga adicional. Desventaja: reacciona solo cuando ya ha pasado tráfico, por lo que las primeras solicitudes afectadas son inevitables. Y no sabe nada sobre direcciones que están inactivas.
Health-check activo mediante sondeos
La verificación activa es un sondeo independiente que el pool realiza por sí mismo, al margen del tráfico de trabajo. Por lo general, es una solicitud ligera a un recurso de control conocido de antemano, que responde de forma estable y permite juzgar la operatividad de la dirección.
Los sondeos activos resuelven lo que el pasivo no puede: verifican las direcciones inactivas y las que están en cuarentena antes de devolverlas al servicio. Es precisamente el sondeo activo la barrera que decide si una dirección sale de la cuarentena y vuelve al servicio.
¿Qué considerar un fallo de verificación?
Definir el fallo es un punto delicado. Si es demasiado estricto, descartarás direcciones normales por un pico aleatorio. Si es demasiado laxo, las direcciones muertas se quedarán en el pool. Un enfoque razonable es un umbral multifactorial.
- Un error aislado no es un fallo. Es ruido. La red es inherentemente poco fiable.
- Un fallo es una señal acumulada: por ejemplo, tres errores de los últimos cinco, o una tasa de éxito en ventana que cae por debajo del setenta por ciento.
- Por separado, la tasa de captchas. El umbral aquí es más bajo, porque un captcha es una señal sobre la reputación de la dirección, no sobre un fallo aleatorio.
Qué intervalos usar
Los intervalos de los sondeos activos son un equilibrio entre frescura de datos y carga innecesaria. Pautas generales:
- Las direcciones sanas bajo carga pueden no recibir sondeos activos; el tráfico pasivo ya habla por ellas.
- Direcciones sanas inactivas: un sondeo cada treinta a sesenta segundos para mantenerlas listas.
- Direcciones en cuarentena: sondeo según el calendario de espera del que hablaremos más adelante.
- No sondees todas las direcciones al mismo tiempo. Distribuye los sondeos en el tiempo, agrega un desplazamiento aleatorio para no crear picos sincrónicos.
Flapping y cómo lo amortigua la histéresis
Ahora uno de los fenómenos más subestimados. Flapping es cuando una dirección oscila rápidamente entre los estados sano y enfermo. Pasó un sondeo – se devuelve al servicio. Inmediatamente un error – se retira. Un segundo después el sondeo vuelve a pasar – se devuelve. Y así en un bucle. Esto desgasta el sistema, crea tirones en las métricas y no permite que la dirección ni trabaje ni descanse.
El tratamiento es la histéresis. Término de la electrónica que significa diferentes umbrales para entrar y salir de un estado. La idea es simple: para considerar una dirección como enferma se necesitan menos señales que para considerarla sana de nuevo. Por ejemplo, tres errores seguidos la ponen en cuarentena, pero para que vuelva se necesitan cinco sondeos exitosos consecutivos. La asimetría de umbrales crea una zona de estabilidad donde las pequeñas fluctuaciones no cambian el estado.
La segunda herramienta contra el flapping es el tiempo mínimo de permanencia en el estado. Una dirección que entra en cuarentena debe permanecer allí un tiempo mínimo, incluso si el sondeo pasa antes. Esto amortigua las oscilaciones rápidas. La combinación de histéresis y tiempo mínimo de espera transforma un sistema nervioso en uno tranquilo y predecible.
Lista de verificación de health-check
- Funcionan ambos tipos de verificación: pasiva por tráfico y activa con sondeos.
- El fallo se define como una señal acumulada, no un error aislado.
- Para la tasa de captchas se ha fijado un umbral separado, más sensible.
- Los sondeos activos están distribuidos en el tiempo con un desplazamiento aleatorio.
- Se ha configurado histéresis: umbral de entrada al problema menor que el umbral de salida.
- Se ha establecido un tiempo mínimo de permanencia en el estado contra el flapping.
Cuarentena y retorno al servicio: espera, límites y protección del pool
Cuando una dirección se considera problemática, no se puede simplemente descartar. A menudo el problema es temporal. La tarea de la cuarentena es darle un descanso a la dirección y luego verificar si se ha recuperado. Aquí hay mucha ingeniería fina.
Espera exponencial
Una cuarentena ingenua mantiene la dirección un tiempo fijo, por ejemplo un minuto, y la devuelve. Pero si la dirección es problemática de forma persistente, la devolverás una y otra vez, obteniendo cada vez una nueva tanda de fallos. La solución es la espera exponencial.
Principio: con cada nueva entrada en cuarentena, el tiempo de espera crece. Primera vez: un minuto. Segunda vez consecutiva: dos minutos. Luego cuatro, ocho, dieciséis. Hasta un techo razonable, por ejemplo una hora. En cuanto la dirección opera exitosamente durante el tiempo suficiente, el contador de espera se reinicia al valor inicial.
Esto resuelve elegantemente dos problemas a la vez. Una dirección que tropezó temporalmente vuelve rápido. Una dirección persistentemente enferma molestará al sistema cada vez menos, hasta convertirse en prácticamente muerta, sin saturar el pool con sondeos inútiles frecuentes.
Agrega a la espera una dispersión aleatoria, el llamado jitter. Sin él, todas las direcciones que entraron en cuarentena al mismo tiempo volverán en una ráfaga simultánea. El jitter distribuye los retornos en el tiempo.
Límite de direcciones en cuarentena simultánea
Y aquí hay una protección críticamente importante que se olvida con más frecuencia. ¿Qué pasa si ocurre un incidente que afecta a la mitad de las direcciones de golpe? Por ejemplo, la plataforma objetivo endureció sus verificaciones. El pool honestamente empezará a poner direcciones en cuarentena una tras otra. Y si no se pone un límite, te quedas con casi nadie en servicio.
De ahí la regla: límite estricto en la proporción de direcciones que pueden estar en cuarentena al mismo tiempo. Por ejemplo, no más del treinta por ciento del pool. Si se alcanza el límite, los nuevos candidatos a cuarentena no se retiran, solo se reduce su peso. La lógica es: mejor trabajar con direcciones ligeramente degradadas que quedarse sin recursos por completo.
Protección contra la situación de todo el pool en cuarentena
Esto es una continuación de la idea anterior, llevada al caso extremo. Imagina: el sondeo al recurso de control se rompió. El recurso cayó o cambió su respuesta. El pool decide que todas las direcciones están muertas y las mete a todas en cuarentena. La aplicación se detiene por completo, aunque las direcciones en realidad están bien.
Mecanismos de protección:
- Mínimo garantizado en servicio. Mantén siempre al menos una o dos direcciones disponibles, aunque formalmente no hayan pasado la verificación. Es mejor que trabajen bajo sospecha a que el sistema se detenga.
- Análisis de correlación. Si los fallos se producen simultáneamente en todas las direcciones, es sospechoso. Lo más probable es que se haya roto un factor común: el sondeo, la red de tu lado, el recurso objetivo. El pool debe ser capaz de reconocer un fallo masivo y no entrar en pánico retirando a todos a la vez.
- Recursos de control separados. No vincules el sondeo activo a un único punto de control. Si se convierte en tu único punto de fallo, las falsas alarmas derrumbarán todo el pool.
Retorno al servicio mediante estado semiabierto
El retorno de la cuarentena no es instantáneo. Una buena práctica es el estado semiabierto, idea del patrón del interruptor automático (circuit breaker). Una dirección que sale de la cuarentena no recibe inmediatamente todo el tráfico. Primero se le da una pequeña fracción, un flujo de prueba. Si las solicitudes de prueba tienen éxito, la fracción se incrementa gradualmente hasta que la dirección alcanza su peso completo. Si las pruebas fallan, vuelve a cuarentena con espera aumentada.
Este ramp-up gradual protege contra una ráfaga prematura de tráfico hacia una dirección que aún no se ha recuperado.
Lista de verificación de cuarentena
- La espera crece exponencialmente en reingresos.
- Se agrega jitter a la espera para evitar retornos sincrónicos.
- Hay un límite en la proporción de direcciones en cuarentena simultánea.
- Se garantiza un mínimo de direcciones en servicio bajo cualquier condición.
- Hay reconocimiento de fallo masivo como indicio de un problema general.
- El retorno se realiza mediante estado semiabierto con aumento gradual del tráfico.
Almacenamiento del estado: memoria del proceso vs. almacenamiento compartido
Toda la lógica que hemos discutido – contadores de carga, reputación, estados de cuarentena – son datos que viven en algún lugar. Dónde exactamente es una decisión arquitectónica determinante. Depende de si tienes un solo proceso o varios.
Memoria del proceso: simple, pero solitario
Si la aplicación funciona en un solo proceso, lo más simple es mantener el estado del pool en memoria. Estructuras de datos comunes: un diccionario de direcciones, sus contadores y temporizadores. Rápido, sin dependencias, sin latencias de red.
Las desventajas son obvias. Reinicio – y todo el historial se pierde, el pool empieza desde cero, olvidando quién estaba en cuarentena. Y lo principal: esto no funciona cuando hay más de un proceso. Cada proceso tendrá su propia visión aislada del mundo. Uno pone una dirección en cuarentena, pero el otro no lo sabe y sigue cargándola.
Almacenamiento compartido: Redis y Memcached
En cuanto tienes varios workers – y bajo carga real casi siempre hay varios – el estado debe ser compartido. Aquí entran en escena almacenamientos rápidos como Redis o Memcached. Viven como un servicio separado al que se conectan todos los workers, proporcionando una visión única y coherente del estado del pool.
Redis es preferible a Memcached en la mayoría de los casos porque ofrece operaciones atómicas, estructuras de datos como conjuntos ordenados y hashes, contadores con incremento atómico y la capacidad de ejecutar pequeños scripts de forma atómica. Todo esto nos será útil.
Condiciones de carrera con múltiples workers
El almacenamiento compartido resuelve el problema de visibilidad, pero genera uno nuevo: las condiciones de carrera. El escenario clásico de least-connections: dos workers leen los contadores al mismo tiempo, ambos ven que la dirección X es la menos cargada, ambos la seleccionan, ambos incrementan el contador. Al final, la dirección recibe el doble de carga, aunque el algoritmo debería haberlo evitado.
Soluciones:
- Operaciones atómicas. El incremento de un contador en Redis es atómico por naturaleza. Úsalo en lugar de leer, incrementar y escribir por separado.
- Scripts. La lógica compleja de selección, donde hay que leer varios valores y tomar una decisión, conviene encapsularla en un único script atómico del lado del almacenamiento. Así nadie se interpone entre la lectura y la escritura.
- Bloqueos distribuidos. Para secciones críticas se puede tomar un bloqueo corto. Pero con cuidado: los bloqueos afectan al rendimiento y ellos mismos pueden convertirse en una fuente de problemas. Las operaciones atómicas son casi siempre mejores.
TTL de las entradas
Una herramienta importantísima en el almacenamiento compartido es el TTL, tiempo de vida de una entrada, tras el cual desaparece automáticamente. Salva de la acumulación de basura y de que los estados se atasquen.
Dónde aplicar TTL:
- Vinculación sticky de clave a dirección. ¿Recuerdas la sesión de negocio? Su duración es el TTL de la entrada de vinculación. Si pones un TTL de quince minutos, después de quince minutos de inactividad la vinculación se disuelve sola, y la cuenta podrá obtener una nueva vinculación la próxima vez que la solicite. Es una forma limpia y elegante de expresar la duración de la sesión.
- Contador de alquileres activos. Si un worker falla sin llamar a release, el contador corre el riesgo de quedarse atascado en un valor inflado para siempre. El TTL o una verificación periódica protegen contra esto. A menudo el alquiler se implementa como una entrada con un TTL ligeramente mayor que la duración máxima de la tarea, para que un worker caído no retenga la dirección eternamente.
- Estado de cuarentena. El propio tiempo de espera se expresa naturalmente mediante TTL: la entrada de cuarentena vive exactamente el tiempo que dura la espera, y al expirar desaparece, abriendo el camino para el retorno.
Enfoque híbrido
En la práctica, a menudo se usa un enfoque híbrido. Los datos calientes, leídos con frecuencia, se cachean localmente en la memoria del worker por un período corto, mientras que la fuente de verdad sigue siendo el almacenamiento compartido. Esto reduce el número de accesos a Redis, pero requiere cuidado con la desincronización. Un buen compromiso es un caché local con un TTL muy corto, por ejemplo uno o dos segundos, para métricas que no requieren precisión instantánea.
Observabilidad: métricas por cada dirección y cómo distinguir una IP mala de un sitio malo
No se puede gestionar lo que no se mide. La observabilidad convierte al pool de una caja negra en un sistema transparente donde ves la salud de cada dirección y entiendes las causas de los problemas.
Métricas por cada dirección
El conjunto mínimo que conviene mantener para cada dirección en una ventana deslizante:
- Tasa de éxito. Relación entre finalizaciones exitosas y el número total de tareas. El principal indicador integral de salud.
- Latencia. No solo el promedio, sino también percentiles. La mediana y, digamos, el percentil noventa y cinco contarán mucho más sobre las colas que el promedio, que se distorsiona fácilmente con valores atípicos.
- Tasa de captchas. Una métrica separada y muy reveladora. Un aumento en la tasa de captchas en una dirección es una señal temprana de caída de su reputación, a menudo antes de que empiecen a crecer los errores directos.
- Número de alquileres activos. La carga actual, necesaria para least-connections y para entender la distribución.
- Historial de cuarentenas. Cuántas veces y durante cuánto tiempo la dirección ha estado en cuarentena. Los infractores crónicos son evidentes de inmediato.
Métricas del pool en su conjunto
- Proporción de direcciones en cada estado: cuántas sanas, degradadas, en cuarentena, muertas.
- Rendimiento total y tasa de éxito agregada.
- Frecuencia de entradas en cuarentena a lo largo del tiempo: un pico indica un incidente.
- Nivel de ocupación del límite de cuarentena: acercarse a él es una señal de alarma.
Cómo distinguir una IP mala de un sitio malo
He aquí una pregunta que separa a un sistema maduro de uno ingenuo. Llegaron los errores, ¿quién tiene la culpa? ¿Una dirección problemática o el sitio objetivo en sí que está temporalmente inaccesible para todos? Si te confundes, empezarás a poner en cuarentena direcciones sanas por los pecados del servidor ajeno.
El método para separar diagnósticos es el análisis de correlación:
- Problema en una sola dirección. Si los errores y captchas se concentran en una o varias direcciones mientras las demás funcionan con normalidad, la culpa es de la dirección. A cuarentena.
- Problema en todas las direcciones hacia un mismo sitio. Si en todas las direcciones la tasa de éxito cayó bruscamente hacia un sitio objetivo concreto, mientras que hacia otros todo va bien, la culpa no es del pool sino de ese sitio. Poner direcciones en cuarentena es inútil y perjudicial.
- Problema en todas las direcciones hacia todos los sitios. Lo más probable es que el problema esté de tu lado: red, infraestructura, el propio sistema de sondeos. Aquí tampoco se puede culpar a las direcciones.
Conclusión práctica: las métricas deben cortarse no solo por dirección, sino también por el par dirección-sitio. Entonces una matriz de éxitos mostrará de inmediato dónde la fila está completamente roja (culpa de la dirección) y dónde la columna está completamente roja (culpa del sitio). Esta representación bidimensional simple ahorra horas de depuración y salva al pool de autodestruirse en un momento.
Logs y trazabilidad
Además de las métricas agregadas, es útil registrar las decisiones del pool: por qué se eligió esta dirección, por qué esa se envió a cuarentena. Cuando algo salga mal, estos rastros de decisiones serán tu salvavidas. No registres cada solicitud en detalle: te ahogarás. Registra las transiciones de estado y las decisiones atípicas.
Práctica: esqueleto de pool en Python y Node.js
Pasemos de la teoría al código. Analicemos las piezas clave de la implementación en dos plataformas populares. Son esqueletos, armazones sobre los que colgarás la especificidad de tu proyecto. Omitimos detalles para que queden claras las ideas principales: la interfaz acquire y release, la selección de dirección y el registro del resultado.
Interfaz: acquire y release
Acordemos un contrato. El método acquire recibe una clave opcional – por ejemplo el identificador de la cuenta para vinculación sticky – y devuelve una dirección. El método release recibe la dirección y el resultado de la tarea. El resultado se describe mínimamente con dos hechos: si hubo éxito y si se encontró un indicio de captcha o bloqueo.
Esqueleto en Python
Consideremos un pool simplificado en Python. Mantenemos la lógica en memoria para mayor claridad, pero señalamos dónde se conecta el almacenamiento compartido.
Elementos clave de la estructura de datos: un diccionario donde la clave es la dirección y el valor es un objeto de estado con campos de reputación, número de alquileres activos, marca de tiempo de salida de cuarentena y duración actual de espera. Por separado guardamos una tabla de vinculaciones sticky clave-dirección.
El pseudocódigo del método acquire en un lenguaje similar a Python sería así. Primero, comprobamos si hay clave. Si la clave está definida y existe una vinculación viva y la dirección vinculada está sana, la devolvemos, renovando el tiempo de vida de la vinculación. Si no hay vinculación, seleccionamos una dirección mediante hash consistente de la clave entre las sanas, guardamos la vinculación con TTL y devolvemos la dirección. Si no hay clave en absoluto, aplicamos la estrategia para tareas sin sesión, por ejemplo selección ponderada entre las sanas. En todos los casos, incrementamos el contador de alquileres activos de la dirección seleccionada.
Pseudocódigo de release: decrementamos el contador de alquileres activos. Actualizamos la ventana deslizante de reputación según el resultado: agregamos éxito o fracaso, por separado registramos el indicio de captcha. Si las señales acumuladas cruzan el umbral de fallo teniendo en cuenta la histéresis, pasamos la dirección a cuarentena: marcamos el estado, calculamos el tiempo de espera como base multiplicado por dos elevado al número de reincidencias, limitado por un techo, agregamos jitter, establecemos TTL o marca de tiempo de retorno. Si el resultado es bueno y la dirección ha estado estable durante mucho tiempo, reiniciamos el contador de reincidencias de cuarentena.
Un bucle de fondo separado, cada cierto intervalo, recorre las direcciones en cuarentena cuyo tiempo de espera haya expirado y lanza un sondeo activo. Si pasa el número necesario de veces considerando la histéresis, pasamos la dirección a estado semiabierto con un peso pequeño, luego gradualmente a sano. Si no pasa, prolongamos la cuarentena con espera aumentada.
Detalles prácticos importantes de la implementación en Python: usa asincronía si tienes muchas tareas en paralelo; envuelve el acceso a las estructuras compartidas con la sincronización adecuada; y al pasar a varios procesos, reemplaza los diccionarios internos por accesos a Redis mediante comandos atómicos y scripts. El contador de alquileres activos encaja perfectamente con un incremento atómico, la vinculación clave-dirección con una entrada con TTL, y el conjunto de direcciones por estado es cómodo de manejar con conjuntos ordenados donde el peso de la dirección es su evaluación.
Esqueleto en Node.js
En Node.js es natural el modelo asíncrono, y el pool suele implementarse como una clase con métodos asíncronos acquire y release que devuelven promesas. La idea es la misma, cambia la idiomática.
Estructura de estado: un objeto-diccionario de direcciones, donde el valor contiene la reputación como un buffer circular de los últimos resultados, el contador de alquileres activos y los campos de cuarentena. Para varios workers, y en Node esto suele ser un clúster de procesos, el estado también se saca a Redis porque cada proceso está aislado.
Método acquire en Node: comprobamos asíncronamente la vinculación sticky a través del almacenamiento; si existe una vinculación viva y sana, la devolvemos y renovamos el TTL. En caso contrario, seleccionamos una dirección – para clave con hash consistente, para tarea sin sesión con ponderación. Incrementamos atómicamente el contador de alquileres. Devolvemos la dirección.
Método release en Node: decrementamos atómicamente el contador. Escribimos el resultado en la ventana de reputación. Comprobamos los umbrales con histéresis y, si hay fallo, establecemos cuarentena con espera exponencial y jitter, respetando el límite de direcciones totales en cuarentena – si se alcanza el límite, en lugar de cuarentena reducimos el peso.
La verificación de fondo en Node se implementa cómodamente con un temporizador periódico que selecciona direcciones con espera expirada y lanza sondeos activos, distribuyéndolos en el tiempo. Los sondeos exitosos devuelven gradualmente la dirección, agregándole peso por pasos.
Un consejo general para ambas plataformas: no intentes hacerlo perfecto desde la primera vez. Empieza con round-robin más health-check pasivo más cuarentena simple en memoria. Asegúrate de que la interfaz acquire y release sea cómoda para tu lógica de negocio. Luego añade secuencialmente sticky mediante hash, sondeos activos, almacenamiento compartido, límites y observabilidad. Deja que cada capa madure bajo carga real.
Mini lista de verificación de implementación
- La interfaz se reduce a acquire con clave opcional y release con resultado.
- Toda la complejidad de los estados está oculta dentro del pool; el código de negocio no sabe de ella.
- Sticky implementado mediante hash determinista, mejor consistente.
- Los contadores y vinculaciones son atómicos con varios workers.
- Hay un bucle de fondo de sondeos activos para la cuarentena y direcciones inactivas.
- Se escriben métricas en cada release.
Errores típicos: lo que no se debe hacer
Hemos asimilado la teoría, hemos construido el esqueleto. Ahora repasemos los rastrillos en los que se pisa una y otra vez. Conocer estos errores te ahorrará semanas de depuración.
Pool común para diferentes sitios
Es tentador tener un solo pool grande y pasar por él todo el tráfico hacia todos los sitios objetivo a la vez. No lo hagas sin pensar. La reputación de una dirección es diferente en distintos sitios. Una dirección que funciona perfectamente con uno puede estar bloqueada en otro. Si mezclas todo en un solo pool y en una sola estadística, obtendrás una imagen borrosa en la que una dirección buena para una tarea será castigada injustamente por problemas con otra.
Lo correcto es llevar la reputación en el par dirección-sitio, y dividir lógicamente los pools o subgrupos para diferentes direccionamientos. Así la decisión de cuarentena se toma de forma precisa y justa.
Falta de límite de tareas por dirección
Incluso una dirección sana tiene un límite. Si el pool asigna alegremente cientos de tareas simultáneas a una dirección popular, tú mismo creas una anomalía: una concentración de actividad artificialmente alta desde un solo punto. Esto sobrecarga la dirección y además se ve sospechoso para la plataforma.
Introduce un techo en el número de alquileres simultáneos por dirección. Al alcanzar el techo, la dirección se excluye temporalmente de los candidatos y el tráfico va a otras. Esta simple restricción protege de muchos males.
Rotación en medio de una sesión
Un pecado capital al trabajar con cuentas. La cuenta empezó una acción con una dirección, y debido a una activación de cuarentena o un cambio en el tamaño del pool, en medio de la sesión se mudó repentinamente a otra. Desde la perspectiva de la plataforma, el usuario se teletransportó. Es una de las señales más evidentes de automatización.
Protección: mientras haya una sesión activa, la vinculación de la clave con la dirección debe ser intocable, incluso si la dirección se ha degradado un poco. Cambia la vinculación solo en los límites de la sesión – al finalizarla de forma natural o al expirar el TTL. Y si la dirección muere por completo y no hay otra opción, es mejor finalizar la sesión correctamente que reubicarla a mitad de camino.
Otros errores frecuentes
- Un error aislado como sentencia. Poner una dirección en cuarentena por un único fallo aleatorio. La red no es fiable, un fallo esporádico es normal. Solo una señal acumulada.
- Falta de histéresis. Lleva al flapping y a los tirones de los que hablamos.
- Espera fija en lugar de exponencial. Una dirección persistentemente enferma volverá eternamente y estropeará las estadísticas.
- Sin límite de cuarentena. Un incidente puede meter a todo el pool en cuarentena y detener el sistema.
- Sondeos sincrónicos. Todas las direcciones se sondean al mismo tiempo, creando picos de carga.
- Estado solo en memoria con varios workers. Cada proceso vive en su propia realidad, sin coordinación.
- Alquileres colgados. No se llamó a release debido a un fallo, el contador se queda inflado, la dirección se considera ocupada para siempre. Se cura con TTL en el alquiler.
- Confianza ciega en un único recurso de control. Si cae, todo el pool se cree muerto.
Herramientas y recursos para la implementación
Reunamos el arsenal práctico. Lo que realmente será útil al construir el pool.
Almacenamientos de estado
- Redis. La opción principal para el estado compartido. Contadores atómicos, conjuntos ordenados para pesos y estados, hashes para metadatos de dirección, TTL incorporado, scripts para lógica compleja atómica. Prácticamente ideal para nuestra tarea.
- Memcached. Más simple y ligero, servirá si solo necesitas cachés básicas con contadores, pero pierde frente a Redis en riqueza de estructuras y atomicidad de operaciones complejas.
Bibliotecas y enfoques por ecosistema
- Python. Stack asíncrono para tareas paralelas, cliente Redis con soporte de asincronía y scripts, implementaciones listas de hashing consistente, así como el patrón del interruptor automático (circuit breaker) para estado semiabierto.
- Node.js. Clases asíncronas idiomáticas, clientes Redis maduros, modelo de clúster de procesos donde el estado compartido es obligatorio, bibliotecas de hashing consistente e implementaciones de circuit breaker.
Observabilidad
- Sistema de métricas de series temporales. Para almacenar indicadores de tasa de éxito, latencia y captchas por dirección y por pares dirección-sitio.
- Dashboards. Visualización de la distribución de estados del pool, matriz dirección-sitio, frecuencia de cuarentenas. Un dashboard permitirá distinguir de un vistazo una dirección mala de un sitio malo.
- Alertas. Notificación al acercarse al límite de cuarentena, ante un fallo masivo, ante una caída de la tasa de éxito general por debajo del umbral.
Qué construir uno mismo
Es probable que no exista una biblioteca universal de pool que se adapte a todos tus matices: la lógica de negocio de las sesiones es demasiado específica. Por eso, el núcleo del pool suele escribirse a medida, apoyándose en los ladrillos mencionados: almacenamiento, hashing, métricas, patrón de interruptor. La buena noticia es que, con una interfaz cuidadosa acquire y release, este núcleo resulta compacto y reutilizable entre proyectos.
Casos prácticos y resultados de aplicación
Analicemos varios escenarios generalizados que muestran cómo funcionan en la práctica los principios de este artículo. Las cifras son ilustrativas, pero las proporciones reflejan la dinámica típica.
Caso uno: recolección de datos públicos sin sesiones
Tarea: recolección masiva de información pública desde páginas independientes. Comenzaron con selección aleatoria ingenua desde un archivo. La tasa de éxito rondaba el setenta por ciento, porque las direcciones muertas seguían recibiendo tráfico al mismo nivel que las vivas.
Implementaron un pool con selección ponderada y health-check pasivo. El peso de la dirección se recalculaba en una ventana deslizante de tasa de éxito. Las direcciones muertas perdían peso rápidamente y casi dejaban de recibir tareas. Agregaron sondeos activos de direcciones inactivas y cuarentena exponencial.
Resultado: la tasa de éxito subió de aproximadamente setenta a más del noventa por ciento. El número de intentos inútiles hacia direcciones muertas cayó múltiples veces. La principal conclusión del caso: incluso sin sesiones, solo el enrutamiento adaptativo por reputación da un salto notable en eficiencia.
Caso dos: trabajo con cuentas y sticky-sessions
Tarea: operación estable de múltiples cuentas, donde la estabilidad del entorno de red de cada una es crítica. Inicialmente usaban selección aleatoria de dirección, y las cuentas saltaban constantemente entre subredes. Resultado: una avalancha de solicitudes de verificación y aumento de la tasa de captchas.
Pasaron a una vinculación determinista mediante hash consistente del identificador de la cuenta, con almacenamiento de las vinculaciones en Redis y un TTL igual a la duración de la sesión de negocio. Introdujeron una regla estricta: ninguna rotación en medio de una sesión. La vinculación solo cambiaba en los límites.
Resultado: la tasa de captchas por cuentas disminuyó notablemente, las solicitudes de verificación cayeron y el comportamiento de las cuentas se volvió estable y natural para las plataformas. La conclusión del caso: para el antifraude, la previsibilidad del entorno de red vale más que cualquier rotación sofisticada.
Caso tres: incidente y protección contra avalancha de cuarentena
Durante la operación del sistema, la plataforma objetivo endureció repentinamente sus verificaciones. En todas las direcciones a la vez empezaron a aparecer captchas. El pool, que no tenía límite de cuarentena, en la primera versión de este escenario metió a casi todo el pool en cuarentena en cuestión de minutos; el sistema prácticamente se detuvo.
Después de la corrección, agregaron tres cosas: límite en la proporción de direcciones en cuarentena, reconocimiento de fallo masivo como indicio de un problema general, y un mínimo garantizado de direcciones en servicio. Al repetirse el incidente, el pool reconoció que los fallos se correlacionaban en todas las direcciones hacia un mismo sitio, entendió que la culpa no era suya y no provocó una avalancha. Simplemente redujo los pesos y continuó operando a través de un mínimo de recursos hasta que la situación se normalizó.
Resultado: en lugar de una parada total, el sistema sobrevivió al incidente con degradación, no con fallo total. Conclusión: la resiliencia se mide por el comportamiento en el peor día, no en el mejor.
Caso cuatro: diagnóstico de dirección mala vs. sitio malo
Un equipo estuvo semanas poniendo periódicamente direcciones sanas en cuarentena sin entender por qué. Las métricas solo se llevaban por dirección, sin desglose por sitio. Cuando añadieron la matriz bidimensional dirección-sitio, la imagen se aclaró al instante: el problema no eran las direcciones, sino un sitio caprichoso que daba picos de errores para todas las direcciones a la vez.
Configuraron la lógica para que los fallos correlacionados por columna de sitio no castigaran a las direcciones. Resultado: el número de cuarentenas falsas cayó casi a cero y la capacidad útil del pool aumentó sin agregar una sola dirección nueva. Conclusión: un corte correcto de las métricas a veces vale más que cualquier algoritmo.
Preguntas frecuentes
¿Se necesita un pool si solo hay unas pocas direcciones?
Incluso con un puñado de direcciones, el pool se amortiza en cuanto aparece algo de carga o el concepto de sesión. Un health-check pasivo y una cuarentena simple te protegen de golpear una dirección muerta. La vinculación sticky preserva la estabilidad de las cuentas. Un hash consistente completo y un almacenamiento compartido para un par de direcciones quizá sean excesivos, pero un pool básico en memoria con interfaz acquire y release vale la pena casi siempre.
¿Cómo elegir la duración de la sticky-session?
Parte del significado de negocio, no de consideraciones técnicas. Pregúntate: ¿cuánto tiempo debe percibirse una secuencia de acciones como una única visita continua? Para escenarios cortos, minutos; para trabajo bajo un perfil durante un período de actividad, decenas de minutos u horas. Establece esa duración como TTL de la vinculación y renuevalo en cada acceso dentro de la sesión para que el trabajo activo no se interrumpa a mitad de frase.
¿Qué pasa si la dirección vinculada muere en medio de una sesión?
Es el caso más desagradable. La regla general es no rotar en medio de una sesión. Si la dirección se ha degradado pero aún está viva, continúa usándola hasta el final de la sesión. Si muere por completo, es preferible finalizar o pausar la sesión correctamente que reubicarla a otra dirección y crear un efecto de teletransportación. Diseña la lógica de negocio de modo que finalizar una sesión sea menos doloroso que migrarla.
¿Con qué frecuencia hacer sondeos activos?
No sondees direcciones sanas con carga; el tráfico vivo habla por ellas a través del control pasivo. Las direcciones sanas inactivas verifícalas cada decenas de segundos para mantenerlas listas. Las direcciones en cuarentena, según el calendario de espera exponencial. Distribuye los sondeos en el tiempo con jitter para no crear picos sincrónicos de actividad.
¿Redis es obligatorio o se puede usar solo memoria?
Si tienes estrictamente un solo proceso y estás dispuesto a perder el estado al reiniciar, la memoria es aceptable al inicio. En cuanto aparece un segundo worker, el almacenamiento compartido se vuelve prácticamente obligatorio; de lo contrario, los procesos vivirán en realidades diferentes sin coordinación. Redis es la opción más conveniente por sus operaciones atómicas, estructuras de datos y TTL. Puedes empezar con memoria, pero deja una abstracción de almacenamiento para que la migración a Redis no requiera reescribir la mitad del código.
¿Cómo distinguir un problema de dirección de un problema del sitio objetivo?
Lleva métricas en el par dirección-sitio, no solo por dirección. Si los errores se concentran en una sola dirección hacia todos los sitios, la culpa es de la dirección. Si son en todas las direcciones hacia un solo sitio, la culpa es del sitio y no se debe castigar a las direcciones. Si son en todas las direcciones hacia todos los sitios, busca el problema en tu lado: red, infraestructura, el propio sistema de sondeos. Este corte bidimensional es la forma más fiable de hacer un diagnóstico correcto.
¿Qué hacer si casi todo el pool entra en cuarentena?
Esto debería ser imposible con una protección adecuada. Establece un límite en la proporción de direcciones en cuarentena simultánea. Garantiza un mínimo de direcciones en servicio bajo cualquier circunstancia. Enseña al pool a reconocer un fallo masivo simultáneo como indicio de un problema general, no culpa de las direcciones. Al alcanzar el límite, pasa de retirar a cuarentena a simplemente reducir los pesos: es mejor trabajar con recursos ligeramente degradados que detenerse por completo.
¿Qué estrategia de selección tomar por defecto?
Si no tienes sesiones y las direcciones son homogéneas, empieza con round-robin. Si las direcciones tienen diferente calidad, selección ponderada con peso dinámico según métricas. Si las tareas varían mucho en duración, least-connections. Si hay vinculación a cuentas o sesiones, hash consistente por clave, y esto no se discute porque determina la estabilidad del trabajo con cuentas. En sistemas complejos, combina estrategias según el tipo de tarea.
¿Es necesario considerar los captchas por separado en las métricas?
Sí, sin duda, y con un umbral más sensible que para los errores comunes. El captcha es una señal temprana y precisa de caída de reputación de una dirección, a menudo anterior a los rechazos directos. Si mezclas captchas con errores generales, perderás este indicador adelantado. Una métrica separada de tasa de captchas por dirección permitirá reducir el peso de una dirección problemática antes de que empiece a fallar abiertamente.
¿Cómo evitar alquileres colgados cuando un worker cae?
No te bases solo en la llamada explícita a release. Implementa el alquiler como una entrada con TTL ligeramente superior a la duración máxima permitida de la tarea. Si el worker cae y no devuelve la dirección, la entrada expirará sola y el contador de carga activa se recuperará. Adicionalmente, puedes verificar periódicamente los contadores con la realidad. Esto protege al pool de una fuga lenta de capacidad debido a alquileres colgados.
Conclusión y siguientes pasos
Hemos recorrido un largo camino. Desde la cruda verdad sobre por qué una lista de direcciones en un archivo de texto se desmorona bajo carga real, hasta un sistema de ingeniería completo para gestionar direcciones dentro de la aplicación. Resumamos lo principal.
Un pool no es una lista, sino un objeto con comportamiento, oculto tras una interfaz simple de acquire y release. La estrategia de selección de dirección viene dictada por la tarea: round-robin para homogéneas, selección ponderada para calidades diferentes, least-connections para tareas de duración variable, y – críticamente importante para trabajar con cuentas – un hash determinista, mejor consistente, por clave. Es la previsibilidad del entorno de red, no la sofisticación aleatoria, lo que genera confianza por parte de los sistemas antifraude.
La salud de las direcciones se sostiene sobre dos pilares: control pasivo mediante tráfico real y sondeos activos. El fallo se define por una señal acumulada, no por un error aislado, y el flapping se amortigua con histéresis y un tiempo mínimo de espera. La cuarentena se basa en una espera exponencial con jitter, limitada obligatoriamente por una proporción máxima de direcciones retiradas, y protegida contra la catástrofe de que todo el pool termine en el banquillo. El estado, con varios workers, vive en un almacenamiento compartido con operaciones atómicas y TTL. Y la observabilidad en el par dirección-sitio permite distinguir una dirección mala de un sitio malo, una habilidad que ahorra semanas.
¿Qué hacer ahora? Propongo un plan concreto de implementación por pasos.
- Envuelve el trabajo actual con direcciones en la interfaz acquire y release, sin cambiar nada internamente aún. Esto prepara el terreno.
- Agrega health-check pasivo y cuarentena simple en memoria. Ya aquí sentirás un aumento de estabilidad.
- Introduce la estrategia de selección necesaria. Si trabajas con cuentas, implanta desde ya la vinculación sticky mediante hash por clave.
- Conecta los sondeos activos y la espera exponencial con límites de protección.
- Traslada el estado a un almacenamiento compartido en cuanto aparezca un segundo worker. Implementa atomicidad y TTL.
- Configura métricas y dashboards, obligatoriamente con el corte dirección-sitio.
- Ajusta la histéresis y los umbrales según tus datos reales – aquí no hay números universales, solo tus observaciones.
No intentes construir todo de golpe. Deja que cada capa madure bajo carga, toma métricas, saca conclusiones, sigue adelante. Un pool de proxies maduro no es una construcción única, sino un sistema vivo que ajustas a medida que crecen las tareas. Pero incluso los primeros pasos de esta guía convertirán una frágil lista de direcciones en un soporte confiable para tu aplicación. Y eso, admitámoslo, vale la pena el esfuerzo invertido.