Внутренняя архитектура

Обновлено:

Эта документация предназначена для системных архитекторов, желающих понимать, за счет каких инженерных решений slyber_turn достигает высокой пропускной способности и минимальных накладных расходов на CPU и память.

1. Сетевой стек: SO_REUSEPORT и автотюнинг буферов сокетов

В классических реализациях TURN один UDP сокет на порту 3478 становится узким местом из за блокировок на уровне ядра ОС при чтении пакетов из одной очереди.

  • Масштабирование по ядрам CPU: На Linux slyber_turn по умолчанию создает столько UDP сокетов на порту 3478, сколько доступно ядер процессора (TURN_THREADS = runtime.NumCPU()), устанавливая флаг сокета SO_REUSEPORT и SO_REUSEADDR. Ядро Linux самостоятельно распределяет входящие пакеты по очередям разных сокетов, позволяя горутинам pion/turn обрабатывать трафик параллельно без конкуренции за один дескриптор.
  • Каскадный тюнинг SO_RCVBUF и SO_SNDBUF:
  • Для управляющих сокетов (3478) сервер последовательно пытается установить размер буфера приема и отправки в 8 МБ → 4 МБ → 1 МБ → 512 КБ (выбирая максимально разрешенный ядром размер).
  • Для каждого выделяемого релейного сокета (49152–65535) автоматически применяется буфер 1 МБ → 512 КБ → 256 КБ.

2. Неблокирующий шейпинг трафика и батчинг телеметрии

Каждое релейное соединение оборачивается в trackedPacketConn (для UDP) или trackedConn (для TCP). Так как через эти методы проходят десятки тысяч пакетов в секунду, на горячем пути (ReadFrom / WriteTo) полностью исключены мьютексы (sync.Mutex):

  • Lock Free bandwidthLimiter: Ограничитель скорости на сессию реализован на атомарных операциях (atomic.Int64 + CompareAndSwap) с окном пополнения токенов 10 мс. При превышении лимита на UDP сокете пакеты отбрасываются без блокировки потока, а на TCP сокете применяется мягкое ожидание (wait).
  • Пакетный сброс счетчиков трафика: Чтобы не вызывать глобальную синхронизацию телеметрии на каждый 100 байтный голосовой пакет, каждая сессия накапливает байты в локальном атомике unflushedBytes и сбрасывает их в глобальный счетчик DefaultTelemetry только при накоплении 64 КБ (или при закрытии/опросе сессии).

3. Zero Allocation аутентификация и защита от утечек памяти

  • Вычисление паролей на стеке: В методе computePassword (и в клиентском turnclient.GenerateLocal) буферы для хеша SHA1 (var sumBuf [20]byte) и Base64 (var b64Buf [28]byte) выделяются в виде фиксированных массивов на стеке, что минимизирует нагрузку на сборщик мусора (GC) при массовых переподключениях клиентов.
  • Жесткие лимиты таблиц (maxTrackedEntries = 50000): Таблицы отслеживания сессий (sessions) и дедупликации ошибок (failDedup) защищены атомарным счетчиком до 50 000 записей и очищаются в фоновой горутине не чаще одного раза в 30 секунд без остановки обработки запросов.

4. Связывание авторизации и аллокации

Поскольку библиотека pion/turn разделяет интерфейс проверки пароля (AuthHandler) и интерфейс выделения релейного порта (RelayAddressGenerator), slyber_turn связывает их через высокоскоростную кольцевую очередь authQueue на 64 элемента и атомарный указатель lastAuth в NodeTelemetry. Это позволяет точно привязать user_id и client_ip к конкретному выделенному UDP/TCP-порту ретрансляции в момент AllocatePacketConn / AllocateConn.