Будущий дизайн OpenSIPS

OpenSIPS — это сигнальный SIP-коммутатор. Если вы хотите обрабатывать реально много SIP-звонков, то, скорее всего, мимо OpenSIPS не пройдете.

Система реально «mature», проверенная в бою и, со временем, обросшая множеством полезных (и не очень) модулей.

Вместе с этим, очевидно, что архитектура, заложеннная еще в 2001 году не отвечает современным требованиям.

Ниже приведен перевод документа » OpenSIPS 2.0 Design «. Интересно, что думает хабрасообщество по этому поводу.

Текущая архитектура OpenSIPS (до версии 2.0) основана на концепциях, которым более 7 лет. В то время требования были простыми (простой stateless SIP-прокси, только UDP) и решения принимались в соответствии с этими требованиями. Но со всеми дополнениями, как в SIP так и функционале (таком как TCP/TLS, манипуляции в скрипте, поддержка диалогов, интеграция с внешними системами и т.д.), существующая архитектура больше не может удовлетворять требованиям и реальным сценариям использования.

Блокировки I/O (транспорт, DB, приложения) Масштабирование при помощи аппаратных ресурсов (ввод-вывод и синхронизация параллельных процессов являются узким местом в нынешней архитектуре) При разработке конфигурации приходится иметь дело с низкоуровневыми функциями (TM, диалоги, NAT) вместо того, чтобы сосредоточиться на логике предоставления услуг. Горизонтальное масштабирование (как ядра, так и логики маршрутизации) Механизм маршрутизации (в виде специализированного языка программирования) имеет очень ограниченный набор возможностей не связанных с SIP (интеграция с другими приложениями, сложна логика сценария маршрутизации, работа с массивами и списками, поддержка сложных операций и типов данных) и требует дополнительных навыков от администратора. Механизм маршрутизации слишком тесно связан с SIP-стеком, это приводит к тому, что его невозможно изменить на ходу без перезапуска OpenSIPS. Невозможно сделать распределенную маршрутизацию между несколькими системами (для улучшения масштабируемости).

Новая архитектура была разработана с учетом проблем и удачных решений в текущей архитектуре, а также учитывает направления развития SIP и OpenSIPS.

Предполагается, что несколько серверов маршрутизации смогут подключаться к одному Ядру, чтобы реализовать разные функциональные возможности, или просто чтобы увеличить мощность системы в целом.

Ядро — это низкоуровневый компонент, который обеспечивает работу функций, связанных с SIP. Они могут выполняться автоматически и не требуют сложной конфигурации. Core будет отвечать за транспортный уровень, анализ пакетов, транзакции и диалоги, за автоматическую поддержку прохода через NAT, SIP-регистрации и онлайн-статусы (презенс), функции, которые четко определены в RFC и не требуют какого-либо вмешательства со стороны высокоуровневой Подсистемы маршрутизации.

SIP-собщение поступает на нижний уровень L. Каждый уровень обрабатывает сообщения и либо передает его на следующий уровень, либо возвращает его на уровень ниже. Если происходит возврат, это говорит о том, что обработка сообщения закончена на этом уровне (например, так работают автоматические ответы на KeepAlive-сообщения или повторная отправка пакетов). Когда сообщение достигает слоя, который обеспечивает взаимодействие с сервером маршрутизации, сообщение передается (со всеми изменениями, которые проводились на каждом уровне) на сервер маршрутизации, который продолжает дальнейшую обработку сообщения. Затем сообщение возвращается обратно по пути, который идет сверху вниз от уровня к уровню, пока не достигнет транспортного уровня (L) и не будет отправлено в сеть.

Читайте также:  «Богатые — рабы своих предприятий»

В обоих потоках каждый уровень может выполнять различные действия (например, уровень SIP-анализатора разбирает сообщения из входящего потока и собирает обратно, помещая их в исходящий поток).

SIP-сообщения читаются из сети в сеть уровнем L(транспортный уровень), и, затем, они передаются на вышестоящие уровни. Каждый слой выполняет соответствующие действия: например, уровень SIP-анализатора добавит к сообщению данные в разобранном формате, уровень транзакций добавит идентификатор транзакции, уровень диалогов добавит идентификатор диалога и т.д. После выполнения своей задачи, уровень может: (а) передать сообщение на вышестоящий уровень для обработки (если текущий уровень не может завершить обработку) или (б) принять решение, что делать с сообщением и передать сообщение в исходящий поток для отправки (в данном случае, все вышестоящие уровни будут пропущены).

Такой алгоритм обеспечивает максимально эффективную обработку сообщений на каждом уровне. Нет необходимости передавать все сообщения через все уровни до Подсистемы маршрутизации, если сообщение может быть обработано автоматически с помощью нижестоящих уровней. Например ответы на keepalive-сообщения могут быть автоматически обработаны Ядром на уровне L4 (прохода через NAT). Другой пример, когда Подсистема маршрутизации заинтересована лишь в получении и обработке первоначальных запросов. В этом случае последующие запросы будут автоматически обрабатываться и маршрутизироваться при помощи уровня диалогов без необходимости передавать их дальше по цепочке.

В дополнение к уровням, Ядро также реализует несколько бэкэндов баз данных. Они используются для сохранения между перезапусками внутренних данных, которые каждый из уровней Ядра хранит в памяти (диалоги, онлайн-статусы, регистрации, информация для прохода через NAT).

Ядро имеет свой собственный файл конфигурации, который содержит параметры для основных уровней и бэкэндов баз данных: на каких сетевых интерфейсах слушать, параметры транзакций, параметры диалогов и т.д.

Ядро реализовано, как асинхронный реактор, который поддерживает неблокирующий ввода/вывод. Для эффективного использования ресурсов на машинах с многоядерными процессорами, Ядро использует несколько потоков (по количеству ядер CPU).

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

Читайте также:  Уголовное дело по обвинению должностных лиц КГГА, незаконно выделивших земельные участки, передано в суд

Подсистема маршрутизации работает поверх Ядра, как отдельный компонент. Как упоминалось ранее, к одному Ядру может подключаться несколько Подсистем маршрутизации — либо для реализации различных сервисов, либо для повышения пропускной способности системы за счет распределения нагрузки на несколько физических машин.

Зачем использовать два подхода? Эти два варианта не исключают, а дополняют друг друга — внутренний Сервер маршрутизации более эффективен в плане производительности и управляемости (за счет тесной интеграции с Ядром непосредственно на уровне C), тогда как внешняя Подсистема маршрутизации будет гораздо более универсальным и простым в реализации решением (будучи приложением на языке высокого уровня), которое гораздо проще контролировать.

Интерпретатор скрипта основывается на существующем специализированном языке с возможностью непосредственно вставлять блоки на языке высокого уровня (например, встраиваемый Perl). В зависимости от степени влияния на производительность, можно полностью заменить специализированный скрипт на язык высокого уровня (например всю логику Модуля маршрутизации написать на Perl из которого вызываются функции Ядра или модулей на C).

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

Встраивание сценариев на высокоуровневых языках также выглядит полезным, поскольку это решение существенно упрощает реализацию сценариев, где требуется сложная логика маршрутизации требуется, которая может быть реализована с использованием возможностей языка высокого уровня. Кроме снижения трудоемкости по написанию сложных скриптов эта возможность избавляет от необходимости изучать специализированный встроенный скриптовый язык. Однако, в этом случае будет происходить некоторое снижение производительности, поскольку интерпретаторы языков высокого уровня тяжелее и медленнее. С другой стороны, использование языков высокого уровня позволит добиться горизонтального масштабирования. Представьте себе кластер OpenSIPS (на нескольких серверах), где все Подсистемы маршрутизации (использующие особенности языка высокого уровня и технологии хранения данных) соединены в сеть и ведут себя, как единое целое — как глобальный Сервер маршрутизации, который использует несколько ядер OpenSIPS.

Модули Подсистемы маршрутизации, которые обеспечивают предопределенные функции (dialplan, LCR, QoS, B2BUA т.д.), могут быть использованы из скрипта, независимо от его формата.

Так же как и встроенная, внешняя Подсистема маршрутизации (или приложение) реализована в виде набора уровней, через которые сообщения проходят в двух направлениях (снизу вверх и сверху-вниз). Эти уровни завершаются уровнем, реализующем логику маршрутизации для конкретной SIP-службы. Последний слой является эквивалентом скрипта, который используется в старой архитектуре.

Некоторые из уровней являются активными — это означает, что они изменяют сообщения в ходе передачи и, возможно, принимают решения прекратить ретрансляцию сообщений наверх, завершить обработку и вернуть сообщение вниз. Это делается либо на основе анализа содержимого сообщения, либо на основе инструкций от предыдущего уровня маршрутизации. Некоторые другие уровни пассивны. Это означает, что они только предоставляют классы и функции, которые осуществляют определенные возможности (например, LCR или управление вызовами), но они не используются для сквозной передачи сообщений между уровнями наверх. Они вызываются явно из логики маршрутизации и изменяют сообщение.

Читайте также:  По российским рекам поплывут ...отели

Приложение регистрируется в определенном Ядре (или на нескольких Ядрах, если это нужно) и сообщает о своих возможностях Ядру. Возможности включают в себя сообщения, в которых заинтересована данная Подсистема маршрутизации и ограничения. Это позволяет Ядру фильтровать, какие сообщения и как много, передавать в данное приложение. Кроме того, приложение может запросить Ядро динамически включать/отключать некоторые из опциональных уровней Ядра, когда Ядро решает, что сообщение должно быть направлено к данному приложению. Это позволяет создавать гибкие конфигурации, в которых определенное приложение может, например, сказать Ядру, что для всех сообщений, которые передаются в конкретное приложение, отключить уровень прохождения через NAT. В результате, когда Ядро передает сообщение в данное приложение, оно будет пропускать уровень прохода через NAT. Это означает, что приложение хочет иметь дело с проходом через NAT самостоятельно. Это также означает, что приложение будет получать keepalive-сообщения. Другие приложения, если они не отключили этот уровень, не будут видеть keepalive-сообщения и не будут решать проблему прохода через NAT, потому что уровень прохождения через NAT будет использоваться. Это позволяет получить очень гибкую конфигурацию, которой где некоторые опциональные уровни Ядра могут быть включены или выключены во время выполнения без необходимости изменения конфигурации Core или перезагрузки Ядра.

Приложение целиком может быть написано на языке высокого уровня, таком как Python. Реализация в OpenSIPS предоставит все необходимые функции, которые позволят пользователю писать высокоуровневую логику маршрутизации. Это соответствует последнему уровню на схеме приложения. Это будет походить на существующий скрипт, только это будет написано на том же языке (Python в данном случае), что и остальное приложение. Конечно, приложение может быть реализовано и на других языках, таких как Java, Ruby или Perl.

Источник: habrahabr.ru