Технологии VoIP имеют репутацию достаточно сложных и не без уважительных причин. Однако это не означает, что любому разработчику среднего уровня будет очень трудно найти и обработать один из многих доступных серверов с открытым исходным кодом. Единственная проблема заключается в отсутствии информации и реальных примеров, поскольку домашние страницы VoIP-сервера редко имеют конфигурации, выходящие за рамки самого простого варианта, и даже если они это делают, они могут быть устаревшими.
Вам также может понравиться: Масштабируемость приложений — Как сделать эффективное масштабирование
Реализовав более десятка сложных VoIP-проектов, я постараюсь обобщить основные принципы и компоненты отказоустойчивой масштабируемой VoIP-системы. В этой статье основное внимание уделяется различным вариантам архитектуры и тому, как все это работает под капотом, с несколькими реальными примерами конфигурации, включенными для хорошей меры.
Введение в VoIP сервер
Если такие имена, как FreeSWITCH, Asterisk, SIP, RTP, WebRTC , не являются для вас просто пустяком, смело пропускайте эту часть.
Мир VoIP стоит на двух основных столпах: SIP и RTP. Это два протокола, которые были разработаны в конце прошлого века и пришли к нам из мира телефонии. SIP (Session Initiation Protocol) — это командный протокол, который отвечает за выбор кодеков, начало / конец вызова, управление вызовом (удержание / передача / и т. Д.) И имеет огромное количество расширений, включая отправку текстовых сообщений, уведомление левых голосовых сообщений и т. д.
IP-телефоны различных производителей могут поддерживать собственный набор расширений. Например, BLF (Busy Lamp Field) очень распространено — поле светодиодов на настольном телефоне, которое можно настроить так, чтобы они отображали состояние телефона другого сотрудника.
RTP ( транспортный протокол реального времени ) — это медиапроток, который отвечает за передачу аудио- и видеоданных. Он использует различные кодеки для кодирования медиа-данных.
Оба протокола реализованы по умолчанию через UDP. Почти у каждого есть TCP в качестве опции, но UDP лучше по нескольким причинам. Шифрование доступно для обоих протоколов.
Архитектура VoIP основана на идее, что серверы SIP и RTP — это два разных сервера, поэтому существуют реализации серверов, которые поддерживают только SIP или только RTP. Тем не менее, FreeSWITCH и Asterisk являются серверами с открытым исходным кодом, которые поддерживают оба этих протокола, а также некоторые другие. Выбор между ними во многом зависит от личных предпочтений, требований и задач интеграции, но оба они позволяют вам получить офисную АТС из коробки.
И последнее , но не в последнюю очередь WebRTC (Web Real-Time Communication) . Это де-факто стандарт для голосовых вызовов в Интернете. WebRTC использует SRTP (безопасный RTP), оставляя реализацию командного уровня во власти кода JS. Для вызова p2p все, что вам нужно сделать, это передать свой адрес и параметры вызова другой стороне, что можно сделать на основе любого протокола. Если вам требуется интеграция с SIP-сервером, обычно используется протокол « SIP over WebSocket ». Реализация — sipjs.com.
FreeSWITCH поддерживает как SIP over WebSocket, так и его альтернативный протокол, реализованный модулем mod_vertoo, разработанный специально для интеграции с WebRTC, который устраняет любые возможные неприятности, возникающие из-за несовместимости WebRTC и SIP, которые в противном случае привели бы к задержке в 1-5 секунд перед вызовом , Существует несколько способов решения этих проблем на стороне JS, но это тема отдельной статьи.
Как и при обычных аналоговых вызовах, для VoIP требуется УАТС, чтобы пользователи могли найти друг друга по определенному номеру телефона и преодолеть проблему, вызванную отсутствием белого IP-адреса клиента. После того, как клиенты нашли друг друга и согласовали кодеки, необходимо установить соединение для медиапотока. В зависимости от требований проекта и конфигурации сети поток данных может идти либо напрямую от клиента к клиенту, либо через сервер.
Мир VoIP обычно стремится пересылать трафик через сервер. Это решает проблему отсутствия белого IP-адреса для клиентов и позволяет централизованно записывать любой разговор.
По умолчанию WebRTC предлагает прямое соединение между клиентами, но это не работает в случае асимметричных межсетевых экранов, когда оба клиента не имеют белого IP-адреса. В этих случаях требуется медиа-прокси-сервер STUN / TURN. Для коммерческих проектов вам придется установить и оплатить прокси-сервер, чтобы ваше приложение на основе WebRTC работало. Браузеры предоставляют только услуги сервера STUN бесплатно (распознавание внешнего IP-клиента), но прокси-сервер трафика (TURN) является платной функцией.
Примечание . Существует технология, которая, по крайней мере на бумаге, позволяет подключиться к двум клиентам без белых IP-адресов с небольшой помощью сервера, но, по нашему опыту, этого не произошло. Если вам интересно, попробуйте поискать «симметричный NAT».
Балансировка нагрузки SIP с помощью OpenSIP
FreeSWITCH и Asterisk позволяют обрабатывать огромное количество одновременных вызовов на одной и той же машине, от 500 до 1000. Но что, если нам нужно больше? Или, если есть необходимость в балансировке нагрузки на основе облака?
Вот тут-то и приходит на помощь балансировщик нагрузки SIP и вот как это работает. Один компьютер обозначен как балансировщик, который на уровне SIP выбирает конечный узел и перенаправляет на него всю обработку мультимедийного трафика. Медиапоток RTP не проходит через балансировщик, освобождая балансировщик, который теперь способен выдерживать значительно большее количество одновременных вызовов. Как только вызов создан, загрузка фактически исчезает.
Выбор балансировщика нагрузки обычно лежит между OpenSIP и Kamailio. Оба проекта имеют общего предка и похожую модульную структуру. Мы оставим сравнение и выбор конкретного решения для отдельной статьи, а пока остановимся на OpenSIP.
Многие уже имели некоторый опыт работы с FreeSWITCH и / или Asterisk, в Интернете достаточно примеров с практическими рекомендациями, но настройка балансировки SIP в OpenSIP или Kamailio совершенно другая.
FreeSWITCH и Asterisk требуют только списка пользователей и довольно простых правил маршрутизации вызовов. Кроме того, есть бесплатные админ-панели, которые позволяют настроить все в веб-интерфейсе.
Напротив, OpenSIP и Kamailio требуют, чтобы пользователь понимал протокол SIP хотя бы на начальном уровне и вручную прописывал, что делать с каждым сообщением SIP.
Конфигурация OpenSIPs — это программа на высокоуровневом псевдо-языке, которая должна объединить более 10 различных модулей в одну систему. Список модулей намного больше, но его части дублируют друг друга, и, возможно, не все они нужны.
В программе вы указываете, как обрабатывать каждое входящее SIP-сообщение и что отправлять в ответ. Однако основная рабочая нагрузка назначается подключаемым модулям, и вам просто нужно настроить большой коммутатор, когда и какой модуль вызывать.
Если вы хотите добавить много пользовательской логики, в какой-то момент может быть проще перенести бизнес-логику на FreeSWITCH (Asterisk) и / или на отдельный сервер, который будет управлять маршрутизацией вызовов, очередей и т. Д.
Простейший скрипт балансировки нагрузки выглядит следующим образом:
- Если входящее SIP-сообщение не первое и уже понятно, куда его направить, вы можете передать управление диалоговому модулю.
- В противном случае вызовите модуль балансировки, чтобы найти узел с более низкой нагрузкой, но его адрес в цели и передать управление диалоговому модулю.
- Обработка ошибок:
- Выбранный узел может вернуть код ошибки (аналогично HTTP-коду), и вам необходимо указать, что делать в каждом случае — отбросить вызов или попытаться выбрать другой узел.
- Модуль балансировки может вернуть ошибку и сказать, что свободных узлов нет.
Примечание. Когда вы пишете программы обработки для OpenSIP, помните, что вы должны обрабатывать как сообщения, поступающие извне в облако, так и сообщения, поступающие из облака извне. Вот почему программа часто делится на 2 или 3 огромных блока If в зависимости от направления пакета SIP.
пример
Для краткости мы исключаем всю обработку ошибок. Полную версию можно найти здесь .
Джава
1
route {
3
if (! has_totag ()) {
5
# The first message of the dialogue, save it to the database and do not exit, you need to find where to route it
6
record_route ();
7
} else {
8
# Not the first message, drive it along the already chosen route and exit
9
loose_route ();
10
t_relay ();
11
exit
12
}
14
// Looking for a free node. 1 - weight of our request, call-resource. You can create a more complex logic and select a different set of machines for different calls
16
load_balance ("1", "call");
18
if ($ retcode <0) {
20
// no free nodes
21
sl_send_reply ("500", "Service full");
22
exit
23
}
25
xlog ("Selected destination is: $ du \ n");
27
if (! t_relay ()) {
29
sl_reply_error ();
30
}
31
}
33
34
35
Особенности OpenSIP:
- Значения заголовка не изменяются во время выполнения программы. Если вы назначите что-то, а затем попытаетесь вывести данные в журнал, будет отображаться начальное значение.
- Существует 2 различных типа пользовательских переменных, и они несовместимы. Некоторые из модулей используют первый тип, другие используют второй.
- Хотя многие функции ничего не возвращают, они часто делают что-то полезное, и вы узнаете, что именно, прочитав всю документацию, а иногда и исходный код.
- Например, load_balance () помещает значение переменной $ du. Record_route () сохраняет $ du в заголовке сообщения, но он вызывается ранее load_balance (). Явное нарушение причинно-следственной связи. Мы предполагаем, что заголовки рассматриваются в самом конце, и record_route () с большей вероятностью поставит определенный флаг или ссылку.
- Парадигма без гражданства. SIP позволяет хранить все данные, необходимые для маршрутизации, непосредственно в заголовках сообщений. Однако клиент обязан копировать их при ответе, что позволяет серверу не сохранять состояние. Тем не менее, если необходимо, вы можете сохранить таблицу маршрутизации в базе данных, не уведомляя всех и вся о внутренней структуре вашего кластера, но это увеличит нагрузку на базу данных и вызовет задержки. Выбор ваш.
Отказоустойчивость и масштабирование VoIP
Отказоустойчивость и масштабирование тесно связаны. Невозможно создать архитектуру отказоустойчивости без учета схемы масштабирования. Все, что вы можете сделать, это реализовать отказоустойчивость в простейшей версии. Для начала немного теории.
На уровне протокола VoIP поддерживает восстановление вызовов даже в случае сбоя сервера.
- Ошибка SIP. По умолчанию SIP использует UDP, который, в отличие от TCP, не устанавливает постоянное соединение. Но клиент перерегистрируется примерно каждую минуту (можно настроить). Новый сервер может отвечать на каждый пакет от клиента. Единственное ограничение заключается в том, что IP-адрес ответного пакета не должен изменяться, и нам нужно где-то хранить состояние вызовов пользователя (если они существуют в данный момент), чтобы любой сервер мог работать с этими данными.
- Ошибка RTP . Если по какой-либо причине ваш сервер RTP, который также использует протокол UDP, вышел из строя, вы не можете легко восстановить поток мультимедиа, но вы можете отправить команду reinvite через протокол SIP. В идеале клиент заметит только несколько секунд потери звука. Реализация обнаружения падения остается вопросом. На момент написания статьи не существует коробочного решения.
Давайте рассмотрим различные варианты архитектуры.
SIP Failover
Детали реализации сильно различаются в зависимости от используемого сервера, остается только одно: потребность в виртуальном (плавающем) IP, который мы можем динамически переназначить другому серверу.
Проблема решается на уровне сценариев оболочки, которые используются для проверки работы сервера и, если нет, для переназначения IP-адреса другому. Основная проблема - это баланс между ложными срабатываниями и долгим ожиданием перед переключением.
Но это не все. Сервер SIP хранит информацию о подключенных клиентах, и чтобы не потерять ее, необходимо настроить свой сервер для хранения этой информации в распределенной базе данных. Тогда в случае перезапуска или переключения на ведомый мы не потеряем никаких данных. Почти все популярные SIP-серверы поддерживают использование блочных баз данных.
Sip Scaling
Правильно выполненное масштабирование неизбежно решает проблему отказоустойчивости. Один сервер умер - просто возьми другой.
Основная проблема заключается в том, что клиент SIP игнорирует пакеты, отправленные с другого IP. Вот пример того, что не будет работать :
Клиент B будет просто игнорировать входящий вызов (приглашение), поскольку его исходный IP-адрес отличается от сервера, на котором зарегистрирован клиент.
Что будет работать?
В зависимости от конкретного проекта, доступного оборудования и возможностей существует три возможных решения проблемы.
Переопределить исходный IP
Самый простой вариант - установить еще более простой (и более быстрый) балансировщик нагрузки, который заменит исходный IP-адрес вашим собственным для всех исходящих пакетов. Скрыть все SIP-серверы за ним. Нам нужно настроить все SIP-серверы на использование общей базы данных.
Очевидным преимуществом здесь является простота настройки.
Очевидные минусы:
- Единственная точка отказа как балансировщик нагрузки.
- Может быть недостаточно пропускной способности балансировщика нагрузки.
Переслать Приглашение
Другой вариант - настроить ферму SIP-серверов с разными IP-адресами, DNS в циклическом режиме. Клиенты случайным образом цепляются за один из многих серверов.
Запрограммируйте сервер SIP на пересылку приглашения на нужный сервер (на котором зарегистрирован другой клиент), чтобы он отправлял команду клиенту от своего имени.
Плюс - все масштабируется замечательно.
Небольшой минус - регистр SIP хорошо масштабируется, тогда как SIP-приглашение и другие команды, связанные со шкалой вызовов, немного хуже, поскольку, по всей вероятности, они будут проходить через два узла. При этом, «регистрация» происходит гораздо чаще, чем «пригласить».
Sharding
Если ваше приложение SaaS для бизнеса, то весьма вероятно, что клиентам разных компаний не нужно звонить друг другу, используя правильно выбранную хэш-функцию, и мы можем распределить многих наших клиентов по разным SIP-серверам.
Вам даже не нужно иметь общую базу данных регистрации SIP. Но вам нужно как-то сообщить клиенту SIP адрес нужного сервера SIP и выяснить, что делать, если он умирает (возможно, переключиться на другой сервер). Если речь идет о решении UC, то, как правило, SIP-клиент встроен в бизнес-приложение и получает все настройки с сервера - поэтому проблем нет.
Ситуация хуже, если мы используем чисто SIP-клиент. Тогда имеет смысл решить проблему на уровне DNS и / или маршрутизации. Например, присвойте каждой компании свой адрес VoIP-сервера и динамически измените таблицы DNS.
RTP Fail Over
В общем, вам нужно отслеживать сбой RTP-сервера и отправлять повторное посещение обоим клиентам.
Эта задача разделена на несколько подпунктов:
- Сохраните диалог SIP в базе данных.
- Отслеживание смерти сервера.
- Отправить повторное приглашение для каждого разговора.
Детали реализации сильно различаются в зависимости от конфигурации кластера.
Как пример: FreeSWITCH имеет встроенную команду «sofia recovery» для восстановления вызовов после перезапуска сервера. Но, судя по отзывам, эта команда не будет работать в случае кластера серверов FreeSWITCH. Здесь вам, вероятно, понадобится индивидуальная разработка.
RTP Scaling
Очевидный способ масштабирования медиапотоков - разместить их на сервере отдельно от трафика SIP. Ниже мы обсудим варианты архитектуры различной сложности, от самых простых до более сложных.
OpenSIPs | Kamailio + RTP прокси
Балансировщик нагрузки SIP, представленный OpenSIP или Kamailio, управляет RTP-прокси или его эквивалентом. Связь между ними осуществляется по определенному протоколу (не SIP), в рамках которого балансировщик нагрузки должен иметь возможность запрашивать распределение портов для протокола RTP для отправки этих данных клиентам. Клиент открывает новое соединение по полученному IP и порту, подключаясь к прокси RTP.
Вся бизнес-логика сосредоточена в балансировщике нагрузки SIP. Решение подходит, если у вас очень мало бизнес-логики, и все, что вам нужно, это переключать вызовы.
Нет необходимости использовать SIP-сервер. Один из наших проектов переключения для 911 был реализован на Java:Система маршрутизации звонков
OpenSIPs | Kamailio + FreeSWITCH | звездочка
Балансировщик нагрузки SIP, представленный OpenSIP или Kamailio, перенаправляет пакеты SIP через себя в выбранный FreeSWITCH | Звездочка сервера. Самая близкая аналогия - балансировщик нагрузки HTTP с липкими сессиями. Сам OpenSIP обрабатывает только регистрацию.
При инициализации нового диалога (вызова) он выбирает наименее загруженный узел FreeSWITCH в соответствии с некоторой стратегией и перенаправляет на него все пакеты SIP, связанные с этим диалогом. Сам FreeSWITCH, обрабатывая клиентские SIP-пакеты, выделяет порт RTP и отправляет клиенту номер порта и его белый IP через OpenSIP.
Имеет смысл перенести бизнес-логику во FreeSWITCH | Звездочка, которая гораздо больше подходит для этого и имеет большой набор различных модулей из коробки.
Это решение лучше подходит для систем с IVR, голосовой почтой, Call Center и другими функциями современных систем УАТС. (Пример из моего опыта: система унифицированных коммуникаций )
OpenSIPs | Kamailio + FreeSWITCH + Сервер приложений
Решение во многом похоже на предыдущее, но вся бизнес-логика размещена на сервере приложений. Такой подход устраняет необходимость использования сценария Lua, но добавляет потенциальную точку отказа.
С другой стороны, если у вас уже есть сервер приложений, и он уже является жизненно важным элементом вашего решения Unified Communication, то почему бы не использовать его? FreeSWITCH имеет модуль xml_curl, который позволяет загружать всю конфигурацию через HTTP со стороннего сервера. Для каждого запроса, будь то авторизация или входящий вызов, FreeSWITCH сначала запрашивает инструкции HTTP XML, а затем выполняет их. Остальная часть схемы проходит по аналогичной линии.
Дальнейшее чтение
Как создавать масштабируемые приложения
Если вы научитесь создавать масштабируемое приложение, вы сможете изменить свою карьеру