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

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

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

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

Комментарии по существу я постараюсь передать разработчикам.

Зачем нужна новая архитектура

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

Внимание! Внутри большой и структурированный текст с картинками.


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

Обзор новой архитектуры

Новая архитектура была разработана с учетом проблем и удачных решений в текущей архитектуре, а также учитывает направления развития SIP и OpenSIPS.
На верхнем уровне OpenSIPS будет состоять из двух полностью независимых частей:
  • SIP-ядро (SIP-core) — обеспечивает поддержку низкоуровневых операций с SIP
  • Подсистема маршрутизации (Routing Engine) — отвечает за высокоуровневую логику маршрутизации

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

SIP-ядро

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

Ядро разделено на несколько горизонтальных уровней, каждый из которых выполняет определенные функции:
  • L 0 — Транспортный уровень (Transport layer). Реализует поддержку UDP, TCP, TLS, SCTP и скрывает все специфические особенности для транспорта
  • L 1 — Уровень SIP-анализатора (SIP parser layer). Отвечает за разбор SIP-сообщений
  • L 2 — Уровень транзакций (Transaction layer) — реализует поддержку SIP-транзакций. Каждое приходящее SIP-сообщение сопоставляется с SIP-транзакцией
  • L 3 — Уровень диалогов — реализует поддержку SIP-диалогов. SIP-сообщение может быть сопоставлено с SIP-диалогом
  • L 4 — Уровень прохода через NAT — автоматический обработчик для прохода через NAT (поддержка на уровне SIP-сигнализации и транспорта)
  • L 5 — Уровень поддержки онлайн-статусов (презенс) — реализует SIP Presence Agent. Автоматическая обработка сообщений, связанных с онлайн-статусами.
  • L 6 и выше… по мере необходимости могут быть добавлены другие уровни (такие как user location).
  • L п — последний уровень — это уровень, который обеспечивает взаимодействие с Подсистемой маршрутизации (Routing Engine) (через API или сокет)

image

Структура ядра

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

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

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

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

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

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

Внутри Ядра уровни можно разделить по важности:
  • Базовое ядро — включает в себя обязательные уровни (L0-L3 и Ln). Они обеспечивают функции, которые требуются всегда.
  • Дополнительные уровни (L4 — Ln-1), которые обеспечивают необязательные функции. Они могут подключаться или отключаться во время работы.

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

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

Подсистема маршрутизации (Routing Engine)


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

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

Новый дизайн позволяет использовать два подхода для соединения Ядра с Подсистемой маршрутизации:
  • Логика маршрутизации в виде библиотеки, которая тесно связана с Ядром, и реализует несколько дополнительных уровней и модулей поверх Ядра. Они соединены между собой внутри одного исполняемого файла.
  • Логика маршрутизации в виде внешнего приложения, которое общается с Ядром, используя сокет. Это приложение может быть написано на языке высокого уровня, например Python или Java.

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

Внутренний модуль маршрутизации

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

image

Внутренняя подсистема маршрутизации

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

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

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

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

Есть возможность перезагрузить скрипт во время работы.

Внешняя подсистема маршрутизации

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

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

image

Подсистема маршрутизации как приложение

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

image

Несколько приложений с одним ядром

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

Каждый из нас может помочь проекту OpenSIPS.

Спасибо за внимание.

Who's online

There are currently 0 users and 2 guests online.