Современная
Телефонная Система
Полный справочник по построению VoIP-системы с нуля — от базовых протоколов до продакшен-архитектуры. Все функции, компоненты, связи и порядок реализации.
Основы телефонии
Фундаментальные протоколы и концепции, на которых строится любая современная телефонная система. Понимание этих основ — ключ ко всему остальному.
Что такое VoIP
VoIP (Voice over Internet Protocol) — это технология передачи голоса через IP-сети (интернет, локальная сеть) вместо традиционных телефонных линий. Голос оцифровывается, сжимается кодеком и отправляется в виде IP-пакетов — точно так же, как веб-страницы или электронная почта.
Выделенный канал на всё время разговора (circuit-switching). Один провод — один звонок. Дорогая инфраструктура, ограниченные возможности, фиксированная маршрутизация.
Голос передаётся пакетами через общую IP-сеть (packet-switching). Один канал — тысячи звонков. Дешёвая инфраструктура, гибкая маршрутизация, интеграция с ПО.
Стоимость: междугородние и международные звонки почти бесплатны. Гибкость: можно звонить с телефона, компьютера, браузера. Интеграция: CRM, аналитика, запись, IVR — всё через API. Масштабирование: добавление линий не требует нового оборудования.
В VoIP есть чёткое разделение сигнализации и медиа. Сигнализация (SIP) — это «кто кому звонит, принять/отклонить». Медиа (RTP) — это сам голос. Они передаются по разным каналам и могут идти разными путями.
Протокол SIP
SIP — протокол сигнализации. Он отвечает только за установку, изменение и завершение сеансов связи. SIP не передаёт голос — для этого используется RTP. SIP похож на HTTP: текстовый, читаемый, с заголовками и методами. Стандартные порты: 5060 (UDP/TCP) и 5061 (TLS).
Полный диалог SIP-вызова между двумя телефонами через АТС (PBX):
Телефон A PBX (АТС) Телефон B │ │ │ │ ──── INVITE ────> │ │ │ │ ──── INVITE ────> │ │ │ │ │ │ <── 100 Trying ─── │ │ <── 100 Trying ─── │ │ │ │ │ │ │ <── 180 Ringing ── │ │ <── 180 Ringing ── │ │ │ │ │ │ │ <──── 200 OK ───── │ │ <──── 200 OK ───── │ │ │ │ │ │ ────── ACK ──────> │ ────── ACK ──────> │ │ │ │ │ ═══════════════════ RTP (голос) ═══════════════════ │ │ │ │ │ ────── BYE ──────> │ ────── BYE ──────> │ │ │ │ │ │ <──── 200 OK ───── │ │ <──── 200 OK ───── │ │ │ │ │
Анатомия SIP INVITE-сообщения — ключевые заголовки:
From / To — кто звонит и кому
Via — маршрут прохождения запроса
Contact — прямой адрес для ответа
Call-ID — уникальный идентификатор звонка
CSeq — порядковый номер транзакции
SDP (Session Description Protocol) передаётся внутри SIP-сообщений и описывает медиа-параметры:
— IP-адрес и порт для RTP
— Список поддерживаемых кодеков
— Тип медиа (аудио, видео)
Обе стороны обмениваются SDP для согласования параметров (offer/answer).
Протокол RTP
RTP — протокол, который передаёт сам голос (и видео). В отличие от SIP, RTP работает поверх UDP и отправляет пакеты с аудио-данными 50 раз в секунду (каждые 20 мс). RTP-поток идёт отдельно от SIP — часто напрямую между устройствами, минуя сервер.
SIP-сигнализация RTP-медиа Телефон A ──SIP──> PBX ──SIP──> Телефон B Телефон A ═══RTP═══ Телефон B (порт 5060) (порт 5060) (порт 10000-20000) Сигнализация проходит через PBX, Медиа может идти напрямую а медиа — напрямую между телефонами (если PBX не нужен для записи/мониторинга)
Структура RTP-пакета:
Payload Type — какой кодек используется (определён в SDP при установке).
Sequence Number — порядковый номер для обнаружения потерь пакетов.
Timestamp — когда был записан аудио-семпл (для синхронизации).
SSRC — уникальный идентификатор потока (один на каждого участника).
Пакеты приходят с разной задержкой (jitter). Джиттер-буфер накапливает несколько пакетов и выдаёт их с равномерным интервалом. Слишком маленький буфер — потери и щелчки. Слишком большой — заметная задержка голоса. Типичный размер: 20–60 мс. Адаптивный буфер подстраивается автоматически.
Вместе с RTP работает протокол RTCP (RTP Control Protocol) — он передаёт статистику качества: потери пакетов, джиттер, задержку. RTCP используется для мониторинга качества связи, но не для самого голоса.
Кодеки
Кодек (Codec) — алгоритм кодирования и декодирования голоса. Определяет качество звука, требуемую полосу пропускания и вычислительную нагрузку. Выбор кодека — компромисс между качеством и трафиком.
| Кодек | Битрейт | Качество | Сжатие | Особенности |
|---|---|---|---|---|
| G.711 (a-law / u-law) | 64 kbps | Отличное | Нет | Стандарт для LAN. a-law — Европа, u-law — Северная Америка. Минимальная задержка. |
| G.722 | 64 kbps | HD Voice | Нет | Широкополосный (wideband, 16 kHz). Звучит заметно лучше G.711. Поддерживается большинством IP-телефонов. |
| G.729 | 8 kbps | Хорошее | 8:1 | Сильное сжатие — идеален для каналов с ограниченной полосой. Лицензируемый (есть бесплатный G.729a). |
| Opus | 6–510 kbps | Лучшее | Переменное | Современный, адаптивный. Используется в WebRTC. Open-source, без лицензий. Лучшее качество на любом битрейте. |
| iLBC | 13.3 / 15.2 kbps | Хорошее | ~4:1 | Устойчив к потерям пакетов (каждый фрейм независим). Хорош для нестабильных сетей. |
| GSM | 13 kbps | Среднее | ~5:1 | Унаследованный кодек мобильных сетей. Используется редко, но иногда встречается в legacy-системах. |
Для внутренней сети (LAN) используйте G.711 или G.722 — максимальное качество, полоса не проблема. Для WebRTC — Opus (он обязателен). Для каналов с ограниченной полосой — G.729. Opus является лучшим универсальным выбором, если оборудование его поддерживает.
DTMF
DTMF — тоновые сигналы, генерируемые при нажатии кнопок на телефоне. Каждая кнопка — это комбинация двух частот (отсюда «dual-tone»). DTMF используется для навигации по голосовым меню (IVR), ввода PIN-кодов, управления конференциями и других интерактивных сценариев.
Три способа передачи DTMF в VoIP:
Тоны передаются прямо в RTP-потоке как обычный звук. Проблема: сжимающие кодеки (G.729) искажают тоны, и они не распознаются. Работает только с G.711.
Ненадёжный
Цифры передаются как специальные RTP-пакеты с payload type 101. Не зависит от кодека — работает всегда. Самый распространённый метод.
Рекомендуемый
Цифра отправляется в SIP-сообщении типа INFO. Используется в некоторых специфичных сценариях (например, при взаимодействии с определёнными шлюзами).
Специальный
Если ваши голосовые меню не реагируют на нажатия — почти всегда проблема в режиме DTMF. Убедитесь, что и сервер, и телефон используют один и тот же метод (обычно RFC 2833).
NAT и телефония
NAT (Network Address Translation) заменяет приватные IP-адреса на публичный при выходе в интернет. Проблема: SIP-сообщения содержат приватный IP телефона («отправляй голос на 192.168.1.100:10000»), который недоступен извне. Результат — одностороннее аудио или полное его отсутствие.
Проблема NAT в VoIP: Телефон A NAT/Роутер SIP-сервер 192.168.1.100 203.0.113.50 198.51.100.10 │ │ │ │ SIP INVITE │ │ │ Contact: 192.168.1.100 │ │ │ SDP: audio 192.168.1.100:10000 │ │ ──────────────────────>│ │ │ │ SIP INVITE │ │ │ Contact: 192.168.1.100 <── Приватный IP! │ │ ──────────────────────>│ │ │ │ │ │ RTP → 192.168.1.100 │ │ │ НЕДОСТУПЕН! │ │ │ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ │
Решения проблемы NAT:
Session Traversal Utilities for NAT. Телефон спрашивает STUN-сервер: «какой у меня публичный IP и порт?» — и подставляет их в SDP. Работает с большинством NAT. Не работает с symmetric NAT.
Traversal Using Relays around NAT. Весь медиа-трафик проходит через TURN-сервер (relay). Работает всегда, но увеличивает задержку и нагрузку на сервер. Используется когда STUN не помогает.
Interactive Connectivity Establishment. Собирает все возможные «кандидаты» (прямой IP, STUN, TURN) и проверяет каждый путь. Выбирает лучший рабочий вариант. Стандарт для WebRTC.
Application Layer Gateway. Функция роутера, которая «умно» переписывает SIP-пакеты. На практике почти всегда ломает SIP. Рекомендация: отключить SIP ALG на роутере.
Если звонок устанавливается (вы слышите гудки), но после ответа нет голоса — в 90% случаев это проблема NAT. Проверьте: STUN настроен? SIP ALG отключён? Порты RTP (10000–20000) проброшены?
WebRTC
WebRTC (Web Real-Time Communication) — стандарт, встроенный во все современные браузеры (Chrome, Firefox, Safari, Edge), позволяющий передавать голос, видео и данные без плагинов. Для VoIP это означает: пользователь открывает веб-страницу и сразу может звонить.
Технологический стек WebRTC:
Opus — обязательный аудио-кодек (лучшее качество)
DTLS-SRTP — шифрование медиа (обязательно, нельзя отключить)
ICE + STUN + TURN — решение NAT-проблем
SDP — описание медиа-сессии (offer/answer)
Способ 1: SIP over WebSocket
Библиотека SIP.js или JsSIP в браузере отправляет SIP-сообщения через WebSocket (WSS). Сервер (FreeSWITCH) принимает их как обычные SIP.
Способ 2: Verto Protocol
Собственный протокол FreeSWITCH поверх WebSocket (JSON-RPC). Проще, чем SIP, но привязан к FreeSWITCH.
Браузер (WebRTC) FreeSWITCH SIP-телефон ┌─────────────┐ ┌──────────────┐ ┌───────────┐ │ SIP.js │ │ mod_verto │ │ │ │ (JavaScript│──WSS──│ mod_sofia │──SIP──│ IP Phone │ │ WebRTC API)│ │ │ │ │ └─────────────┘ └──────────────┘ └───────────┘ │ │ │ └─────── DTLS-SRTP (шифрованный RTP) ───────┘ или через сервер
WebRTC делает телефонию доступной любому пользователю с браузером — без установки ПО, без настроек сети, с обязательным шифрованием. Идеально для контакт-центров, CRM-интеграций и клиентских порталов.
Минимальный пример подключения SIP.js к FreeSWITCH
SIP-транки и SBC
Представьте: вы построили собственную телефонную систему (FreeSWITCH, Kamailio, веб-телефоны). Внутри вашей сети все сотрудники могут звонить друг другу. Но как позвонить на обычный мобильный или городской номер? Ваша система не подключена к телефонной сети (PSTN).
SIP-транк — это виртуальный «провод» между вашей телефонной системой и оператором связи, который имеет выход в обычную телефонную сеть (PSTN). Через этот «провод» передаётся SIP-сигнализация и голосовой трафик (RTP).
Ваша PBX — это офисная АТС.
SIP-транк — это телефонная линия от оператора, которая приходит в вашу АТС.
Без неё АТС работает только внутри офиса. С ней — звонки уходят наружу на любые номера мира.
Раньше это был физический кабель (ISDN PRI — 30 каналов в одном проводе). Сегодня SIP-транк — это IP-подключение через интернет. Никаких физических проводов — только настройка в конфигурации.
DID-номера — вам выделяют телефонные номера (например +49 30 1234567 для Берлина),
на которые люди могут звонить и попадать в вашу систему.
Исходящие звонки — ваши сотрудники могут звонить на любые номера мира,
оператор маршрутизирует вызов в PSTN.
Caller ID — при исходящем звонке у абонента отображается ваш номер (DID),
а не какой-то случайный.
Ёмкость — количество одновременных звонков. В отличие от ISDN (30 каналов),
SIP-транк может пропускать сотни звонков одновременно.
• IP-аутентификация — вы сообщаете IP-адрес вашего сервера, провайдер разрешает трафик только с этого IP. Без паролей.
• Credentials — логин + пароль, ваш сервер регистрируется на SIP-сервере провайдера (как обычный SIP-телефон).
| IP-аутентификация | Registration (credentials) | |
|---|---|---|
| Как работает | Провайдер добавляет ваш IP в белый список. Все SIP-пакеты с этого IP принимаются без пароля | Ваш сервер отправляет REGISTER на SIP-сервер провайдера с логином/паролем, как обычный SIP-телефон |
| Для кого | Enterprise с выделенным IP. Наш вариант | Небольшие компании, динамический IP, домашние PBX |
| Безопасность | Высокая — привязка к IP, нельзя подключиться с чужого адреса | Средняя — пароль может быть скомпрометирован |
| NAT | Не проблема — IP фиксированный | Может быть проблемой — REGISTER через NAT требует keep-alive |
| Failover | Можно указать несколько IP (primary + backup) | Ограничен — регистрация с одного хоста |
Как SIP-транк выглядит технически (SIP-сообщения)
Когда кто-то звонит на ваш DID +49 30 1234567, провайдер отправляет на ваш сервер SIP INVITE:
Ваш сервер отвечает 200 OK со своим SDP (свой IP для RTP, свои кодеки), и голосовой канал устанавливается.
Для исходящего звонка — наоборот: ваш сервер отправляет INVITE на SIP-сервер провайдера:
Сколько стоит SIP-транк (ценообразование)
| Компонент | Модель оплаты | Примерная цена |
|---|---|---|
| DID-номер | Ежемесячная аренда | $1–15/мес за номер (зависит от страны, US дешевле, ОАЭ дороже) |
| Входящие звонки | По минутам или бесплатно | $0–0.01/мин (многие провайдеры включают во входящие бесплатно) |
| Исходящие звонки | По минутам | $0.005–0.03/мин (локальные), $0.02–0.15/мин (международные) |
| Каналы (ёмкость) | Зависит от провайдера | Некоторые берут за concurrent channels ($2–5/канал), другие — без ограничений |
| Экстренные номера (E911) | Обязательный сбор (US) | $1–2/мес за номер |
При масштабе 43 страны — договаривайтесь с провайдерами напрямую о volume pricing. Крупные провайдеры (Telnyx, Bandwidth, BICS) дают скидки при объёме от 10,000+ минут/мес. Для некоторых стран выгоднее использовать локальных операторов (дешевле, лучше качество, регуляторные требования).
Когда у вас один провайдер — можно обойтись без Trunk SBC. FreeSWITCH напрямую общается с провайдером.
Когда у вас 43 страны и десятки провайдеров — начинается хаос, и Trunk SBC становится спасением:
Стандарт SIP (RFC 3261) — это 269 страниц. Каждый провайдер интерпретирует его по-своему:
• Один отправляет Caller ID в заголовке From
• Другой — в P-Asserted-Identity
• Третий — в Remote-Party-ID
• Кто-то требует + перед номером, кто-то без
• Форматы номеров: E.164, локальный, с кодом страны, без
Trunk SBC решает: нормализует все входящие SIP-сообщения к единому формату. FreeSWITCH всегда видит одинаковый SIP независимо от провайдера.
Звонок из Берлина в Мюнхен:
• Через Telnyx: $0.008/мин
• Через sipgate (локальный DE): $0.003/мин
• Через Twilio: $0.014/мин
Звонок из Берлина в Дубай:
• Через Telnyx: $0.09/мин
• Через du (локальный UAE): $0.04/мин
• Через Twilio: $0.12/мин
Trunk SBC решает: LCR (Least Cost Routing) — автоматически выбирает самый дешёвый маршрут для каждого звонка на основе префикса номера. Экономия — 30-60% на исходящих звонках.
У любого провайдера бывают аварии. Если FreeSWITCH напрямую подключён к одному транку — все исходящие звонки прекращаются.
Trunk SBC решает: автоматический failover. Если primary-транк не отвечает за 3 секунды → переключение на backup-транк. Пользователь даже не заметит.
Самая опасная угроза в телефонии. Злоумышленник получает доступ к вашей системе и начинает звонить на premium-номера (Куба, Сомали, спутниковые) по $3-5/минуту. За выходные — счёт на $50,000+.
Trunk SBC решает:
• Rate limiting — максимум N одновременных исходящих звонков
• Geo-blocking — блокировка звонков в «опасные» направления
• Spending alerts — уведомление если расходы за час превысили порог
• Anomaly detection — необычный паттерн (100 звонков на Кубу в 3 AM) → блокировка
Без Trunk SBC провайдер видит внутреннюю структуру вашей сети — IP-адреса FreeSWITCH, номера портов, имена серверов в заголовках Via и Contact.
Trunk SBC решает: переписывает все SIP-заголовки. Провайдер видит только IP/имя Trunk SBC. Ваша внутренняя архитектура скрыта.
Внутри вашей сети используется Opus (высокое качество, низкий трафик). Но многие PSTN-провайдеры принимают только G.711 (PCMU/PCMA) или G.729.
Trunk SBC + RTPEngine решают: транскодирование на границе. Внутри сети — Opus/G.722. На выходе к провайдеру — G.711. Прозрачно для пользователей.
ВХОДЯЩИЙ ЗВОНОК (клиент звонит на ваш DID) Мобильный SIP-провайдер Trunk Kamailio FreeSWITCH Access Оператор телефон ───▶ (Telnyx) ───▶ SBC ───▶ Core ───▶ (IVR, ───▶ SBC ───▶ (веб- PSTN→SIP нормализ. маршрут. очередь) SRTP↔RTP телефон) E.164 LB→FS bridge WebRTC ИСХОДЯЩИЙ ЗВОНОК (оператор звонит клиенту) Оператор Access Kamailio FreeSWITCH Trunk SIP-провайдер Мобильный (веб- ───▶ SBC ───▶ Core ───▶ (bridge) ───▶ SBC ───▶ (sipgate) ───▶ телефон телефон) WSS→SIP маршрут. dial out LCR SIP→PSTN SRTP→RTP LB→FS failover anti-fraud
Протоколы АТС — полная карта
В телефонии один звонок задействует 5-8 протоколов одновременно. Каждый отвечает за свою задачу: один устанавливает звонок, другой передаёт голос, третий шифрует, четвёртый пробивает NAT. Без понимания этих протоколов невозможно диагностировать проблемы.
Браузер SIP-телефон SBC Kamailio FreeSWITCH Go API (WebRTC) (аппаратный) (граница) (Core) (медиа) (логика) │ │ │ │ │ │ │◄──── WSS ────────┼──────────────►│ │ │ │ │ SIP over │ │ │ │ │ │ WebSocket │ │ │ │ │ │ (порт 443) │ │ │ │ │ │ │◄── SIP/TLS ──►│ │ │ │ │ │ (порт 5061) │ │ │ │ │ │ │◄── SIP/UDP ──►│ │ │ │ │ │ (порт 5060) │ │ │ │ │ │ │◄── SIP/UDP ──►│ │ │ │ │ │ (порт 5060) │ │ │ │ │ │ │ │ │◄──DTLS-SRTP──────┼──────────────►│ │ │ │ │ голос │ │RTPEngine │ │ │ │ шифрованный │◄── RTP ──────►│ SRTP↔RTP │ │ │ │ (порты │ голос │ транскод. │ │ │ │ 10000-20000) │ (порты │ │◄── RTP ──────►│ │ │ │ 10000-20000) │ │ (внутренний) │ │ │ │ │ │ │ │ │◄──ICE/STUN───────┼───────────────┼──►STUN-сервер │ │ │ │ NAT traversal │ │ │ │ │ │ │ │ │ │◄── ESL ──────►│ │ │ │ │ │ (порт 8021) │ │ │ │ │ │ TCP, текст │ │ │ │ │ │ │ │ │ │ │ │── HTTP ──────►│ │ │ │ │ │ JSON CDR │ │ │ │ │ │ (порт 8080) │ │ │ │ │ │ │ │◄──────────── HTTPS / WSS ────────────────────────────────────────────────────────►│ │ REST API, WebSocket (дашборд, управление) │ (порт 443) │
┌───────────────────────────────────────────────────────────────┐ │ УРОВЕНЬ 1: СИГНАЛИЗАЦИЯ (кто кому звонит, куда направить) │ │ │ │ SIP ─── установка/завершение звонка │ │ SDP ─── «мой IP для голоса: X, мои кодеки: Y» │ │ OСОБЕННОСТЬ: сигнализация и голос идут РАЗНЫМИ путями! │ └───────────────────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────────────┐ │ УРОВЕНЬ 2: МЕДИА (сам голос / видео) │ │ │ │ RTP ──── голосовые пакеты (каждые 20мс = 50 пакетов/сек) │ │ RTCP ─── «качество связи: потери 2%, jitter 15мс» │ │ SRTP ─── зашифрованный RTP (TLS для голоса) │ │ DTLS ─── обмен ключами для SRTP (WebRTC) │ └───────────────────────────────────────────────────────────────┘ ┌───────────────────────────────────────────────────────────────┐ │ УРОВЕНЬ 3: ТРАНСПОРТ И ВСПОМОГАТЕЛЬНЫЕ │ │ │ │ TLS ──── шифрование SIP (SIP/TLS = SIPS, порт 5061) │ │ WSS ──── SIP поверх WebSocket (для браузеров) │ │ STUN ─── «мой публичный IP = X» (NAT discovery) │ │ TURN ─── relay через сервер (когда STUN не помог) │ │ ICE ──── комбинация STUN+TURN для надёжного соединения │ │ DNS ──── SRV-записи для нахождения SIP-серверов │ └───────────────────────────────────────────────────────────────┘
Что делает: устанавливает, изменяет и завершает звонки. Это «управляющий» протокол — он НЕ передаёт голос, а только договаривается о звонке.
Аналогия: SIP — это когда вы набираете номер на телефоне и слышите гудки. Сам разговор — это уже RTP.
Телефон A FreeSWITCH Телефон B │── INVITE ──────────►│ │ │ «Хочу позвонить │── INVITE ────────────────►│ │ на 1002» │ «Звонок для тебя» │ │ │ │ │◄── 100 Trying ──────│ │ │ «Обрабатываю» │◄── 180 Ringing ──────────│ │◄── 180 Ringing ─────│ «Звоню» │ │ «Звонит» │ │ │ │◄── 200 OK ────────────────│ │◄── 200 OK ───────────│ «Поднял трубку» │ │ «Ответил!» │ │ │── ACK ───────────────►│── ACK ──────────────────►│ │ «Подтверждаю» │ │ │ │ │ │◄═══════════ RTP (голос) ═══════════════════════════►│ │ разговор идёт... │ │ │ │ │── BYE ───────────────►│── BYE ──────────────────►│ │ «Кладу трубку» │ │ │◄── 200 OK ───────────│◄── 200 OK ────────────────│
| SIP-метод | Что делает | Когда |
|---|---|---|
| INVITE | Начинает звонок или изменяет параметры (re-INVITE) | Набор номера, hold, перевод |
| ACK | Подтверждает 200 OK | Завершение handshake |
| BYE | Завершает звонок | Кто-то повесил трубку |
| REGISTER | «Я телефон 1001, я по адресу 192.168.1.50» | При включении телефона, каждые 60 сек |
| OPTIONS | «Ты живой?» — проверка доступности | Keep-alive, мониторинг |
| CANCEL | Отменяет INVITE до ответа | Звонящий повесил трубку пока звонит |
| REFER | Перевод звонка на другой номер | Attended/blind transfer |
| SUBSCRIBE/NOTIFY | Подписка на события (BLF, presence) | Лампочки занятости на телефоне |
| INFO | Передача данных в диалоге (DTMF) | SIP INFO метод для DTMF |
Транспорт SIP:
| Транспорт | Порт | Когда |
|---|---|---|
| UDP | 5060 | Внутренняя сеть (быстро, без overhead). Между Kamailio и FreeSWITCH |
| TCP | 5060 | Большие SIP-сообщения (>MTU), надёжная доставка |
| TLS | 5061 | Шифрование. SIP-телефоны через интернет к SBC |
| WSS | 443 | WebSocket Secure. Браузеры (WebRTC) к SBC |
Что делает: передаётся внутри SIP-сообщений (INVITE и 200 OK). Говорит: «Мой IP для голоса — такой-то, мои кодеки — такие-то, слушаю на порту таком-то».
Аналогия: SIP — это «Давай созвонимся». SDP — это «Вот мой номер и я говорю по-русски и по-английски».
Второй участник отвечает своим SDP в 200 OK — выбирает кодек из предложенных. После этого оба знают куда и как слать голос.
RTP (Real-time Transport Protocol)
Собственно голос. Аудио нарезается на пакеты по 20мс (50 пакетов/сек) и отправляется по UDP. Каждый пакет — ~160 байт (G.711).
Аналогия: если SIP — это «Давай поговорим», то RTP — это сам разговор по телефону.
Порты: 10000–20000 (UDP). Каждый звонок занимает 2 порта (чётный для RTP, нечётный для RTCP).
Ключевой момент:
SIP и RTP идут разными путями! SIP — через Kamailio (сигнализация). RTP — через RTPEngine или напрямую (медиа). Это создаёт проблемы с NAT и файрволами.
A ──SIP──► Kamailio ──SIP──► B │ │ A ──RTP──► RTPEngine ──RTP──► B (разные IP и порты!)
| Протокол | Что | Шифрование | Когда |
|---|---|---|---|
| RTP | Голос / видео | Нет | Внутренняя сеть (доверенная) |
| SRTP | Зашифрованный RTP | AES-128 | Между SBC и клиентами, между SBC и провайдерами |
| DTLS-SRTP | SRTP с обменом ключами по DTLS | AES-128 + DTLS handshake | WebRTC (обязательно) — браузер отказывается без шифрования |
| RTCP | Статистика качества (потери, jitter, RTT) | — | Параллельно с RTP, для мониторинга |
Когда в IVR нажимаете «1» — как эта цифра попадает на сервер? Три способа:
| Метод | Как | Плюсы | Минусы |
|---|---|---|---|
| RFC 2833 Рекомендуем | Специальные RTP-пакеты с типом 101 (telephone-event). Цифра передаётся внутри RTP-потока, но отдельно от голоса | Надёжный, не зависит от кодека, не искажается транскодированием | — |
| SIP INFO | SIP-сообщение INFO с телом Signal=1, Duration=250. Идёт через сигнализацию (SIP), не через медиа (RTP) |
Работает когда RTP недоступен | Задержки (SIP медленнее RTP), не все устройства поддерживают |
| In-band | Звук тонального сигнала (бип) кодируется прямо в голосовой поток | Работает с любым устройством | Искажается при сжатии (G.729). Ненадёжный |
WebRTC — это не один протокол, а стек из 5-6 протоколов:
┌──────────────────────────────────────────┐ │ БРАУЗЕР │ │ │ │ ┌──────────────────────────────┐ │ │ │ SIP.js / JsSIP │ │ │ │ SIP-клиент в JavaScript │ │ │ └──────────┬───────────────────┘ │ │ │ │ │ ┌──────────▼───────────────────┐ │ │ │ WebSocket (WSS) │ │ │ │ SIP-сообщения через WS │ │ │ │ порт 443, TLS обязателен │ │ │ └──────────┬───────────────────┘ │ │ │ │ │ ┌──────────▼───────────────────┐ │ │ │ ICE (Interactive Connectivity │ │ │ │ Establishment) │ │ │ │ Пробует все варианты связи: │ │ │ │ 1. Напрямую (host candidate) │ │ │ │ 2. Через NAT (STUN) │ │ │ │ 3. Через relay (TURN) │ │ │ └──────────┬───────────────────┘ │ │ │ │ │ ┌──────────▼───────────────────┐ │ │ │ DTLS (Datagram TLS) │ │ │ │ Обмен ключами шифрования │ │ │ │ (как TLS, но для UDP) │ │ │ └──────────┬───────────────────┘ │ │ │ │ │ ┌──────────▼───────────────────┐ │ │ │ SRTP │ │ │ │ Зашифрованный голос / видео │ │ │ │ AES-128 │ │ │ └──────────────────────────────┘ │ └──────────────────────────────────────────┘
Большинство устройств находятся за NAT (роутером). Их внутренний IP (192.168.x.x) не виден из интернета. Как доставить голос?
| Протокол | Что делает | Аналогия |
|---|---|---|
| STUN | Браузер спрашивает STUN-сервер: «Какой у меня публичный IP?» Сервер отвечает: «203.0.113.55:12345». Теперь браузер знает свой внешний адрес и сообщает его в SDP | Спросить друга: «Какой у меня номер на определителе?» |
| TURN | Если STUN не помог (симметричный NAT, корпоративный файрвол) — TURN-сервер становится relay: весь голос идёт через него. Работает всегда, но дороже (трафик через сервер) | Звонить через посредника, который передаёт слова |
| ICE | Комбинирует STUN и TURN. Пробует все варианты связи и выбирает лучший: напрямую → через STUN → через TURN | Попробовать позвонить напрямую, если не получилось — через оператора |
Кроме стандартных телефонных протоколов, в нашем стеке есть протоколы для связи между компонентами:
| Протокол | Между кем | Зачем | Порт |
|---|---|---|---|
| ESL (Event Socket) | Go API ↔ FreeSWITCH | Управление звонками из Go: originate, transfer, hold, события | 8021 / TCP |
| NATS | Go API ↔ другие сервисы | Шина событий: «звонок завершён», «агент освободился», «новый CDR» | 4222 / TCP |
| HEP (Homer Encapsulation) | Kamailio / FS → Homer | Копирование SIP-пакетов для мониторинга и troubleshooting. Homer показывает call-flow диаграммы | 9060 / UDP |
| DMQ (Kamailio Dialog) | Kamailio ↔ Kamailio | Синхронизация состояния между нодами кластера (регистрации, диалоги) | 5062 / UDP |
Полная сводная таблица: все протоколы в одном месте
| Протокол | Уровень | Транспорт | Порт(ы) | Зачем | Где у нас |
|---|---|---|---|---|---|
| SIP | Сигнализация | UDP/TCP | 5060 | Установка/завершение звонков | Везде |
| SIP/TLS | Сигнализация | TLS | 5061 | Шифрованный SIP | SBC ↔ телефоны |
| SIP/WSS | Сигнализация | WebSocket+TLS | 443 | SIP из браузера | SBC ↔ браузер |
| SDP | Сигнализация | внутри SIP | — | Описание медиа-параметров | В каждом INVITE/200 OK |
| RTP | Медиа | UDP | 10000-20000 | Голос (незашифрованный) | Внутренняя сеть |
| SRTP | Медиа | UDP | 10000-20000 | Голос (зашифрованный) | SBC ↔ внешний мир |
| DTLS-SRTP | Медиа | UDP | 10000-20000 | Голос WebRTC (обязательно шифрованный) | RTPEngine ↔ браузер |
| RTCP | Медиа | UDP | RTP+1 | Статистика качества | Параллельно RTP |
| DTMF (RFC 2833) | Медиа | внутри RTP | — | Нажатия клавиш в IVR | В RTP-потоке |
| STUN | NAT | UDP | 3478 | Определение публичного IP | Браузер → STUN-сервер |
| TURN | NAT | UDP/TCP | 3478 | Relay медиа через сервер | Браузер → TURN-сервер |
| ICE | NAT | — | — | Фреймворк: STUN + TURN + выбор лучшего пути | WebRTC |
| ESL | Внутренний | TCP | 8021 | Go управляет FreeSWITCH | Go API ↔ FS |
| NATS | Внутренний | TCP | 4222 | Шина событий между сервисами | Go API ↔ сервисы |
| HEP | Мониторинг | UDP | 9060 | Копирование SIP для Homer | Kamailio/FS → Homer |
| DMQ | Кластер | UDP | 5062 | Синхронизация Kamailio-нод | Kamailio ↔ Kamailio |
| HTTP/REST | API | TCP+TLS | 443/8080 | REST API, CDR, конфигурация | React ↔ Go API ↔ FS |
| WebSocket | API | TCP+TLS | 443 | Реалтайм дашборды, уведомления | React ↔ Go API |
Когда оператор в браузере звонит клиенту на мобильный через PSTN:
Браузер оператора Наш стек Мобильный клиента 1. WSS SIP INVITE ──────► Access SBC (Kamailio) 2. SIP/UDP ──► Kamailio Core 3. SIP/UDP ──► FreeSWITCH 4. ESL Go получает event ◄── FreeSWITCH 5. SIP/UDP FS dial out ──► Kamailio Core 6. SIP/UDP ──► Trunk SBC 7. SIP/TLS ──► SIP-провайдер ──► PSTN ──► Мобильный Медиа (голос): 8. DTLS-SRTP браузер ──► RTPEngine (Access) 9. RTP RTPEngine ──► FreeSWITCH (транскод Opus→G.711) 10. RTP FreeSWITCH ──► RTPEngine (Trunk) 11. RTP RTPEngine ──► SIP-провайдер ──► PSTN ──► Мобильный Параллельно: 12. HEP SIP-пакеты ──► Homer (мониторинг) 13. NATS событие ──► Dashboard (React) 14. RTCP статистика ──► Prometheus → Grafana
14 протоколов задействованы в одном звонке. Вот почему телефония сложнее веб-разработки.
Каталог функций
Полный каталог всех функций современной телефонной системы — 134 функции в 16 категориях. Каждая функция с описанием и техническим объяснением работы.
Управление вызовами (Call Control) 9
| Функция | Описание | Как это работает |
|---|---|---|
| Входящий вызов | Приём звонка извне | SIP INVITE → обработка диалпланом |
| Исходящий вызов | Звонок наружу | Выбор транка по LCR, SIP INVITE наружу |
| Внутренний вызов | Звонок между сотрудниками | Маршрутизация через location table |
| Удержание (Hold) | Пауза, абонент слышит музыку | re-INVITE с a=sendonly, MOH stream |
| Возобновление (Resume) | Снять с удержания | re-INVITE с a=sendrecv |
| Завершение (Hangup) | Положить трубку | SIP BYE → разрыв RTP |
| Повторный набор (Redial) | На последний номер | Клиентская функция, хранит историю |
| Быстрый набор (Speed Dial) | Звонок по короткому коду | Диалплан: *1 → translate → INVITE |
| Горячая линия (Hotline) | Снял трубку = автозвонок | Автоматический INVITE при off-hook |
Переводы и переадресации (Transfer & Forwarding) 9
| Функция | Описание | Как это работает |
|---|---|---|
| Слепой перевод (Blind Transfer) | Перевёл — отключился | SIP REFER → новый INVITE |
| Перевод с консультацией (Attended Transfer) | Поговорил → перевёл | Два вызова + REFER с Replaces |
| Переадресация всегда (Forward Always) | Все звонки на другой номер | Диалплан проверяет настройку до звонка |
| По занятости (Forward Busy) | Если занят — переведи | Проверка статуса 486 Busy → forward |
| По неответу (Forward No Answer) | Не ответил за N сек | Таймер → при timeout → forward |
| По недоступности (Forward Unreachable) | Телефон выключен | Нет регистрации → forward |
| Follow Me | Звонит по очереди на разные устройства | Последовательный набор с таймаутами |
| Simultaneous Ring | Звонит везде сразу | Параллельный fork INVITE на все контакты |
| DISA | Звонок снаружи → внутренний гудок | IVR с аутентификацией → internal dialplan |
Многосторонние вызовы (Multi-party) 7
| Функция | Описание | Как это работает |
|---|---|---|
| Трёхсторонний вызов | Добавить третьего | Conference bridge с 3 участниками |
| Конференц-комната | Постоянная комната с PIN | mod_conference, номер + PIN вход |
| Динамическая конференция | Создаётся через API | ESL conference create |
| Модератор конференции | mute/unmute/kick | Управление через ESL или DTMF-коды |
| Видеоконференция | Видео + аудио + экран | mod_conference с видео-кодеками |
| Intercom / Пейджинг | Сообщение на группу | Multicast RTP или sequential paging |
| Двусторонний Intercom | Автоответ на громкую | Alert-Info: Auto Answer header |
IVR — голосовое меню 8
| Функция | Описание | Как это работает |
|---|---|---|
| Одноуровневое IVR | Простое меню | Lua/JS скрипт + playAndGetDigits |
| Многоуровневое IVR | Вложенные меню | Дерево скриптов с переходами |
| DTMF-ввод | Распознавание нажатий | RFC 2833 events → digit collection |
| ASR (распознавание речи) | Голосовой ввод | mod_unimrcp → внешний ASR (Vosk/Whisper) |
| TTS (синтез речи) | Текст → голос | mod_tts → внешний TTS (Piper/Google) |
| Набор по имени (Directory) | Поиск по фамилии | DTMF → T9 mapping → search → transfer |
| Динамический IVR | Меню из БД/CRM | Lua/JS HTTP-запрос → построение меню |
| Callback из IVR | «Нажмите 1, перезвоним» | Сохранение номера → originate позже |
Очереди (ACD) 13
| Функция | Описание | Как это работает |
|---|---|---|
| Ring All | Звонят все операторы | Параллельный bridge ко всем агентам |
| Round Robin | По очереди | Циклический выбор следующего агента |
| Longest Idle | Кто дольше свободен | Трекинг времени последнего вызова |
| Fewest Calls | У кого меньше звонков | Счётчик вызовов за период |
| Skill-based Routing | По навыкам | Сопоставление skills вызова и агента |
| Priority Queue | VIP в начало | Приоритет из CRM/CallerID → сортировка |
| Позиция в очереди | «Вы 3-й» | Счётчик + TTS/запись |
| Время ожидания | «Ожидайте 2 мин» | Среднее время = total_wait / answered |
| Периодические объявления | Каждые 30 сек | Таймер → playback в очереди |
| MOH (музыка ожидания) | Музыка/реклама | mod_local_stream → аудиопоток |
| Overflow | Ждёт > 5 мин → другая группа | Таймер/порог → transfer |
| Callback | Перезвоним не теряя место | Сохранить позицию → originate при освобождении |
| Wrap-up Time | Пауза после звонка | Таймер Not Ready → авто Ready |
Голосовая почта (Voicemail) 9
| Функция | Описание | Как это работает |
|---|---|---|
| Личный ящик | У каждого свой | mod_voicemail, box per extension |
| Приветствие | Личное голосовое | Запись через *97, файл в storage |
| Разные приветствия | По ситуации | busy/unavail/temp greetings |
| Voicemail-to-Email | Сообщение на почту | Email с MP3-вложением |
| Визуальная голосовая почта | Список в UI | API → список сообщений → плеер |
| Общий ящик | Групповой для отдела | Shared mailbox, несколько подписчиков |
| PIN-доступ | Прослушивание по телефону | DTMF PIN → menu → playback |
| Транскрипция | Голос → текст | ASR → текст в email/UI |
| Автоудаление | Удаление через N дней | Cron job / scheduled cleanup |
Маршрутизация (Routing & Dialplan) 12
| Функция | Описание | Как это работает |
|---|---|---|
| Внутренние номера (Extensions) | 100-199 | Dialplan pattern matching |
| DID (прямые номера) | Внешний → внутренний | DID → extension mapping в БД |
| DNIS | По какому номеру позвонили | SIP To header → routing decision |
| CallerID / АОН | Определение номера | SIP From header → display |
| CallerID подмена | Исходящий показывает номер компании | effective_caller_id_number |
| Маршрутизация по времени | День/ночь | Time conditions в dialplan |
| По CallerID | Регион/VIP | Prefix matching → route |
| По географии | Международный → дешёвый транк | GeoIP / prefix table |
| LCR (Least Cost Routing) | Самый дешёвый маршрут | Rate table → sort by cost → try |
| Failover | Основной упал → запасной | Sequential try → next on failure |
| Чёрный список (Blacklist) | Блокировка номеров | Lookup в БД/Redis → reject |
| Белый список (Whitelist) | Только разрешённые | Lookup → allow or reject |
Супервизор и контроль (Supervisor) 7
| Функция | Описание | Как это работает |
|---|---|---|
| Прослушивание (Spy) | Слышит разговор | eavesdrop UUID, mute flag |
| Подсказка (Whisper) | Говорит оператору | eavesdrop с whisper flag |
| Вмешательство (Barge-In) | Входит в разговор | three_way_bridge |
| Перехват (Call Pickup) | Ответить за коллегу | intercept UUID |
| Групповой перехват | Внутри отдела | Pickup group → intercept first ringing |
| Принудительная запись | Включить запись | uuid_record start |
| Оценка звонка (Call Scoring) | Оценка оператора | Метаданные в CDR → отчёты |
Запись и аналитика (Recording & Analytics) 10
| Функция | Описание | Как это работает |
|---|---|---|
| Запись всех вызовов | Автоматическая | uuid_record on CHANNEL_ANSWER |
| Выборочная запись | Определённые направления | Условия в dialplan → record |
| Запись по требованию | Кнопка | API → uuid_record start/stop |
| CDR (журнал вызовов) | Полный лог | mod_cdr_csv / pg_cdr → PostgreSQL |
| Живые дашборды | Реалтайм статистика | ESL events → NATS → WebSocket → React |
| Исторические отчёты | За период | SQL-агрегация CDR → графики |
| SLA-мониторинг | % отвеченных за N сек | CDR analysis: answer_time < threshold |
| Речевая аналитика | AI-анализ | Whisper STT → LLM → тональность, темы |
| Скрининг (Screen Recording) | Запись экрана | Отдельный агент на рабочей станции |
| Heatmap нагрузки | Пиковые часы | CDR group by hour → visualization |
Статусы и Presence 5
| Функция | Описание | Как это работает |
|---|---|---|
| BLF (Busy Lamp Field) | Лампочки на телефоне | SIP SUBSCRIBE/NOTIFY dialog |
| Presence | Статус пользователя | SIP PUBLISH → presence server |
| DND (Do Not Disturb) | Не беспокоить | Флаг в user settings → reject calls |
| Автостатус | Календарь → DND | Calendar API polling → update status |
| Статус оператора | Ready/Not Ready | Agent state machine в mod_callcenter |
Автообзвон (Dialer) 8
| Функция | Описание | Как это работает |
|---|---|---|
| Preview Dialer | Оператор видит карточку | Показать данные → ждать клик → originate |
| Progressive Dialer | Автоматический набор | Agent free → originate next number |
| Predictive Dialer | Предиктивный | Алгоритм: abandon rate + avg talk time → pace |
| Power Dialer | N номеров сразу | Multi-originate → first answer → bridge |
| Автоинформатор | Робот с сообщением | Originate → playback → hangup |
| IVR-обзвон | Робот с меню | Originate → IVR script → collect response |
| SMS-рассылка | Массовые SMS | HTTP API к SMS-шлюзу |
| Кампании | Управление списками | CRUD кампаний, schedule, retry logic |
Интеграции (Integrations) 9
| Функция | Описание | Как это работает |
|---|---|---|
| CRM-интеграция | Карточка клиента | Webhook on call → CRM API → screen pop |
| Click-to-Call | Клик по номеру | POST /calls → ESL originate |
| Webhook / API | Событие → HTTP | Event → NATS → webhook dispatcher |
| LDAP/AD | Импорт сотрудников | LDAP sync → create extensions |
| Календарь | Google/Outlook | Calendar API → time conditions, auto-DND |
| Helpdesk | Автосоздание тикета | Webhook → Zendesk/Jira API |
| Мессенджеры | Omnichannel | Telegram/WhatsApp bot → unified queue |
| AI-ассистент | Бот на типовые вопросы | STT → LLM → TTS → response or transfer |
| Скрипты разговоров | Сценарий оператору | Web UI с динамическим скриптом по этапам |
WebRTC и мобильность 6
| Функция | Описание | Как это работает |
|---|---|---|
| Web-телефон | Звонки из браузера | SIP.js + WebSocket + WebRTC |
| Мобильный клиент | iOS/Android | SIP-клиент + push notifications |
| Softphone | Программный телефон на ПК | SIP UA (общие открытые варианты) |
| Один номер везде | Единый на всех устройствах | Multiple registrations per AOR |
| Переключение устройства | Без прерывания | REFER + Replaces → move call |
| Push-уведомления | Входящий → push | SIP → push service → wake app → REGISTER → INVITE |
Безопасность (Security) 9
| Функция | Описание | Как это работает |
|---|---|---|
| TLS | Шифрование сигнализации | mod_tls / Kamailio tls module |
| SRTP | Шифрование голоса | Crypto suite negotiation в SDP |
| Fail2ban / Антифрод | Блокировка подбора | Kamailio pike + fail2ban integration |
| ACL | Разрешённые IP | Kamailio permissions module |
| Fraud Detection | Подозрительные звонки | Rules: international burst, off-hours, new dest |
| Лимиты | Макс. одновременных / расход | dialog module counter + budget check |
| Аудит-лог | Кто что менял | API middleware → log to DB |
| 2FA | Двухфакторная в панель | TOTP / email OTP для web login |
| SRTP обязательный | Запрет незашифрованных | crypto: mandatory в SIP profile |
Отказоустойчивость (High Availability) 6
| Функция | Описание | Как это работает |
|---|---|---|
| HA (Active/Standby) | Основной + запасной | Keepalived + shared IP (VIP) |
| Кластеризация | Несколько серверов как один | FreeSWITCH mod_sofia profiles per node |
| Балансировка нагрузки | Распределение | Kamailio dispatcher module |
| Geo-redundancy | Разные дата-центры | DMQ sync + DNS SRV records |
| Автомасштабирование | Облачное | Kubernetes + metrics-based HPA |
| Резервное копирование | Бэкапы | pg_dump + rsync recordings + cron |
Администрирование (Administration) 7
| Функция | Описание | Как это работает |
|---|---|---|
| Web-панель | GUI настройки | React SPA + REST API |
| Мультитенантность | Несколько компаний | tenant_id в каждой таблице, domain routing |
| Роли и права | admin/supervisor/user | RBAC в API middleware |
| Провижининг | Автонастройка телефонов | HTTP provisioning server + MAC-based config |
| Шаблоны | Типовые профили | Template → apply to user → create extension+settings |
| Bulk-операции | Массовые действия | CSV import → batch create |
| REST API | Полное управление | OpenAPI spec, JWT auth, rate limiting |
Архитектура системы
Общая архитектура, слои системы, потоки данных и разделение сигнализации и медиа.
Общая схема
Архитектура для крупной компании с присутствием в 43 странах. Гео-распределённая система с полной отказоустойчивостью, SBC на границе каждого региона, локальный breakout трафика (вызовы остаются в регионе), централизованное управление и мониторинг. Каждый регион автономен — при потере связи между регионами локальные вызовы продолжают работать.
═══════════════════════════════════════════════════════════════════════════════════════ ГЛОБАЛЬНАЯ АРХИТЕКТУРА — 43 СТРАНЫ, 4 РЕГИОНА ═══════════════════════════════════════════════════════════════════════════════════════ ┌─────────────────────────────┐ │ КЛИЕНТЫ (43 страны) │ │ WebPhone IP-телефоны │ │ Мобильные Софтфоны │ │ Web UI Contact Center │ └──────────────┬──────────────┘ │ GeoDNS / Anycast (маршрутизация к ближайшему региону) │ ┌──────────────────────────────┬┴┬──────────────────────────────┐ ▼ ▼ ▼ ┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐ │ РЕГИОН: EUROPE │ │ РЕГИОН: AMERICAS │ │ РЕГИОН: ASIA-PAC │ + MIDDLE EAST │ Frankfurt (Primary) │ │ US-East (Primary) │ │ Singapore (Primary) │ Dubai │ Amsterdam (Standby) │ │ US-West (Standby) │ │ Tokyo (Standby) │ │ ~20 стран │ │ ~10 стран │ │ ~8 стран │ ~5 стран └──────────┬───────────┘ └──────────┬───────────┘ └──────────┬───────────┘ │ │ │ └───────────────────────────┼───────────────────────────┘ │ Межрегиональная связь (WireGuard VPN mesh / выделенные каналы) │ ┌────────┴────────┐ ▼ ▼ ┌──────────────────┐ ┌──────────────────┐ │ CENTRAL MGMT │ │ GLOBAL MONITOR │ │ Go API Master │ │ Homer + Grafana │ │ PostgreSQL (HA) │ │ Prometheus │ │ Admin Panel │ │ Loki (logs) │ └──────────────────┘ └──────────────────┘
Каждый регион — это полностью автономный кластер со всеми компонентами. Ниже — детальная схема одного региона (Europe / Frankfurt):
═══════════════════════════════════════════════════════════════════════════════════════ РЕГИОН EUROPE (Frankfurt) — детальная архитектура ═══════════════════════════════════════════════════════════════════════════════════════ КЛИЕНТЫ РЕГИОНА ┌───────────┬────────────┬──────────────┬───────────────┐ │ Web UI │ WebPhone │ IP-телефоны │ Мобильные │ │ (React) │ (SIP.js) │ Yealink,Snom │ iOS/Android │ └─────┬─────┴─────┬──────┴───────┬──────┴──────┬────────┘ │HTTPS │WSS │SIP/TLS │SIP/TLS ▼ ▼ ▼ ▼ ┌─────────────────────────────────────────────────────────┐ │ СЛОЙ 1: EDGE / LOAD BALANCER │ │ ┌─────────────────┐ ┌─────────────────┐ │ │ │ HAProxy / Nginx │ │ HAProxy / Nginx │ (Active/Active)│ │ │ HTTPS, WSS │ │ HTTPS, WSS │ │ │ │ TLS termination│ │ TLS termination│ │ │ └────────┬────────┘ └────────┬────────┘ │ │ └──────────┬─────────┘ │ └──────────────────────┼──────────────────────────────────┘ │ ┌──────────────────────┼──────────────────────────────────┐ │ СЛОЙ 2: SBC (Session Border Controller) │ │ │ │ │ ┌───────────────────▼───────────────────┐ │ │ │ Kamailio SBC Cluster (Active/Active) │ │ │ │ │ │ │ │ ┌────────────┐ ┌────────────┐ │ │ │ │ │ KAM-SBC-1 │ │ KAM-SBC-2 │ │ Keepalived │ │ │ │ Внешний │ │ Внешний │ │ Floating │ │ │ │ интерфейс │ │ интерфейс │ │ VIP │ │ │ └──────┬─────┘ └──────┬─────┘ │ │ │ │ └────────┬────────┘ │ │ │ │ │ │ │ │ │ Функции SBC: │ │ │ │ • Topology hiding (скрытие топологии) │ │ │ │ • SIP normalization (нормализация) │ │ │ │ • Anti-fraud / rate limiting │ │ │ │ • TLS termination для SIP │ │ │ │ • Транк-аутентификация (IP ACL) │ │ │ │ • DDoS protection (pike + htable) │ │ │ │ • Протокол-конвертация (WSS↔UDP) │ │ │ └────────────────────┬──────────────────-┘ │ │ ┌────────────────────▼───────────────────┐ │ │ │ RTPEngine Cluster │ │ │ │ ┌────────────┐ ┌────────────┐ │ │ │ │ │ RTPE-1 │ │ RTPE-2 │ │ │ │ │ │ NAT trav. │ │ NAT trav. │ │ │ │ │ │ SRTP↔RTP │ │ SRTP↔RTP │ │ │ │ │ │ WebRTC↔SIP │ │ WebRTC↔SIP │ │ │ │ │ └────────────┘ └────────────┘ │ │ │ └────────────────────────────────────────┘ │ └──────────────────────┬──────────────────────────────────┘ │ SIP (внутренний, доверенный) ┌──────────────────────┼──────────────────────────────────┐ │ СЛОЙ 3: CORE SIP PROXY │ │ │ │ │ ┌───────────────────▼───────────────────┐ │ │ │ Kamailio Core Cluster (Active/Active) │ │ │ │ ┌────────────┐ ┌────────────┐ │ │ │ │ │ KAM-CORE-1 │ │ KAM-CORE-2 │ │ DMQ sync │ │ │ │ Регистрация│ │ Регистрация│ │ между │ │ │ │ Маршрутиз. │ │ Маршрутиз. │ │ нодами │ │ │ │ LB → FS │ │ LB → FS │ │ │ │ │ └──────┬─────┘ └──────┬─────┘ │ │ │ │ └────────┬────────┘ │ │ │ └──────────────────┼─────────────────────┘ │ │ │ │ │ Функции Core: │ │ │ • User registration (usrloc) │ │ • Internal routing (диалплан, DID) │ │ • Load balancing → FreeSWITCH (dispatcher) │ │ • Failover между FS-нодами │ │ • Dialog tracking │ └─────────────────────┼────────────────────────────────────┘ │ ┌─────────────────────┼────────────────────────────────────┐ │ СЛОЙ 4: MEDIA SERVERS (FreeSWITCH Cluster) │ │ │ │ │ ┌──────────────────▼──────────────────────────────┐ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ FS-1 │ │ FS-2 │ │ FS-N │ │ │ │ │ │ IVR │ │ IVR │ │ IVR │ │ │ │ │ │ Queues │ │ Queues │ │ Queues │ │ │ │ │ │ Conf │ │ Conf │ │ Conf │ │ │ │ │ │ Record │ │ Record │ │ Record │ │ │ │ │ │ Bridge │ │ Bridge │ │ Bridge │ │ │ │ │ └──────────┘ └──────────┘ └──────────┘ │ │ │ │ (горизонтальное масштабирование по нагрузке) │ │ │ └─────────────────────────────────────────────────┘ │ └──────────┬──────────────┬──────────────┬─────────────────┘ │ESL │CDR │recordings ▼ ▼ ▼ ┌──────────────────────────────────────────────────────────┐ │ СЛОЙ 5: APPLICATION & API │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────┐ │ │ │ Go API-1 │ │ Go API-2 │ │ Go API-N │ │ │ │ REST/WS │ │ REST/WS │ │ REST/WS │ │ │ │ ESL client │ │ ESL client │ │ ESL client │ │ │ │ CDR writer │ │ Dialer │ │ Webhooks │ │ │ └─────────────┘ └─────────────┘ └──────────────┘ │ └──────────┬──────────────┬──────────────┬─────────────────┘ │ │ │ ┌──────────┼──────────────┼──────────────┼─────────────────┐ │ СЛОЙ 6: DATA & EVENTS │ │ │ │ │ │ │ │ │ ┌───────▼─────┐ ┌─────▼─────┐ ┌─────▼──────┐ │ │ │PostgreSQL │ │ Redis │ │ NATS │ │ │ │Primary + │ │ Sentinel │ │ Cluster │ │ │ │Replica (HA) │ │ Cluster │ │ (JetStream)│ │ │ └─────────────┘ └───────────┘ └────────────┘ │ │ │ │ ┌─────────────┐ ┌───────────────┐ │ │ │ MinIO (S3) │ │ Homer + Grafana│ │ │ │ Recordings │ │ SIP capture │ │ │ │ Voicemail │ │ Prometheus │ │ │ │ IVR audio │ │ Loki (logs) │ │ │ └─────────────┘ └───────────────┘ │ └──────────────────────────────────────────────────────────┘ │ │ SIP (от Core к транкам) │ ┌──────────┼───────────────────────────────────────────────┐ │ СЛОЙ 7: TRUNK SBC (Session Border Controller — PSTN) │ │ │ │ │ ┌───────▼──────────────────────────────────────┐ │ │ │ Kamailio Trunk SBC (Active/Active) │ │ │ │ │ │ │ │ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ KAM-TRUNK-1 │ │ KAM-TRUNK-2 │ │Keepalived│ │ │ │ SIP-facing │ │ SIP-facing │ │Floating │ │ │ │ к провайдерам│ │ к провайдерам│ │VIP │ │ │ └──────┬──────┘ └──────┬──────┘ │ │ │ │ └────────┬────────┘ │ │ │ │ │ │ │ │ │ Функции Trunk SBC: │ │ │ │ • SIP-нормализация (разные провайдеры = разный SIP) │ │ │ • Транк-аутентификация (IP ACL + credentials) │ │ │ • E.164 нормализация номеров │ │ │ • LCR (Least Cost Routing) между транками │ │ │ • Failover: primary → backup провайдер │ │ │ • Topology hiding от провайдеров │ │ │ • Anti-fraud / toll fraud protection │ │ │ • Кодек-переговоры (Offer/Answer) с провайдерами │ │ │ • Caller ID manipulation / CNAM │ │ └────────────────────┬─────────────────────────┘ │ │ │ │ │ ┌────────────────────▼───────────────────┐ │ │ │ RTPEngine Trunk (опционально) │ │ │ │ ┌────────────┐ ┌────────────┐ │ │ │ │ │ RTPE-T1 │ │ RTPE-T2 │ │ │ │ │ │ Кодек │ │ Кодек │ │ │ │ │ │ транскод. │ │ транскод. │ │ │ │ │ │ G.711↔G.729│ │ G.711↔G.729│ │ │ │ │ └────────────┘ └────────────┘ │ │ │ │ (для провайдеров с жёсткими кодек- │ │ │ │ требованиями или RTP-пиннингом) │ │ │ └────────────────────────────────────────┘ │ └──────────────────────┬────────────────────────────────────┘ │ ┌──────────────────────┼───────────────────────────────────┐ │ СЛОЙ 8: PSTN / ВНЕШНИЙ МИР │ │ │ │ │ ┌───────────────────▼───────────────────────────────┐ │ │ │ SIP-транки (локальные для региона) │ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │ │ │ │ │ Telnyx │ │ Twilio │ │ Локальный │ │ │ │ │ │ (backup) │ │ (primary)│ │ оператор │ │ │ │ │ └──────────┘ └──────────┘ │ (DE, FR, UK) │ │ │ │ │ └──────────────┘ │ │ │ │ Маршрутизация: LCR + failover + geo-local │ │ │ └───────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────┘
1. Тройной Kamailio — Access SBC (внешний, клиенты/интернет), Core (внутренний, маршрутизация), Trunk SBC (внешний, PSTN-провайдеры). Три зоны доверия: Access SBC принимает удар из интернета, Trunk SBC нормализует трафик от провайдеров, Core работает только с доверенным трафиком от обоих SBC.
2. Двусторонняя граница — SBC и на северной стороне (клиенты), и на южной (PSTN). Внутренняя сеть полностью изолирована от внешнего мира с обоих направлений.
3. Гео-распределение — каждый регион автономен. Звонки внутри региона не зависят от других регионов.
4. Локальный breakout — звонок из Германии на немецкий номер идёт через локальный транк в Frankfurt, не через US.
5. Все компоненты в HA — нет единой точки отказа. Каждый компонент задублирован минимум 2x.
При работе в 43 странах с десятками SIP-провайдеров SBC обязателен. Это не дублирование Kamailio Core — это принципиально другая роль: граница доверия между внешним миром и вашей внутренней сетью.
| Компонент | Зона | Задачи | Почему отдельный |
|---|---|---|---|
| Kamailio Access SBC | DMZ North / клиенты | Topology hiding, anti-DDoS, SIP normalization, TLS termination, протокол-конвертация (WSS↔UDP), NAT traversal | Принимает весь недоверенный трафик от клиентов из интернета. Северная граница |
| RTPEngine Access | DMZ North / клиенты | NAT traversal, SRTP↔RTP, WebRTC bridge, медиа-прокси для клиентского трафика | Медиа-трафик из интернета не попадает напрямую к FreeSWITCH |
| Kamailio Core | Внутренняя сеть | Регистрация пользователей, внутренняя маршрутизация, LB между FreeSWITCH, dialog tracking | Работает только с доверенным трафиком от обоих SBC. Оптимизирован под скорость, не под защиту |
| FreeSWITCH | Внутренняя сеть | IVR, очереди, конференции, запись, bridge, диалплан | Никогда не виден из интернета. Занимается только медиа-логикой |
| Kamailio Trunk SBC | DMZ South / PSTN | SIP-нормализация для разных провайдеров, транк-аутентификация (IP ACL + credentials), E.164 нормализация, LCR, Caller ID, anti-fraud | Изолирует внутреннюю сеть от PSTN-провайдеров. Южная граница. Каждый провайдер «видит» только Trunk SBC |
| RTPEngine Trunk | DMZ South / PSTN | Транскодирование кодеков (G.711↔G.729), RTP-пиннинг, медиа-прокси для PSTN-трафика | Опциональный. Нужен если провайдеры требуют специфические кодеки или RTP-параметры |
На нашем масштабе оба SBC (Access + Trunk) необходимы:
Access SBC (северная граница — клиенты):
— Защита от атак — публичные SIP-адреса постоянно сканируются ботами, без SBC внутренняя сеть уязвима
— WebRTC-мост — конвертация WSS/DTLS-SRTP от браузеров в стандартный SIP/RTP
— NAT traversal — клиенты за NAT, SBC решает проблемы доставки медиа
Trunk SBC (южная граница — PSTN-провайдеры):
— Десятки SIP-транков в разных странах с разными особенностями SIP-стека (каждый оператор кодирует SIP по-своему)
— Регуляторные требования — в ряде стран (ОАЭ, Сингапур, Германия) оператор требует сертифицированную точку подключения
— Нормализация SIP — разные провайдеры отправляют CallerID, кодеки и заголовки в разных форматах, Trunk SBC приводит всё к единому виду
— LCR + Failover — маршрутизация по стоимости между провайдерами, автоматическое переключение при отказе транка
— Anti-fraud — защита от toll fraud (мошеннические звонки на premium-номера) на уровне транков
Compliance (оба SBC):
— GDPR (Europe), CCPA (US), локальные законы о записи звонков требуют контроля на обеих границах
Межрегиональная маршрутизация и GeoDNS
┌──────────────────┐
│ GeoDNS │
│ sip.company.com │
└────────┬─────────┘
│
┌──────────────────┼──────────────────┐
▼ ▼ ▼
┌────────────────┐ ┌────────────────┐ ┌────────────────┐
│ EU SBC Cluster │ │ US SBC Cluster │ │ APAC SBC │
│ Frankfurt │ │ Virginia │ │ Singapore │
│ 2x Kamailio │ │ 2x Kamailio │ │ 2x Kamailio │
│ 2x RTPEngine │ │ 2x RTPEngine │ │ 2x RTPEngine │
└───────┬────────┘ └───────┬────────┘ └───────┬────────┘
│ │ │
└─────────┬─────────┴─────────┬─────────┘
│ │
▼ ▼
WireGuard VPN Mesh SIP trunk routing:
(между регионами) EU user → EU trunk
US user → US trunk
Cross-region → nearest
Правила межрегиональной маршрутизации:
┌─────────────────────────────────────────────────────────┐
│ 1. Звонок внутри региона → локальный FreeSWITCH + транк│
│ 2. Звонок между регионами → VPN → SBC другого региона │
│ 3. PSTN → ближайший к номеру регион (local breakout) │
│ 4. Failover: EU primary down → EU standby │
│ EU standby down → US cluster (geo-failover) │
└─────────────────────────────────────────────────────────┘
| Регион | Primary ДЦ | Standby ДЦ | Страны | Локальные транки |
|---|---|---|---|---|
| Europe | Frankfurt, DE | Amsterdam, NL | ~20 (DE, FR, UK, ES, IT, PL, ...) | Deutsche Telekom, BT, Orange, Telnyx EU |
| Americas | Virginia, US | Oregon, US | ~10 (US, CA, MX, BR, AR, ...) | Twilio, Bandwidth, Telnyx US |
| Asia-Pacific | Singapore | Tokyo, JP | ~8 (SG, JP, AU, IN, KR, ...) | SingTel, NTT, Telstra, Tata |
| Middle East | Dubai, UAE | Frankfurt (EU failover) | ~5 (UAE, SA, QA, KW, BH) | du, Etisalat, STC |
Сетевые зоны безопасности (Network Zones)
ИНТЕРНЕТ — КЛИЕНТЫ (недоверенная зона North) ───────────────────────────────────────────────── │ WebRTC-браузеры, SIP-телефоны за NAT, мобильные ───────────────────────────────────────────────── │ FIREWALL L1 (North) │ DMZ NORTH — Access (полу-доверенная зона) ───────────────────────────────────────────────── │ Kamailio Access SBC — внешний SIP от клиентов │ RTPEngine Access — медиа-прокси, SRTP↔RTP │ HAProxy/Nginx — HTTPS/WSS, TLS termination │ TURN-сервер — relay для WebRTC ───────────────────────────────────────────────── │ FIREWALL L2 │ CORE (доверенная зона) ───────────────────────────────────────────────── │ Kamailio Core — внутренняя маршрутизация │ FreeSWITCH — медиа-обработка (IVR, очереди) │ Go API — бизнес-логика, ESL, WebSocket ───────────────────────────────────────────────── │ FIREWALL L3 │ DATA (защищённая зона) ───────────────────────────────────────────────── │ PostgreSQL — данные (шифрование at rest) │ Redis — кеш и сессии │ MinIO — записи разговоров │ NATS — шина событий ───────────────────────────────────────────────── │ FIREWALL L4 │ DMZ SOUTH — Trunk (полу-доверенная зона) ───────────────────────────────────────────────── │ Kamailio Trunk SBC — SIP к PSTN-провайдерам │ RTPEngine Trunk — транскодирование кодеков │ Нормализация SIP, LCR, anti-fraud, Caller ID ───────────────────────────────────────────────── │ FIREWALL L5 (South) │ PSTN / ВНЕШНИЕ ПРОВАЙДЕРЫ (недоверенная зона South) ───────────────────────────────────────────────── │ SIP-транки (Telnyx, Twilio, локальные операторы) │ SMS-шлюзы, внешние API ─────────────────────────────────────────────────
Северная граница (клиенты): Любой SIP/RTP от клиентов из интернета сначала проходит через Access SBC (DMZ North), где нормализуется, фильтруется и проксируется. Только доверенный трафик от Access SBC попадает к Kamailio Core и FreeSWITCH.
Южная граница (PSTN): Исходящие вызовы к PSTN проходят через Trunk SBC (DMZ South), который нормализует SIP под требования каждого провайдера, применяет LCR-маршрутизацию и защищает от toll fraud. Провайдеры видят только Trunk SBC, а не внутреннюю архитектуру.
Данные (PostgreSQL, Redis) находятся в самой защищённой зоне и недоступны извне — ни с северной, ни с южной стороны.
Слои системы
Система разделена на шесть функциональных слоёв. Каждый слой изолирован и взаимодействует с соседними по чётко определённым протоколам.
Точки входа пользователей в систему. Четыре типа клиентов:
React — веб-интерфейс администратора и оператора (управление, статистика, настройки).
SIP.js — веб-телефон в браузере через WebRTC (звонки без установки ПО).
IP-телефоны — аппаратные SIP-телефоны (Yealink, Grandstream, Snom).
Мобильные — SIP-клиенты на смартфонах (Ooh, Ooh, Ooh Ooh Ooh, Ooh Ooh Ooh Ooh, Ooh Ooh, Ooh Ooh, Ooh Ooh Ooh Ooh, и т.д.).
Входная точка для всего трафика. Два компонента:
Nginx — reverse proxy для HTTP/HTTPS и WebSocket. TLS-терминация, маршрутизация запросов к Go API и WebSocket-соединений к Kamailio.
Kamailio — SIP proxy/registrar. Регистрация устройств, аутентификация, маршрутизация SIP, балансировка нагрузки между FreeSWITCH-серверами, обработка NAT.
Центральный узел управления системой:
Go Backend API — REST API и WebSocket-сервер. Управление пользователями, правами, настройками. Логика автообзвона, аналитика, CDR-записи, интеграции с CRM. Взаимодействует с FreeSWITCH через ESL (Event Socket Library).
Обработка голоса и медиа-потоков:
FreeSWITCH — медиа-сервер (B2BUA). IVR, очереди, конференции, запись, диалплан. Кластер из N серверов для горизонтального масштабирования.
RTPEngine — медиа-прокси. Транскодирование (WebRTC ↔ RTP), управление NAT для медиа, проксирование голосового трафика.
Персистентное и оперативное хранение:
PostgreSQL — основная БД: пользователи, настройки, CDR, диалпланы, маршрутизация.
Redis — кеш и оперативные данные: сессии, статусы операторов, блокировки, очереди задач.
MinIO — S3-совместимое хранилище: записи разговоров, голосовые сообщения, файлы IVR.
Второй SBC — пограничный контроллер между внутренней сетью и PSTN-провайдерами:
Kamailio Trunk SBC — SIP-нормализация для разных провайдеров, транк-аутентификация, E.164 нормализация номеров, LCR-маршрутизация, Caller ID manipulation, anti-fraud.
RTPEngine Trunk — опциональный, для транскодирования кодеков (G.711↔G.729) если провайдеры требуют специфические форматы.
Topology hiding — провайдеры видят только Trunk SBC, а не внутреннюю архитектуру.
Связь с внешними сетями и сервисами:
SIP-транки — подключение к PSTN через провайдеров (Telnyx, Twilio, локальные операторы). Входящие и исходящие вызовы на обычные телефоны.
SMS-шлюзы — отправка SMS-уведомлений (пропущенные, callback).
Внешние API — CRM, Helpdesk, системы аналитики.
Потоки данных
Три основных сценария звонков, показывающие как данные проходят через все слои системы.
Клиент звонит на номер компании, проходит IVR-меню и попадает к свободному оператору.
Оператор звонит клиенту через веб-телефон (SIP.js в браузере) на мобильный номер.
Сотрудник звонит коллеге из браузера на настольный IP-телефон внутри компании.
Сигнализация vs Медиа
Фундаментальный принцип VoIP-архитектуры: сигнализация и медиа идут по совершенно разным путям, через разные компоненты, по разным протоколам. Сигнализация (SIP) — это управляющий канал: «кто звонит, кому, принять или отклонить». Медиа (RTP) — это сам голос, реальные аудио-данные. Понимание этого разделения критически важно для диагностики и архитектуры.
═══ СИГНАЛИЗАЦИЯ (Control Plane) ═══ ═══ МЕДИА (Data Plane) ═══ Протокол: SIP Протокол: RTP / SRTP Транспорт: TCP / UDP / WSS Транспорт: UDP Нагрузка: Лёгкая (текстовые сообщения) Нагрузка: Тяжёлая (50 пакетов/сек) Задача: Управление вызовами Задача: Передача голоса ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Телефон │─SIP─▶│ Kamailio │─SIP─▶│FreeSWITCH│ └──────────┘ └──────────┘ └──────────┘ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ Телефон A│═RTP═▶│RTPEngine │═RTP═▶│ Телефон B│ └──────────┘ └──────────┘ └──────────┘ Сигнализация проходит: Медиа проходит: Телефон → Kamailio → FreeSWITCH Телефон A → RTPEngine → Телефон B (настройка, изменение, завершение) (голос, реальное время)
Путь: Телефон → Kamailio → FreeSWITCH
Протокол: SIP (TCP/UDP/WSS, порт 5060/5061)
Характер: Лёгкий трафик, текстовые сообщения
Задачи: Установка вызова, перевод, удержание, завершение, регистрация устройств
Компоненты: Kamailio (маршрутизация), FreeSWITCH (логика)
Путь: Телефон A → RTPEngine → Телефон B
Протокол: RTP/SRTP (UDP, порты 10000–20000)
Характер: Тяжёлый трафик, 50 пакетов/сек на поток
Задачи: Передача голоса, транскодирование, шифрование
Компоненты: RTPEngine (прокси, транскодирование)
Сигнализация — это SMS-сообщения, которыми вы договариваетесь о встрече: «Давай встретимся в кафе в 15:00». Медиа — это сама встреча: реальный разговор лицом к лицу. SMS идут через сотового оператора, а встреча происходит в кафе — совершенно разные каналы для совершенно разных задач.
Если звонок устанавливается (телефон звонит), но нет голоса — проблема в медиа-пути (RTPEngine, NAT, файрволл для UDP-портов). Если звонок вообще не проходит — проблема в сигнализации (Kamailio, SIP-маршрутизация, аутентификация). Диагностика всегда начинается с определения: «Это проблема сигнализации или медиа?»
Компоненты стека
Детальное описание каждого компонента: что делает, какие функции покрывает, ключевые модули и примеры конфигурации.
Kamailio — SIP-прокси
Kamailio — высокопроизводительный SIP-прокси-сервер, написанный на C. Способен обрабатывать тысячи вызовов в секунду на одном ядре. Работает исключительно с сигнализацией (SIP) — голосовые потоки (RTP) через него не проходят.
| Модуль | Назначение |
|---|---|
| usrloc | Хранение регистраций (кто где онлайн) |
| auth_db | Аутентификация SIP из PostgreSQL |
| dispatcher | Балансировка между FreeSWITCH нодами |
| pike | Защита от brute-force (rate limiting) |
| permissions | ACL — белые/чёрные списки IP |
| tls | Шифрование SIP-сигнализации |
| nathelper | Исправление NAT в SIP-заголовках |
| websocket | SIP поверх WebSocket для WebRTC |
| rtpengine | Управление RTPEngine для медиа-проксирования |
| dmq | Синхронизация данных между нодами кластера |
| dialog | Отслеживание активных диалогов |
| htable | In-memory хеш-таблицы для кеширования |
Kamailio не работает с голосом. Его задача — быстро решить КУДА отправить вызов и сделать это надёжно. Думайте о нём как о «маршрутизаторе для SIP-пакетов».
FreeSWITCH — Медиа-сервер
FreeSWITCH — медиа-сервер и «мозг» обработки вызовов. Написан на C, обрабатывает голос, IVR-меню, очереди, конференции и запись разговоров. Именно здесь вызов «оживает» — FreeSWITCH отвечает, проигрывает приветствие, соединяет абонентов и записывает разговор.
| Модуль | Назначение |
|---|---|
| mod_sofia | SIP-стек (регистрация, вызовы) |
| mod_callcenter | Очереди вызовов (ACD) |
| mod_conference | Конференц-связь |
| mod_voicemail | Голосовая почта |
| mod_dptools | Инструменты диалплана (bridge, transfer, record) |
| mod_ivr | Интерактивное голосовое меню |
| mod_lua | Lua-скрипты для логики вызовов |
| mod_event_socket | ESL — внешнее управление (Go, Python) |
| mod_verto | WebRTC-протокол (альтернатива SIP.js) |
| mod_local_stream | Музыка ожидания (MOH) |
| mod_spandsp | Факс T.38, DTMF detection |
| mod_unimrcp | ASR/TTS через MRCP-протокол |
FreeSWITCH управляется извне через ESL (Event Socket). Ваш Go-сервис подключается к ESL и командует: «ответь», «переведи», «запиши». Это позволяет вынести всю бизнес-логику из конфигов FreeSWITCH в нормальный код.
RTPEngine — Медиа-прокси
RTPEngine — медиа-прокси, разработанный компанией Sipwise. Проксирует RTP/SRTP-потоки между клиентами, обеспечивая NAT traversal, конвертацию шифрования и транскодирование кодеков. Не обрабатывает логику вызовов — только пропускает и трансформирует медиа-потоки.
| Функция | Назначение |
|---|---|
| NAT traversal | Проксирование медиа для клиентов за NAT |
| SRTP ↔ RTP | Конвертация шифрованного в нешифрованный поток |
| WebRTC bridge | DTLS-SRTP (браузер) ↔ plain RTP (IP-телефон) |
| Codec transcode | Перекодирование (Opus ↔ G.711) при необходимости |
| Media recording | Запись RTP-потока в файл |
| ICE handling | Обработка ICE candidates для WebRTC |
Без RTPEngine WebRTC-клиенты не смогут разговаривать с обычными SIP-телефонами — разные протоколы шифрования и кодеки. RTPEngine выступает «переводчиком» между мирами.
Go Backend — API и бизнес-логика
Go Backend — ваш кастомный сервис, «клей» между всеми компонентами системы. Предоставляет REST API для фронтенда, управляет вызовами через ESL, обрабатывает CDR-события, управляет кампаниями автообзвона и рассылает вебхуки во внешние системы.
| Функция | Назначение |
|---|---|
| REST API | CRUD пользователей, очередей, IVR, маршрутов |
| ESL Client | Управление вызовами через FreeSWITCH |
| WebSocket Hub | Реалтайм-события для React-дашборда |
| CDR Processor | Приём событий → запись в PostgreSQL |
| Dialer Engine | Управление кампаниями автообзвона |
| Webhook Dispatcher | Отправка событий во внешние системы (CRM) |
| Provisioning Server | Автонастройка IP-телефонов по MAC |
| Auth & RBAC | JWT-аутентификация, роли и права |
Ключевые Go-библиотеки:
Go идеален для телефонии: горутины для тысяч одновременных ESL-событий, низкая задержка, простой деплой одним бинарником. Вся бизнес-логика живёт здесь, а не в конфигах Kamailio или FreeSWITCH.
PostgreSQL — Основная БД
PostgreSQL — основное постоянное хранилище всех данных системы. Содержит информацию о пользователях, маршрутизации, CDR-записях, конфигурации IVR, очередей и кампаний. Используется как Go-сервисом (бизнес-данные), так и Kamailio (аутентификация SIP).
| Группа | Таблицы / Назначение |
|---|---|
| Мультитенантность | tenants — компании, домены, лимиты |
| Пользователи | users, extensions, sip_credentials, devices |
| Маршрутизация | dialplans, did_numbers, trunks, outbound_routes |
| IVR | ivr_menus — дерево меню в JSON |
| Очереди | queues, queue_members — настройки и агенты |
| CDR | cdr — журнал всех вызовов |
| Записи | recordings — метаданные файлов записей |
| Voicemail | voicemail_boxes, voicemail_msgs |
| Конференции | conference_rooms |
| Автообзвон | campaigns, campaign_numbers |
| Аудит | audit_log — история изменений |
PostgreSQL используется и Kamailio (для аутентификации), и Go (для бизнес-данных). Это единый источник правды (single source of truth) для всей системы.
Redis — Кеш и реалтайм
Redis — in-memory хранилище для эфемерного состояния реального времени. Здесь живут данные, которые нужны «прямо сейчас»: активные вызовы, статусы операторов, метрики очередей, API-сессии и счётчики rate limiting.
| Ключ (key pattern) | Хранимые данные |
|---|---|
| call:{uuid} | Активный вызов: caller, callee, state, start_time |
| agent:{id}:status | Статус оператора: ready/busy/break/offline |
| presence:{ext} | Presence: available/dnd/away |
| queue:{id}:stats | Очередь: waiting, talking, avg_wait, sla% |
| session:{token} | API-сессия: user_id, tenant_id, role, expires |
| rate:{ip} | Rate limiting: counter + TTL |
| location:{aor} | Кеш регистраций из Kamailio |
Redis — это «оперативная память» системы. Всё что нужно знать ПРЯМО СЕЙЧАС (кто онлайн, кто свободен, сколько в очереди) — здесь. Данные эфемерны: перезапуск Redis не сломает систему, только временно обнулит метрики.
NATS — Шина событий
NATS — легковесный брокер сообщений, написанный на Go. Быстрее RabbitMQ, проще Kafka. Обеспечивает асинхронную связь между компонентами: FreeSWITCH генерирует события, Go обрабатывает их и рассылает подписчикам — дашборду, CDR-процессору, вебхук-диспетчеру.
| Subject (тема) | Данные события |
|---|---|
| call.started | uuid, caller, callee, direction, queue |
| call.answered | uuid, agent_id, wait_time |
| call.ended | uuid, duration, disposition, recording_path |
| agent.status.changed | agent_id, old_status, new_status |
| queue.stats.updated | queue_id, waiting, talking, sla |
| voicemail.new | box_id, caller, duration, file_path |
| cdr.ready | cdr_id, complete record |
| webhook.dispatch | url, payload, event_type |
NATS развязывает компоненты. FreeSWITCH не знает о React-дашборде, Go не знает о CRM — они просто публикуют события. Каждый подписчик получает то, что ему нужно, не создавая связности.
React Frontend — Панель управления
React Frontend — одностраничное приложение (SPA) для администрирования системы, панели оператора и реалтайм-дашбордов. Включает встроенный WebRTC-телефон на базе SIP.js и визуальный конструктор IVR на React Flow.
| Страница | Назначение |
|---|---|
| Dashboard | Реалтайм: активные вызовы, очереди, агенты |
| Users | CRUD пользователей и расширений |
| Queues | Настройка очередей, стратегий, агентов |
| IVR Builder | Визуальный drag-n-drop конструктор меню |
| CDR | Журнал вызовов с фильтрами и экспортом |
| Recordings | Прослушивание записей, скачивание |
| Reports | Графики, SLA, heatmap нагрузки |
| WebPhone | Встроенный SIP.js-телефон |
| Campaigns | Управление автообзвоном |
| Settings | Транки, маршруты, безопасность |
Ключевые библиотеки:
React Flow позволяет строить IVR визуально — перетаскивая блоки. Это намного удобнее чем XML/Lua конфиги. Пользователь рисует меню мышкой, а система генерирует конфигурацию автоматически.
Homer — SIP-мониторинг
Homer — специализированный инструмент захвата и анализа SIP-трафика. Перехватывает все SIP-пакеты через HEP-протокол, строит визуальные диаграммы вызовов и помогает диагностировать проблемы с сигнализацией. Незаменим при отладке «звонок не проходит».
| Функция | Назначение |
|---|---|
| SIP Capture | Захват всех SIP-пакетов через HEP-протокол |
| Call Flow | Визуальная диаграмма: INVITE → 180 → 200 → BYE |
| Search | Поиск по номеру, Call-ID, времени, статусу |
| Alerts | Уведомления при SIP-ошибках, высоком % отказов |
| Statistics | Графики: calls/sec, response codes, latency |
Homer — ваш «Wireshark для SIP». Когда звонок не проходит, Homer покажет ТОЧНО где он застрял: на Kamailio? На FreeSWITCH? Получил 403? Таймаут? Визуальная диаграмма call flow экономит часы отладки.
AI/ML-сервисы — Распознавание и аналитика
AI/ML-сервисы — набор внешних сервисов для обработки речи и интеллектуальной аналитики. Распознавание речи (ASR), синтез речи (TTS), анализ транскрипций через LLM и определение тональности. Могут быть как self-hosted, так и облачными.
| Сервис | Назначение |
|---|---|
| Vosk | Self-hosted ASR (Speech-to-Text), поддержка русского |
| Whisper | OpenAI ASR, высокое качество, можно self-host |
| Piper | Self-hosted TTS (Text-to-Speech), нейросетевой |
| LLM (Claude/GPT) | Анализ транскрипций, суммаризация, классификация |
| Sentiment | Определение тональности: позитивная/негативная |
Поток обработки (integration flow):
Вызов → FreeSWITCH записывает → WAV-файл → Whisper STT → текст транскрипции → LLM-анализ (суммаризация, классификация, тональность) → результаты в БД
Vosk работает оффлайн и бесплатно. Для реалтайм-распознавания в IVR — идеальный выбор. Whisper лучше для post-call анализа, где важно качество, а не скорость.
Инфраструктура — DevOps-компоненты
Инфраструктура — набор DevOps-инструментов, обеспечивающих контейнеризацию, мониторинг, логирование, хранение файлов и автоматизацию развёртывания. Без этого слоя невозможно надёжно эксплуатировать систему в продакшене.
| Инструмент | Назначение |
|---|---|
| Docker / Compose | Контейнеризация всех компонентов |
| Nginx | Reverse proxy, TLS, балансировка HTTP |
| Prometheus | Сбор метрик (CPU, RAM, calls, queue depth) |
| Grafana | Визуализация метрик, алерты |
| Loki | Агрегация логов из всех контейнеров |
| MinIO | S3-совместимое хранилище для записей разговоров |
| Certbot | Автоматическое обновление SSL-сертификатов |
| Ansible | Автоматизация развёртывания и конфигурации |
Docker Compose — ваш лучший друг на старте. Один файл docker-compose.yml поднимает ВСЮ систему. Для продакшена — переходите на Docker Swarm или Kubernetes, но начинайте с Compose.
Связи компонентов
Матрица «функция → компонент», диаграмма взаимодействий и полная последовательность обработки вызова.
Матрица «Функция → Компонент»
Матрица показывает, какой компонент за какую функцию отвечает. — основная ответственность (Primary): компонент является главным исполнителем функции. — участие (Secondary): компонент вспомогательно участвует в реализации. Пустая ячейка — компонент не задействован.
| Категория функций | Kamailio | FreeSWITCH | RTPEngine | Go API | PostgreSQL | Redis | NATS | React |
|---|---|---|---|---|---|---|---|---|
| Управление вызовами | ||||||||
| Переводы | ||||||||
| Многосторонние | ||||||||
| IVR | ||||||||
| Очереди (ACD) | ||||||||
| Голосовая почта | ||||||||
| Маршрутизация | ||||||||
| Супервизор | ||||||||
| Запись / Аналитика | ||||||||
| Статусы / Presence | ||||||||
| Автообзвон | ||||||||
| Интеграции | ||||||||
| WebRTC | ||||||||
| Безопасность | ||||||||
| Отказоустойчивость | ||||||||
| Администрирование |
По строкам видно, какие компоненты нужны для каждой функции. По столбцам — зона ответственности каждого компонента. Например, FreeSWITCH — основной исполнитель большинства телефонных функций, а Go API отвечает за бизнес-логику, аналитику и интеграции.
Кто с кем говорит
Диаграмма показывает все межкомпонентные соединения с указанием используемых протоколов. Каждая стрелка — это реальный сетевой канал, который нужно настроить, защитить и мониторить.
┌───────────┐ ┌────────────┐
│ Браузер │ │ IP-телефон │
│(SIP.js) │ │ (SIP) │
└─────┬─────┘ └──────┬─────┘
│ │
WSS/SIP UDP/SIP
│ │
┌─────▼────────────────────────▼─────┐
│ Kamailio │
│ SIP Proxy / Registrar │
└──┬─────────┬──────────┬──────────┬──┘
│ │ │ │
SIP │ ng ctrl│ SQL │ HEP │
│ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌──────────┐ ┌───────┐
│ FreeSWITCH │ │ RTPEngine│ │PostgreSQL│ │ Homer │
│ (B2BUA) │ │ (Media) │ │ (Data) │ │(SIP │
│ │ │ │ │ │ │ мон.) │
└──┬───┬──┬──┘ └──────────┘ └─────▲────┘ └───▲───┘
│ │ │ │ │
RTP │ │ │HEP │ │
│ │ └─────────────────────────────────►─┘
▼ │
┌──────────┐ │S3 API ┌──────────────────┐
│ RTPEngine│ │ │ Go API │
│ (Media │ │ │ REST + WebSocket │
│ proxy) │ ▼ └──┬───┬───┬───┬───┘
└──────────┘ ┌──────┐ │ │ │ │
│MinIO │ ESL/│ │SQL│ │NATS
│(S3) │ TCP │ │ │ │
└──────┘ │ │ │ │
▼ ▼ ▼ ▼
┌────────────┐ ┌──────────┐ ┌─────┐ ┌──────────┐
│ FreeSWITCH │ │PostgreSQL│ │NATS │ │ Redis │
└────────────┘ └──────────┘ └──┬──┘ └──────────┘
│
события
│
▼
┌───────────┐
│ React │
│ Dashboard │
└───────────┘
Сводная таблица всех соединений:
| Источник | Назначение | Протокол | Назначение соединения |
|---|---|---|---|
| Браузер (SIP.js) | Kamailio | WSS/SIP | SIP-сигнализация через WebSocket (регистрация, звонки) |
| IP-телефон | Kamailio | UDP/SIP | SIP-сигнализация (регистрация, звонки) |
| Kamailio | FreeSWITCH | SIP | Маршрутизация вызовов к медиа-серверу |
| Kamailio | RTPEngine | ng control | Управление медиа-проксированием (offer/answer SDP) |
| FreeSWITCH | RTPEngine | RTP | Голосовые потоки к медиа-прокси |
| Go API | FreeSWITCH | ESL/TCP | Управление вызовами, получение событий (Event Socket) |
| Go API | PostgreSQL | SQL | Чтение/запись данных (пользователи, CDR, настройки) |
| Go API | Redis | Redis protocol | Кеш, сессии, статусы, блокировки |
| Go API | NATS | NATS | Публикация и подписка на события (real-time) |
| Go API | React | HTTP/WS | REST API и WebSocket для реалтайм-обновлений |
| Kamailio | PostgreSQL | SQL | Таблицы маршрутизации, регистрации, ACL |
| Kamailio | Homer | HEP | Зеркалирование SIP-трафика для мониторинга |
| FreeSWITCH | Homer | HEP | Зеркалирование SIP-трафика для мониторинга |
| FreeSWITCH | MinIO | S3 API | Сохранение записей разговоров и голосовой почты |
Каждое соединение из таблицы выше требует открытых портов и сетевых правил. При развёртывании убедитесь, что все указанные протоколы и порты разрешены между соответствующими хостами. Особое внимание — UDP-портам RTP (10000–20000), которые часто блокируются по умолчанию.
Полная последовательность входящего вызова
Детальная последовательность обработки входящего звонка, проходящего через каждый компонент системы: от поступления SIP INVITE из телефонной сети до обновления дашборда в реальном времени. Каждый шаг указывает задействованный компонент и выполняемое действие.
Несмотря на 15 шагов и участие 8 компонентов, весь процесс от поступления INVITE до звонка телефона оператора занимает менее 500 мс при нормальной нагрузке. SIP-сигнализация передаётся в виде текстовых сообщений и обрабатывается каждым компонентом за единицы миллисекунд.
Диаграмма последовательности (sequence diagram) в текстовом формате
PSTN Kamailio RTPEngine FreeSWITCH Redis Оператор Go API NATS React │ │ │ │ │ │ │ │ │ │─INVITE─▶│ │ │ │ │ │ │ │ │ │──offer──▶│ │ │ │ │ │ │ │ │◀─answer──│ │ │ │ │ │ │ │ │──INVITE─────────────▶│ │ │ │ │ │ │ │ │ │──IVR─────▶│ │ │ │ │ │ │ │ │◀─DTMF 2──│ │ │ │ │ │ │ │ │──query───▶│ │ │ │ │ │ │ │ │◀─agent───│ │ │ │ │ │ │◀─INVITE──────────────│ │ │ │ │ │ │ │──────────────────────────────────▶│ │ │ │ │ │ │◀──────────────────────────────────│ 200 OK │ │ │ │ │ │──answer──▶│ │ │ │ │ │ │ │ │ │═══RTP═══▶│═══════════│══════════│ │ │ │ │═══RTP══▶│═══════════│◀══RTP═══│ │ │ │ │ │ │ │ │ │──event──────────────────────▶│ │ │ │ │ │ │ │ │ │──pub──▶│ │ │ │ │ │ │ │ │ │──WS──▶│
План реализации
Пошаговый план от первого звонка между двумя телефонами до полноценной продакшен-системы.
Установка FreeSWITCH, регистрация двух софтфонов и первый звонок между ними. Минимальная рабочая система.
Что делаем:
— Устанавливаем FreeSWITCH из пакетов или исходников
— Создаём два SIP-аккаунта (extensions) в конфигурации
— Регистрируем софтфоны (Oнлинфон, Oбщие, Oлинфон или Oбщие — любой SIP-клиент)
— Совершаем звонок между двумя внутренними номерами
Чему учимся: SIP-регистрация, базовый call flow (INVITE → 200 OK → ACK → BYE), dialplan, профили пользователей.
Разблокируем: внутренние звонки
Пишем Go-клиент для ESL (Event Socket Library) — программный контроль над звонками.
Что делаем:
— Подключаемся к FreeSWITCH через ESL (inbound-режим)
— Слушаем события: новый звонок, ответ, завершение
— Управляем звонками из кода: ответить, положить трубку, перевести
— Originate — инициация звонка из Go-кода
Чему учимся: протокол ESL, событийная архитектура (event-driven), команды FreeSWITCH API, управление каналами.
Разблокируем: API управления звонками
Добавляем веб-клиент на SIP.js — звонки прямо из браузера без установки ПО.
Что делаем:
— Настраиваем WebSocket-транспорт в FreeSWITCH (mod_verto или WSS для SIP)
— Подключаем библиотеку SIP.js на веб-странице
— Реализуем входящие и исходящие звонки из браузера
— Настраиваем TLS-сертификаты (WebRTC требует HTTPS)
Чему учимся: SIP over WebSocket, SRTP-шифрование, ICE/STUN/TURN, кодек Opus, взаимодействие WebRTC и SIP.
Разблокируем: звонки из браузера
Создаём полноценный Go REST API для управления пользователями, расширениями и правилами маршрутизации.
Что делаем:
— Проектируем схему базы данных: пользователи, расширения (extensions), правила маршрутизации
— REST API: CRUD для пользователей и настроек
— Синхронизация конфигурации FreeSWITCH через mod_xml_curl или ESL
— Аутентификация и авторизация API (JWT)
Чему учимся: проектирование API, схема БД для телефонии, провижининг (provisioning), динамическая конфигурация.
Разблокируем: админ-панель, автоматический провижининг
Подключаем SIP-провайдера для звонков на реальные телефонные номера (PSTN).
Что делаем:
— Выбираем SIP-провайдера (trunk provider) и регистрируемся
— Настраиваем SIP-транк (gateway) в FreeSWITCH
— Маршрутизация исходящих звонков через транк
— Приём входящих звонков на DID-номера (Direct Inward Dialing)
— Настройка Caller ID (отображаемый номер)
Чему учимся: конфигурация транков, DID-номера, Caller ID, кодеки для WAN, аутентификация у провайдера (IP-based или credentials).
Разблокируем: связь с внешним миром (PSTN)
Голосовые меню (IVR) на Lua-скриптах и очереди вызовов через mod_callcenter.
Что делаем:
— Создаём голосовое меню (IVR): «Нажмите 1 для продаж, 2 для поддержки»
— Пишем Lua-скрипты для логики IVR (условия, переходы, TTS)
— Настраиваем mod_callcenter: очереди, агенты, стратегии распределения
— Стратегии: round-robin, longest-idle, ring-all, skill-based routing
Чему учимся: dialplan-скриптинг (Lua), логика IVR, стратегии очередей, статусы агентов, music on hold.
Разблокируем: функции контакт-центра
Запись звонков, сбор CDR (Call Detail Records) и построение дашбордов аналитики.
Что делаем:
— Включаем запись звонков (mod_recording) с хранением в файловой системе или S3
— Собираем CDR: время, длительность, номера, статус (mod_cdr_csv, mod_cdr_pg_csv)
— Сохраняем CDR в PostgreSQL для анализа
— Строим дашборд: количество звонков, среднее время ожидания, SLA
Чему учимся: медиа-хранилище, форматы записи, CDR-схемы, аналитика звонков, отчёты по качеству.
Разблокируем: мониторинг качества, аналитика
Добавляем Kamailio как SIP-прокси, кластеризуем FreeSWITCH и подключаем Homer для мониторинга.
Что делаем:
— Устанавливаем Kamailio перед FreeSWITCH: балансировка, маршрутизация, защита
— Кластер FreeSWITCH: несколько инстансов для отказоустойчивости и нагрузки
— Homer SIP Capture — сбор и визуализация SIP-трафика для отладки
— Health checks, автоматический failover, мониторинг (Prometheus + Grafana)
Чему учимся: SIP-проксирование, балансировка нагрузки, высокая доступность (HA), SIP capture, продакшен-мониторинг.
Разблокируем: продакшен-система
Вы можете остановиться после любой фазы и иметь полностью рабочую систему. Каждая следующая фаза строится на предыдущей, добавляя новые возможности. Начните с Фазы 1 — первый звонок можно совершить за один вечер.
Реализация FreeSWITCH
Детальный план реализации FreeSWITCH (Слой 4: Media) — 8 фаз от базового звонка до WebRTC. Миграция с MirtaPBX (Asterisk). ~400 concurrent calls в EU, полный call-center.
Что такое FreeSWITCH
FreeSWITCH — это soft-switch (программный коммутатор). Если раньше телефонные станции были огромными шкафами с оборудованием, то FreeSWITCH — это то же самое, но в виде программы на Linux-сервере.
Представьте коммутатор из кинофильма — оператор втыкает провода, соединяя абонентов. FreeSWITCH делает то же самое, только программно и автоматически.
Open Source Написан на C Создан в 2006 Автор: Anthony Minessale (ex-Asterisk)
FreeSWITCH — это B2BUA. Он не просто пропускает звонок насквозь (как прокси). Он полностью разрывает звонок на две половинки:
Телефон A FreeSWITCH Телефон B │ │ │ │──── SIP Leg A ──────│ │ │ (отдельный звонок) │ │ │ │──── SIP Leg B ──────│ │ │ (отдельный звонок) │ │ │ │ │◄═══ Голос (RTP) ════►│◄═══ Голос (RTP) ════►│
Leg A — звонок между телефоном A и FreeSWITCH.
Leg B — звонок между FreeSWITCH и телефоном B.
Это даёт полный контроль: между двумя ногами FreeSWITCH может делать что угодно —
записывать, транскодировать, подмешивать аудио, ставить на удержание, переводить, подключать третьего участника.
Прокси (Kamailio) — это почтальон, который передаёт конверт не открывая. Быстро, массово, но не может изменить содержимое.
B2BUA (FreeSWITCH) — это секретарь, который читает письмо, может переписать, перенаправить, скопировать, добавить приложение.
┌──────────────────────────────────────────────────────────────┐ │ FreeSWITCH │ │ │ │ ┌────────────────────────────────────────────────────────┐ │ │ │ ЯДРО (Core) │ │ │ │ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ │ │ │ │ │ State Machine│ │ Event System │ │Channel Engine │ │ │ │ │ │ жизненный │ │ все события │ │ управление │ │ │ │ │ │ цикл канала │ │ pub/sub │ │ звонками │ │ │ │ │ └──────────────┘ └──────────────┘ │ Leg A ↔ Leg B │ │ │ │ │ └────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌────────────────┐ │ │ │ │ │ Codec Engine │ │ Timer System │ │ Memory Pool │ │ │ │ │ │ перекодиров. │ │ │ │ управление │ │ │ │ │ │ Opus↔G.711 │ │ │ │ памятью │ │ │ │ │ └──────────────┘ └──────────────┘ └────────────────┘ │ │ │ └───────────────────────┬────────────────────────────────┘ │ │ │ Module API │ │ ┌───────────────────────┼────────────────────────────────┐ │ │ │ МОДУЛИ (~200) │ │ │ │ │ │ │ │ Endpoints Applications Codecs │ │ │ │ mod_sofia(SIP) mod_dptools mod_opus │ │ │ │ mod_verto(WS) mod_callcenter mod_g711 │ │ │ │ mod_skinny mod_conference mod_g729 │ │ │ │ mod_voicemail mod_g722 │ │ │ │ │ │ │ │ Events Languages Formats │ │ │ │ mod_event_socket mod_lua mod_shout(MP3) │ │ │ │ mod_json_cdr mod_python mod_sndfile(WAV) │ │ │ │ mod_v8(JS) │ │ │ │ │ │ │ │ Config TTS / ASR │ │ │ │ mod_xml_curl mod_flite │ │ │ │ mod_dialplan_xml mod_tts_commandline │ │ │ │ mod_dialplan_lua │ │ │ └─────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ КОНФИГУРАЦИЯ │ │ │ │ Directory (кто) Dialplan (куда) SIP Profiles (как) │ │ │ └─────────────────────────────────────────────────────────┘ │ └──────────────────────────────────────────────────────────────┘
Написано на C. Это фундамент, который нельзя менять — на него нанизываются модули.
| Компонент | Что делает |
|---|---|
| Channel Engine | Управляет звонками. Каждый звонок = «канал» (channel) с уникальным UUID. Канал проходит состояния: NEW → INIT → ROUTING → EXECUTE → EXCHANGE_MEDIA → HANGUP |
| Event System | Внутренняя шина событий. Всё что происходит — звонок создан, DTMF нажата, запись начата — генерирует событие. ESL подписывается на эти события извне |
| State Machine | Жизненный цикл каждого канала. Чёткие переходы между состояниями. Гарантирует что ресурсы освобождаются при завершении звонка |
| Codec Engine | Перекодирование аудио на лету: Opus ↔ G.711 ↔ G.729. Если два телефона говорят на разных кодеках — ядро транскодирует прозрачно |
| Memory Pool | Управление памятью. Каждый звонок получает свой пул — когда звонок завершается, вся память освобождается одним блоком (нет утечек) |
Модули FreeSWITCH — подробная классификация
Endpoint-модули — как FreeSWITCH общается с внешним миром:
| Модуль | Протокол | Зачем |
|---|---|---|
mod_sofia | SIP (UDP/TCP/TLS/WSS) | Основной. 95% звонков идут через него |
mod_verto | WebSocket JSON-RPC | WebRTC из браузера (собственный протокол FS) |
mod_skinny | Cisco SCCP | Аппаратные Cisco-телефоны |
mod_freetdm | TDM / ISDN | Подключение к аналоговым линиям (legacy) |
Application-модули — что делать со звонком:
| Модуль | Зачем |
|---|---|
mod_dptools | Базовые инструменты: answer, bridge, playback, record, transfer, hold, park, intercept |
mod_callcenter | Очереди (ACD): стратегии распределения, агенты, тиры, музыка ожидания |
mod_conference | Конференц-звонки: комнаты, PIN, mute/unmute, запись |
mod_voicemail | Голосовая почта: greeting, хранение, уведомления, MWI |
mod_avmd | Определение автоответчика (Answering Machine Detection) |
mod_fifo | Простая парковка звонков (First In, First Out) |
Codec-модули — как кодировать голос:
| Модуль | Кодек | Битрейт | Когда |
|---|---|---|---|
mod_opus | Opus | 6-510 kbps (адаптивный) | WebRTC, внутренние звонки (лучшее качество) |
| встроен | G.711 (PCMU/PCMA) | 64 kbps | PSTN-транки, базовая совместимость |
mod_g729 | G.729 | 8 kbps | Экономия трафика (WAN, мобильные) |
mod_g722 | G.722 | 64 kbps | HD Voice (wideband) |
Language-модули — скриптовые языки для dialplan:
| Модуль | Язык | Для чего |
|---|---|---|
mod_lua | Lua | Рекомендуем Быстрый, встроенный, IVR-логика, HTTP-запросы из dialplan |
mod_python | Python | Сложная логика, ML/AI интеграции |
mod_v8 | JavaScript (V8) | Для тех кто знает JS |
Event/CDR-модули — связь с внешним миром:
| Модуль | Зачем |
|---|---|
mod_event_socket | ESL — Go подключается и управляет FS программно (порт 8021) |
mod_json_cdr | Отправляет CDR (детализацию) по HTTP в Go API после каждого звонка |
mod_xml_curl | FS запрашивает конфигурацию по HTTP у Go API (динамические пользователи, dialplan, очереди) |
| Раздел | Что определяет | Аналогия | Источник |
|---|---|---|---|
| Directory | Кто может звонить. Список SIP-аккаунтов с паролями и настройками | Телефонная книга компании | XML или mod_xml_curl (из БД) |
| Dialplan | Куда маршрутизировать звонок. Правила: «если набрали 1XXX → bridge к user/1XXX» | Правила коммутатора: «этот провод соединить с тем» | XML, Lua или mod_xml_curl |
| SIP Profiles | На каких IP/портах слушать SIP, какие кодеки, TLS, NAT | Какие «двери» открыты для звонков | XML (статический, меняется редко) |
Телефон A набирает 1002
│
▼
[1] CHANNEL_CREATE ← FS создаёт канал (Leg A), UUID: abc-123
│
▼
[2] ROUTING ← Ищет в Dialplan: что делать с номером 1002?
│ Нашёл: bridge user/1002
▼
[3] CHANNEL_CREATE ← Создаёт второй канал (Leg B), UUID: def-456
│ Звонит на телефон 1002
▼
[4] RINGING ← Телефон 1002 звонит (180 Ringing)
│
▼
[5] CHANNEL_ANSWER ← Телефон 1002 поднят (200 OK)
│
▼
[6] EXCHANGE_MEDIA ← Голос идёт: A → FS → B и B → FS → A
│ Здесь: запись, транскодирование, DTMF
│
│ ... разговор ...
│
▼
[7] CHANNEL_HANGUP ← Кто-то повесил трубку (BYE)
│
▼
[8] CDR ← Запись о звонке → Go API → PostgreSQL
│
▼
[9] DESTROY ← Каналы уничтожены, память освобождена
| FreeSWITCH ДЕЛАЕТ | FreeSWITCH НЕ ДЕЛАЕТ (это другие компоненты) |
|---|---|
| Принимает/делает SIP-звонки | Маршрутизация тысяч SIP-запросов → Kamailio |
| IVR, очереди, конференции | Веб-интерфейс → React |
| Запись звонков | REST API для пользователей → Go API |
| Транскодирование кодеков | Хранение данных → PostgreSQL / ClickHouse |
| DTMF-обработка | Балансировка нагрузки между серверами → Kamailio |
| Голосовая почта, TTS, ASR | NAT traversal для медиа → RTPEngine |
| Мост между двумя абонентами | Anti-DDoS, topology hiding → SBC (Kamailio) |
| Автообзвон (по команде из Go) | Бизнес-логика, кампании → Go API |
FreeSWITCH = медиа-сервер. Он обрабатывает звонки: принимает, играет аудио, записывает, соединяет, ставит в очередь.
Kamailio = SIP-прокси. Он маршрутизирует SIP быстро и массово, но не трогает медиа (голос).
Вместе: Kamailio решает куда направить звонок, FreeSWITCH решает что с ним делать.
Способы конфигурации FreeSWITCH (XML, HTTP API, Lua, ESL)
| Способ | Как работает | Для чего |
|---|---|---|
| Статический XML | Файлы на диске. Изменения → reloadxml в CLI |
SIP-профили, глобальные параметры — то, что меняется редко |
| mod_xml_curl Рекомендуем | FS запрашивает конфигурацию по HTTP у Go API. Go отвечает XML из PostgreSQL | Пользователи, очереди, dialplan — всё динамическое, из БД |
| mod_lua | Lua-скрипт в dialplan вместо XML. Может ходить в Redis, HTTP API | Сложная IVR-логика, маршрутизация по данным из БД |
| ESL (Event Socket) | Go подключается к FS (порт 8021) и управляет звонками программно | originate, transfer, hold, record — всё в реальном времени |
| mod_callcenter API | ESL-команды для управления очередями и агентами без перезагрузки | Добавить/удалить агента, сменить статус, создать очередь |
Для enterprise-системы: XML остаётся только для статики (SIP-профили, модули, ESL). Всё остальное — динамически через mod_xml_curl + Go API + PostgreSQL.
┌──────────────────────────────────────────────┐ │ Go API │ │ │ │ │ PostgreSQL (users, queues, │ │ dialplan rules, IVR trees) │ └─────────────┬──────────────┬─────────────────┘ │ │ ┌───────────┼──────────────┼──────────────┐ │ │ │ │ ▼ ▼ ▼ │ mod_xml_curl mod_lua ESL inbound │ «кто user? dialplan originate, │ какие логика на transfer, │ очереди?» Lua + HTTP uuid_kill... │ │ │ │ │ └───────────┼──────────────┘ │ │ │ FreeSWITCH │ │ │ Статический XML: │ • sip_profiles/internal.xml │ • modules.conf.xml │ • event_socket.conf.xml │ ┌───────────────────────────────────────────┘
Откуда: MirtaPBX (на базе Asterisk) — полный call-center: IVR, очереди, запись, автообзвон, супервизор, WebRTC.
Куда: FreeSWITCH — в составе enterprise-архитектуры (Kamailio SBC → Kamailio Core → FreeSWITCH → Go API).
Почему: масштабирование (43 страны), полный контроль, стоимость лицензий.
Подход: «снизу вверх» — на каждой фазе работающая система, постепенное наращивание функционала.
ФАЗА 1 ФАЗА 2 ФАЗА 3 ФАЗА 4 Базовый ESL IVR Очереди звонок управление меню ACD ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │ mod_sofia│────▶│mod_event│────▶│ mod_lua │────▶│mod_call │ │ dialplan │ │ _socket │ │ DTMF │ │ center │ │ bridge │ │ Go ← FS │ │ TTS │ │ агенты │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ ▼ ▼ ▼ ▼ 2 телефона Go управляет Входящие Распределение звонят друг звонками попадают по операторам другу программно в меню автоматически ФАЗА 5 ФАЗА 6 ФАЗА 7 ФАЗА 8 Запись Конференции Автообзвон WebRTC + CDR + Voicemail (Dialer) + Видео ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ │record_ │────▶│mod_conf │────▶│ Go ESL │────▶│mod_verto│ │ session │ │mod_voice│ │originate│ │ SIP.js │ │json_cdr │ │ mail │ │ AMD │ │ SRTP │ └─────────┘ └─────────┘ └─────────┘ └─────────┘ │ │ │ │ ▼ ▼ ▼ ▼ Все звонки Комнаты с Обзвон Браузер = записываются PIN, голосовая списков телефон CDR → PostgreSQL почта автоматически оператора
Фаза 1: Установка + базовый звонок
Самый минимум: FreeSWITCH запущен, два устройства зарегистрированы, можно позвонить. Это фундамент, на котором строится всё остальное.
1. Docker-образ FreeSWITCH
Собираем из исходников (Debian-based) — чтобы включить только нужные модули. Ванильный пакет содержит ~200 модулей, нам нужно ~30.
2. SIP-профили (mod_sofia)
internal (порт 5060) — для внутренних устройств (телефоны, веб-клиенты).
external (порт 5080) — для SIP-транков (пока пустой, пригодится в будущем).
3. User Directory (XML)
Регистрация SIP-аккаунтов: 1001, 1002 — с паролями, доменом, настройками кодеков.
4. Dialplan (XML)
Минимальный: набираем 1001 → bridge к устройству 1001. Набираем 1002 → bridge к 1002.
5. Кодеки
G.711 (PCMU/PCMA) — базовый, работает везде. Opus — для будущего WebRTC.
| Модуль | Зачем |
|---|---|
mod_sofia | SIP-стек FreeSWITCH. Регистрация, приём/отправка SIP |
mod_dptools | Инструменты dialplan: bridge, answer, hangup, playback |
mod_dialplan_xml | Парсинг XML-диалплана |
mod_commands | CLI-команды (sofia status, show channels) |
mod_console | Консоль для отладки (fs_cli) |
mod_logfile | Логирование в файл |
Пример конфигурации: SIP-профиль + User + Dialplan
SIP-профиль internal (sip_profiles/internal.xml):
User Directory (directory/default/1001.xml):
Dialplan (dialplan/default.xml):
Установите MicroSIP (Windows) или Orologio (Mac). Зарегистрируйте 1001 и 1002. С 1001 наберите 1002 — должен пойти звонок. Если слышите голос — Фаза 1 готова.
Фаза 2: ESL — программное управление
ESL (Event Socket Library) — протокол FreeSWITCH для внешнего управления. Через него Go-backend может создавать звонки, слушать события, переводить, вешать трубку — всё программно, без изменения XML-конфигурации.
Inbound (основной) — Go подключается к FreeSWITCH на порт 8021. Может отправлять команды и подписываться на события. Один Go-сервис управляет всем FS.
Outbound — FreeSWITCH сам подключается к Go при входящем звонке. Каждый звонок = отдельное TCP-соединение. Используется для сложной логики dialplan на стороне Go.
Рекомендация Начинаем с Inbound — проще, Go всегда online, одно соединение.
| Команда | Что делает |
|---|---|
originate | Инициировать звонок программно (позвонить на номер) |
uuid_bridge | Соединить два активных канала |
uuid_transfer | Перевести звонок на другой номер/extension |
uuid_kill | Повесить трубку на конкретном канале |
uuid_hold | Поставить на удержание |
uuid_record | Начать/остановить запись |
uuid_setvar | Установить переменную на канале |
Go подписывается на события FreeSWITCH и реагирует в реальном времени:
| Событие | Когда | Что делаем в Go |
|---|---|---|
CHANNEL_CREATE | Новый звонок появился | Создаём запись в Redis (active call) |
CHANNEL_ANSWER | Абонент поднял трубку | Обновляем статус, начинаем таймер |
CHANNEL_HANGUP | Звонок завершён | CDR → PostgreSQL, очищаем Redis |
DTMF | Нажата клавиша | Обработка IVR, перевод по DTMF |
RECORD_START/STOP | Запись началась/кончилась | Метаданные записи → БД |
CUSTOM callcenter::info | Событие очереди | Обновляем dashboard супервизора |
Пример: Go ESL-клиент (inbound)
Конфигурация ESL + Lua в FreeSWITCH
Event Socket (autoload_configs/event_socket.conf.xml):
Lua-скрипт (scripts/incoming_call.lua) — пример сложной логики:
Go-сервис запускается, подключается к FS по ESL, вызывает originate — телефон 1001 звонит. Go получает события CHANNEL_CREATE → CHANNEL_ANSWER → CHANNEL_HANGUP. Если это работает — Фаза 2 готова.
Фаза 3: IVR (Interactive Voice Response)
IVR — это «голосовой робот», который отвечает: «Нажмите 1 для продаж, 2 для поддержки, 3 для бухгалтерии». В FreeSWITCH реализуется через Lua-скрипты + аудиофайлы + play_and_get_digits.
1. Аудиофайлы
Приветствие, пункты меню, ожидание, "Пожалуйста подождите", "Все операторы заняты". Формат: WAV 16kHz mono (для качества) или 8kHz (для совместимости).
2. IVR-дерево на Lua
Многоуровневое меню: Главное → Подменю → Действие. Вся логика в Lua-скриптах.
3. DTMF-обработка
RFC 2833 (основной), SIP INFO (fallback). play_and_get_digits — играет аудио и ждёт нажатия.
4. Таймауты и fallback
Не нажал ничего за 5 сек → повторяем. 3 неудачных попытки → перевод на оператора.
5. TTS (Text-to-Speech)
Для динамических фраз: "Ваш номер в очереди — пять". mod_flite (английский) или mod_tts_commandline + Piper (русский).
Входящий звонок
│
«Здравствуйте!»
«Нажмите 1, 2 или 3»
│
┌────────┼────────┐
│ │ │
[1] [2] [3]
Продажи Поддержка Бухгалтерия
│ │ │
│ ┌──┼──┐ │
│ [1] [2] │
│ Тех. Возврат │
│ отдел │
▼ ▼ ▼
Очередь Очередь Extension
sales support 3001
Пример: IVR на Lua
Звоним на номер 5000 (IVR) → слышим приветствие → нажимаем 1 → попадаем в очередь продаж. Нажимаем 2 → подменю поддержки. Ничего не нажимаем → через 3 попытки переводят на оператора. Если всё работает — Фаза 3 готова.
Фаза 4: Очереди (ACD — Automatic Call Distribution)
ACD — ядро call-центра. Клиент ждёт в очереди, система выбирает лучшего свободного оператора и соединяет. В FreeSWITCH — mod_callcenter.
| Стратегия | Как работает | Когда использовать |
|---|---|---|
| longest-idle-agent | Звонок идёт оператору, который дольше всех свободен | Самая справедливая. Рекомендуем |
| round-robin | По кругу: 1→2→3→1→2→3 | Равная загрузка |
| ring-all | Звонят всем одновременно, кто первый поднял | Маленькие команды |
| top-down | Сначала первому, если не ответил → второму | Приоритетные агенты |
| agent-with-least-talk-time | Кто меньше всего наговорил за смену | Равная нагрузка по минутам |
Очереди: sales, support, billing — каждая со своей стратегией, MOH, таймаутами.
Агенты: статусы — Available, On Break, Logged Out. Тиры (уровни приоритета): tier 1 звонят первыми, если все в tier 1 заняты → tier 2.
Музыка ожидания (MOH): mod_local_stream — разные плейлисты для разных очередей.
Announcements: «Вы 3-й в очереди», «Примерное время ожидания — 2 минуты».
Callback: клиент нажимает * → оставляет номер → система перезвонит когда оператор освободится.
Overflow: очередь переполнена (>20 ждущих) → перевод в другую очередь / voicemail / IVR.
Конфигурация mod_callcenter
Очередь (autoload_configs/callcenter.conf.xml):
Агенты:
Тиры:
ESL-события от очереди (Go получает в реальном времени):
Звоним на 5001 (очередь sales) → слышим музыку ожидания → «Вы 1-й в очереди» → телефон оператора 1001 звонит → оператор поднимает → разговор. Go получает все события через ESL. Если работает — Фаза 4 готова.
Фаза 5: Запись + CDR
Запись нужна для контроля качества, обучения, compliance (GDPR требует уведомлять). CDR — для аналитики: сколько звонков, средняя длительность, пропущенные, загрузка агентов.
record_session — записывает весь звонок от начала до конца, включая переводы.
Двуканальная запись (stereo): оператор в левом канале, клиент в правом. Это критично для speech analytics — позволяет анализировать каждого отдельно.
Формат: WAV (для качества) → конвертация в MP3 (mod_shout) для хранения. Или сразу MP3.
Хранение: локальный диск → Go API загружает в MinIO (S3). Путь в PostgreSQL.
Условная запись: только очереди, только внешние, по расписанию, по согласию клиента.
GDPR: уведомление «Разговор записывается» (playback перед записью), автоудаление через N дней.
mod_json_cdr — после каждого звонка FreeSWITCH отправляет JSON по HTTP POST в Go API.
Что содержит CDR:
caller_id кто звонил
destination куда звонил
start_time начало звонка
answer_time когда ответили
end_time конец звонка
duration общая длительность
billsec оплачиваемое время (от answer)
hangup_cause причина завершения
recording_path путь к записи
queue_name очередь
agent оператор
Go API → PostgreSQL → таблица cdr.
При 400+ concurrent calls CDR быстро растут до миллионов записей. PostgreSQL одна не справится с аналитическими запросами на таких объёмах. Решение — тройной стек: каждая БД делает то, в чём она лучшая.
FreeSWITCH │ │ JSON CDR (HTTP POST после каждого звонка) ▼ Go API ────────────────────────────────────────────── │ │ │ │ sync (мгновенно) │ async (батчами) │ async (батчами) ▼ ▼ ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────────┐ │ PostgreSQL │ │ ClickHouse │ │ Elasticsearch │ │ │ │ │ │ │ │ «Горячие» │ │ ВСЯ история │ │ Полнотекстовый │ │ последние │ │ за годы │ │ поиск │ │ 7-30 дней │ │ │ │ │ │ │ │ Аналитика: │ │ «Найди звонок │ │ JOIN с │ │ агрегации, │ │ с +4917...» │ │ users, │ │ отчёты, │ │ «Все звонки │ │ queues, │ │ дашборды │ │ агента Иванов» │ │ agents │ │ │ │ Fuzzy match │ │ │ │ Retention: │ │ │ │ TTL: 30 дней│ │ 3-5 лет │ │ TTL: 90 дней │ │ затем → │ │ сжатие 5-10x │ │ (горячий индекс) │ │ удаляется │ │ │ │ │ └─────────────┘ └──────────────┘ └──────────────────┘ │ │ │ ▼ ▼ ▼ Оперативная Grafana / Kibana / работа BI-отчёты поиск в UI
Роль: основная БД для «горячих» данных последних 7-30 дней. Здесь хранятся текущие смены, активные агенты, незакрытые звонки.
Почему PG: нужны JOIN-ы с таблицами users, queues, agents, tenants. Транзакции, целостность данных. ACID.
Что хранит:
cdr — CDR за последние 30 дней
active_calls — текущие звонки (Redis лучше, но PG как backup)
agent_sessions — рабочие смены агентов
queue_stats — текущая статистика очередей
TTL: cron-задача или pg_partman — автоудаление записей старше 30 дней (они уже в ClickHouse).
Оптимизации:
• Партиционирование по дате (1 партиция = 1 день)
• Индексы: caller_id, callee, start_time, agent_id, queue_name
• BRIN-индексы на start_time (эффективно для диапазонов дат)
Роль: вся история CDR за годы. Аналитические запросы, отчёты, дашборды.
Почему ClickHouse: колоночная СУБД — запросы вида «средняя длительность по очередям за Q4 2025» выполняются за 0.1-0.5 сек на 100M+ записей (PostgreSQL: 15-30 сек).
Что хранит: полную копию всех CDR за 3-5 лет. Сжатие 5-10x (100M CDR ≈ 8-15 GB).
Типичные запросы:
• «Все звонки за март в Германии» — 0.2 сек
• «Средняя длительность по очередям за квартал» — 0.1 сек
• «Top-10 агентов по количеству звонков» — 0.05 сек
• «Почасовая нагрузка за последний год» — 0.3 сек
• «Стоимость звонков по странам за месяц» — 0.1 сек
Запись: Go отправляет CDR батчами (каждые 5 сек или 1000 записей) через HTTP-интерфейс ClickHouse.
Роль: мгновенный поиск CDR по любому полю. Супервизор вводит номер телефона — за 50мс видит все звонки.
Почему Elastic:
• Full-text search — поиск по фрагменту номера: «+4917» → все звонки с номерами начинающимися на +4917
• Fuzzy matching — ошибся на цифру? Всё равно найдёт
• Агрегации в реальном времени — «сколько звонков за последний час по каждой очереди»
• Kibana — готовые дашборды без написания кода
Что индексируем:
caller_id callee agent_name queue_name hangup_cause direction country tenant_id
TTL: ILM-политика — горячий индекс (7 дней, SSD) → тёплый (30 дней) → удаление (90 дней). Для долгосрочного хранения — ClickHouse.
| Задача | Кто | Скорость |
|---|---|---|
| Текущие звонки агента | PostgreSQL | <10 мс |
| CDR с JOIN users/queues | PostgreSQL | <50 мс (30 дней) |
| «Все звонки за Q4 по Германии» | ClickHouse | 0.1-0.5 сек |
| «Среднее время ожидания за год» | ClickHouse | 0.05-0.2 сек |
| Ежемесячный отчёт (PDF) | ClickHouse | 1-3 сек |
| «Найди звонок с +4917612...» | Elasticsearch | <50 мс |
| «Все звонки агента Иванов» | Elasticsearch | <50 мс |
| Kibana дашборд (live) | Elasticsearch | real-time |
Go API: запись CDR в три хранилища
ClickHouse — схема таблицы CDR:
Elasticsearch — индекс CDR:
Примеры запросов: PostgreSQL vs ClickHouse vs Elasticsearch
«Текущие звонки агента 1001» → PostgreSQL (JOIN с users)
«Среднее время ожидания по очередям за Q4 2025» → ClickHouse
«Стоимость звонков по странам за январь» → ClickHouse
«Найди все звонки с номера +4917612...» → Elasticsearch
Шаг 1: Начинаем только с PostgreSQL (Фаза 5 базовая). Это работает сразу.
Шаг 2: Когда CDR перевалят за 1-5M записей и запросы начнут тормозить — добавляем ClickHouse. Go пишет в оба хранилища.
Шаг 3: Когда супервизорам понадобится мгновенный поиск и Kibana-дашборды — добавляем Elasticsearch.
Не нужно внедрять всё сразу. Каждый компонент добавляется когда появляется реальная потребность.
Фаза 6: Конференции + Voicemail
Конференции — для совещаний (несколько участников в одном звонке). Voicemail — когда оператор не ответил, клиент оставляет голосовое сообщение.
Создание комнат: набираем 8XXX → попадаем в конференцию 8XXX. Динамическое создание.
PIN-код: при входе вводится PIN для авторизации.
Управление: mute/unmute по DTMF (0 — mute себя), kick через ESL, запись конференции.
ESL: conference 8001 list — список участников. conference 8001 mute {member_id} — замутить.
Лимиты: максимум участников, автоокончание если остался 1.
Голосовая почта: если оператор не ответил за 20 секунд → «Оставьте сообщение после сигнала».
Greeting: персональное приветствие (запись своего) или стандартное.
Прослушивание: набираем *97 → вводим PIN → слушаем сообщения, удаляем, сохраняем.
Уведомление: email (SMTP) или webhook в Go API при новом сообщении.
MWI: индикатор на SIP-телефоне — мигающая лампочка «новое сообщение».
Фаза 7: Автообзвон (Dialer)
Вся логика дайлера — в Go, не в FreeSWITCH. FS только делает звонки по команде через ESL. Go управляет кампаниями, списками, стратегиями, пейсингом.
| Режим | Как работает | Когда | Ratio |
|---|---|---|---|
| Preview | Оператор видит карточку клиента на экране → сам нажимает «Позвонить» → Go отправляет ESL originate | Сложные продажи, VIP-клиенты. Оператор готовится к звонку | 1:1 |
| Progressive | Go ждёт свободного агента → originate на следующий номер из списка → при ответе bridge к агенту | Стандартный исходящий обзвон. Агент не простаивает, но и не перегружен | 1:1 |
| Predictive | Go оценивает average handle time + answer rate → originate заранее на N номеров одновременно → когда агент освободится, звонок уже ждёт | Массовый обзвон. Максимальная эффективность, но есть риск «брошенных звонков» (клиент поднял, а агент ещё не освободился) | 1.2–2:1 |
Campaign: название, список номеров, режим, расписание, Caller ID, trunk.
Pacing: сколько одновременных originate. Predictive подстраивает автоматически.
Retry: не ответил → повторить через 30 мин, максимум 3 попытки.
Blacklist / DNC: проверка номера перед звонком (Do Not Call).
Расписание: звоним только 9:00–18:00 по часовому поясу клиента.
Статистика: answer rate, average handle time, abandonment rate → dashboard.
mod_avmd — определяет, ответил человек или автоответчик.
Если автоответчик → оставляем голосовое сообщение (или вешаем трубку).
Если человек → bridge к оператору.
Точность: ~85-90%. Ложное срабатывание = потерянный клиент, поэтому лучше ошибиться в сторону «человек».
Альтернатива: не использовать AMD, а сразу bridge к агенту — агент сам разберётся. Проще, но агент тратит время на автоответчики.
Фаза 8: WebRTC + Видео
Финальная фаза — браузер становится полноценным телефоном. Оператору не нужен софтфон или аппаратный телефон — всё в веб-интерфейсе.
| Вариант A: mod_verto | Вариант B: SIP over WSS (через Kamailio) | |
|---|---|---|
| Как | Собственный протокол FreeSWITCH — JSON-RPC поверх WebSocket. Клиент: verto.js | Стандартный SIP через WebSocket (RFC 7118). Kamailio SBC терминирует WSS → UDP к FS. Клиент: SIP.js или JsSIP |
| Плюсы | Проще настроить. Прямое подключение к FS. Больше контроля | Стандартный протокол. Не привязан к FS. Kamailio SBC уже в архитектуре |
| Минусы | Привязка к FreeSWITCH. Нестандартный протокол | Сложнее: Kamailio WSS + RTPEngine + SRTP. Больше компонентов |
| Для нас | Быстрый старт, proof of concept | Production — SBC уже есть, стандартный SIP, масштабируемость |
Рекомендация Начать с mod_verto (быстрый proof of concept), потом перейти на SIP over WSS через Kamailio для production.
1. mod_verto — включаем, настраиваем WSS (порт 8082, TLS обязателен для браузеров).
2. Кодеки — Opus (голос, обязателен для WebRTC), VP8/VP9 (видео).
3. SRTP — обязательно для WebRTC (DTLS-SRTP). Браузеры не поддерживают обычный RTP.
4. React-интеграция — вебтелефон встроен в панель оператора: кнопки звонок/hold/transfer/mute, номеронабиратель, статус.
5. Видео — mod_conference с видео (VP8), screen sharing через extended SDP.
Открываем React-панель → нажимаем «Позвонить» на 1002 → браузер использует микрофон → звонок проходит → голос слышен в обе стороны. Если работает — все 8 фаз FreeSWITCH реализованы.
| Фаза | Модули | Результат |
|---|---|---|
| 1 | mod_sofia, mod_dptools, mod_dialplan_xml, mod_commands | Два телефона звонят друг другу |
| 2 | mod_event_socket, mod_lua, mod_json_cdr | Go управляет звонками через ESL |
| 3 | mod_lua, mod_flite, mod_tts_commandline, mod_say_ru | Входящие попадают в голосовое меню |
| 4 | mod_callcenter, mod_local_stream | Звонки распределяются по операторам |
| 5 | mod_dptools (record_session), mod_json_cdr, mod_shout | Запись + CDR в PostgreSQL |
| 6 | mod_conference, mod_voicemail | Конференции и голосовая почта |
| 7 | mod_avmd + Go ESL | Автообзвон списков |
| 8 | mod_verto / mod_sofia (WSS), mod_opus, mod_vp8 | Браузер = телефон |
После Фазы 1 — уже можно звонить. После Фазы 4 — уже полноценный call-center. После Фазы 8 — полный паритет с MirtaPBX (и больше). Миграцию пользователей можно начинать уже после Фазы 4-5.