← Назад в базу знаний

Ротация IP без разрыва сессии: sticky session на прокси-пулах

Ротация IP без разрыва сессии: sticky session на прокси-пулах

Суть проблемы

Обычная ротация IP выглядит так: запрос ушёл через 185.234.XX.XX, ответ пришёл. Следующий запрос — уже через 91.243.YY.YY. Для большинства задач это нормально. Но есть сценарии, где смена IP на лету убивает всю логику.

Сервер видит: пришёл запрос от клиента A, начал сессию. Через секунду — запрос от клиента B, но с теми же куками. И сервер в лёгком недоумении. Или вообще сбрасывает сессию, видя несоответствие IP в сессионных данных.

Как работает привязка сессии

Sticky session — механизм, который закрепляет конкретный IP из пула за сессией клиента. Пока сессия жива — IP не меняется. Никаких магий. Просто прокси-сервер запоминает: этому клиенту мы отдаём 185.234.XX.XX и не трогаем его.

Реализация через хеширование:

```

hash = crc32(client_ip + session_id) % pool_size

proxy_ip = pool[hash]

```

Пока сессия активна — хеш не меняется. Клиент получает тот же IP.

Техническая реализация

В прокси-пуле sticky session обычно работает на уровне балансировщика. Пример конфигурации для nginx:

```

upstream backend {

ip_hash;

server 185.234.xx.xx:3128;

server 91.243.yy.yy:3128;

server 193.32.zz.zz:3128;

}

server {

listen 80;

location / {

proxy_pass http://backend;

proxy_set_header X-Real-IP \$remote_addr;

}

}

```

`ip_hash` — директива, которая закрепляет клиента за конкретным upstream-сервером на основе его IP. Но есть нюанс: если клиент сидит за NAT — все пользователи из одной сети получат один и тот же прокси. Для прокси-пулов это не проблема, даже наоборот.

Внутренняя кухня: как прокси хранит состояние

Прокси-сервер ведёт таблицу сессий. Примерная структура:

```

session_id | client_ip | proxy_ip | created_at | last_used | ttl

123456 | 10.0.0.1 | 185.234.xx.xx | 1712345678 | 1712345789 | 300

789012 | 10.0.0.2 | 91.243.yy.yy | 1712345700 | 1712345795 | 300

```

TTL обычно 5-10 минут. Если клиент молчит дольше — привязка сбрасывается. При новом запросе — новый IP из пула. Это защита от ситуаций, когда клиент ушёл, а запись висит часами.

Реальные цифры

Замеры на пуле из 50 IPv6 адресов:

| Метрика | Без sticky | Со sticky |

|---------|------------|-----------|

| Средняя задержка установки соединения | 45 мс | 48 мс |

| Разброс задержек | 30-120 мс | 40-80 мс |

| Ошибок сессии | 12% | 0.3% |

| Нагрузка на прокси | 100% | 103% |

Разница в задержке — 3-5 мс. Погрешность измерения. А вот ошибки сессий упали в 40 раз.

Почему sticky session не панацея

Есть грабли. Главная — если прокси лёг. Привязка была к 185.234.xx.xx, а он упал. Механизм должен переключить клиента на другой IP. Но если реализация тупая — клиент повиснет в ошибках до истечения TTL.

Хорошая реализация делает так: при ошибке соединения — немедленно переключает на новый IP из пула, но запоминает, что старый сдох. И больше не использует его для этого клиента.

Плохая реализация: ждёт TTL, потом пытается переподключиться к мёртвому IP. И так по кругу.

Пример: работа с сессиями через curl

```bash

!/bin/bash

PROXY_POOL=("185.234.xx.xx:3128" "91.243.yy.yy:3128" "193.32.zz.zz:3128")

SESSION_ID="test_session_123"

Вычисляем индекс по сессии

HASH=\$(echo -n "\$SESSION_ID" | cksum | awk '{print \$1}')

INDEX=\$((HASH % \${#PROXY_POOL[@]}))

PROXY=\${PROXY_POOL[\$INDEX]}

curl -x "http://\$PROXY" \

-b "session_id=\$SESSION_ID" \

-c cookies.txt \

https://api.example.com/data

```

Каждый запуск — один и тот же прокси. Пока сессия жива.

Python-реализация sticky session

```python

import hashlib

import requests

class StickyProxySession:

def __init__(self, proxy_list):

self.proxy_list = proxy_list

self.session_map = {}

def get_proxy(self, session_id):

if session_id in self.session_map:

return self.session_map[session_id]

hash_val = int(hashlib.md5(session_id.encode()).hexdigest(), 16)

index = hash_val % len(self.proxy_list)

proxy = self.proxy_list[index]

self.session_map[session_id] = proxy

return proxy

def request(self, url, session_id):

proxy = self.get_proxy(session_id)

proxies = {

'http': f'http://{proxy}',

'https': f'http://{proxy}'

}

return requests.get(url, proxies=proxies)

pool = StickyProxySession([

'185.234.xx.xx:3128',

'91.243.yy.yy:3128',

'193.32.zz.zz:3128'

])

response = pool.request('https://api.example.com/login', 'user_session_42')

```

Когда sticky session обязателен

Три сценария, где без неё хреново:

**Авторизация с IP-блокировкой.** Некоторые сервисы после входа запоминают IP и проверяют его при каждом запросе. Смена IP — мгновенный логаут. Sticky session решает это полностью.

**Работа с WebSocket.** Установили соединение через 185.234.xx.xx. Сервер ждёт keepalive с того же IP. Если прокси сменился — соединение рвётся. Приходится переустанавливать, теряя состояние.

**Скачивание больших файлов.** HTTP-сервер часто проверяет IP при запросах Range. Сменили прокси во время скачивания — сервер отвечает 403 или отдаёт файл с начала.

Кейс: парсинг с сессионными куками

Был проект: парсинг интернет-магазина, где после добавления товара в корзину сервер проверял IP. Если IP менялся — корзина очищалась.

Проблема: стандартная ротация меняла IP каждые 3 запроса. На 4-м запросе корзина пуста. Добавление товара — снова ок. Ещё 3 запроса — опять пусто.

Решение: sticky session с привязкой по ID сессии. IP меняется только при старте новой сессии. Внутри сессии — стабильный IP. Корзина перестала сбрасываться.

Цифры: до sticky — 30% успешных сессий. После — 98%. Остальные 2% — проблемы с сетью.

Кейс: WebSocket для трейдинга

Пример: терминал для криптобиржи. WebSocket-соединение держится часами. Биржа проверяет IP при каждом торговом ордере.

Проблема: обычная ротация разрывала WebSocket каждые 5-10 минут. Приходилось переподключаться, теряя текущие данные стакана.

Решение: sticky session на уровне балансировщика. Один IP на всё время работы терминала. WebSocket живёт сутками. Задержка стабильная — 12-15 мс.

Кейс: работа с GeoIP-сервисами

Сервис геолокации определял страну по IP. При смене прокси страна могла измениться. Сервер сбрасывал настройки языка и валюты.

Проблема: пользователь выбирал рубли, а после ротации видел доллары. И наоборот.

Решение: sticky session с привязкой к региону. Если клиенту нужна Россия — он получает IP из российского пула. И не меняет его до конца сессии.

Как тестировать sticky session

Простейший тест:

```bash

for i in {1..10}; do

curl -x http://185.234.xx.xx:3128 \

-H "X-Session-ID: test_123" \

-s https://httpbin.org/ip | jq .origin

done

```

Если все 10 запросов вернули один IP — sticky работает. Если IP плавает — механизм не включён или настроен неправильно.

Грабли и подводные камни

Первая грабля — TTL сессии. Если клиент ушёл на 10 минут, а потом вернулся — привязка могла сброситься. Новый IP. Если сервер всё ещё помнит старый — проблемы.

Вторая грабля — перегрузка одного прокси. Если 1000 клиентов закреплены за одним IP, а остальные 49 IP простаивают — баланс хреновый. Решается рандомизацией начального распределения.

Третья грабля — отказоустойчивость. Упал прокси, к которому привязаны 500 сессий. Механизм должен переключить их быстро. Без этого — 500 клиентов в ошибках.

Четвёртая грабля — NAT. За одним публичным IP могут сидеть 100 реальных пользователей. Sticky session по IP клиента объединит их всех под один прокси. Для некоторых сценариев это проблема.

✔️Купить прокси