Если попробовать загуглить sms шлюзы с открытым исходным кодом, то скорее всего вы найдёте Jasmin Gateway и Kannel. Про Kannel легко найти хорошие статьи в рускоязычном сегменте интернета, а вот Jasmin таким вниманием обделен, поэтому я решил сегодня рассказать немного про его устройство.
Если сделать короткую выдержку
- Написан на языке Python
- Реализует SMPP 3.4
- Доступен как пакет начиная с ubuntu 15.10
- Для своей работы использует rabbitMQ и redis
- Существенно однопоточен(есть отдельные части, которые теоретически могут работать в несколько потоков, но на практике это не случается)
- Изменение настроек обычно идёт с даунтаймом
- Реальная производительность 100 сообщений в секунду (ограничение на вход, исходящие может отправлять со скоростью в 1000 сообщений) (версия 0.9b)
- Умеет принимать на вход смс с http протокола
- Умеет работать и как SMPP клиент, и как SMPP сервер
Что Jasmin умеет:
- Принимать смс отправленные через smpp или http
- Пересылать полученные смс на внешний smpp сервис (например на компанию поствщика мобильных услуг)
- Получать и передавать дальше статистику по смс
- Поддерживает различные кодировки смс
- Поддерживает длинные смс(состоящие из нескольких сообщений)
- Возможность гибкой настройки переключения каналов отправки смс в зависимости от отправителя\содержания\времени
- Учёт финансов и объема смс трафика
Как устроен Jasmin
Сами разработчики описывают архитектуру Jasmin так:
Но я бы предложил посмотреть на архитектуру с точки зрения того, что происходит с сообщением, когда оно попадает на Jasmin сервер. Я рассмотрю путь для smpp сообщения, для http путь будет такой же:
- Слушая порт Jasmin получает сообщение. Он проверяет его на валидность исходя из контекста и, если оно не валидно, то отбрасывает его (тут кстати заключается одно из самых слабых мест Jasmin: почти все компании с которыми мне довелось работать не используют протокол Smpp как таковой, а используют какие-то свои решения, которые в отдельных моментах отличаются от протокола - такие места приходилось допиливать руками по месту).
- Распарсив сообщение Jasmin кладет его в очередь в rabbitMQ, для дальнейшей обработки и начинает дальше слушать порт.
- В другом слушателе(но в рамках того же потока), Jasmin получает сообщение из очереди и отдает его на intercept, если он настроен(intercept - это механизм в рамках которого вашему заранее заданному скрипту отдается сообщение, и скрипт может решить что с сообщением делать дальше: например можно отклонить, изменить получателя, или перенаправить в зависимоти от содержания сообщения. Самым большим минусом работы с intercept в Jasmin является то, что для изменения скрипта нужно сбрасывать перехватчик, что при большой нагрузке означает потерю части сообщений).
- После этого на основе получаенных команд от intercept и таблица оутинга jasmin вычисляет, куда нужно отправить сообщение, и кладёт его в отдельную очередь для каждого поставщика услуг по отправке смс
- В другом слушателе(но в рамках того же потока), Jasmin получает сообщение из очереди поставщика услуг и проверяет нужно ли по сообщению отдавать статистику, если да, то асинхронно записывает сообщение в redis, и отправляет сообщение
Получив же данные по статистике сообщения Jasmin делает следующее:
- Слушая порт Jasmin получает сообщение. Он проверяет его на валидность исходя из контекста и, если оно не валидно, то отбрасывает его.
- Распарсив сообщение Jasmin ищет в redis исходное сообщение, чтобы понять кому нужно отправить ответ. Сразу же после этого сообщение из redis удаляется. Если исходного сообщения нет, то ответ отбрасывается, иначе обратное сообщение кладется в очередь rabbitMQ.
- В другом слушателе(но в рамках того же потока), Jasmin получает сообщение из очереди и отправляет сообщение.
Оценка архитектуры
Все основное взаимодействие в Jasmin строится через rabbitMQ. С учётом возможностей асинхронной работы с ним, это позволяет достаточно сильно повысить производительность по сравнению с линейным однопоточным приложением. Так как Jasmin хранит в памяти только временную информацию (от отправки смс, до получения его статуса), то он весьма нетребователен к памяти.
С другой стороны, такая система передачи внутренних команд и сообщений может привести к достаточно сложно повторимым багам: мы получаем ответ на сообщение, до того как сообщение будет записано в redis(такой баг очень любопытно наблюдать и отлаживать). Похожие причины не дают разработчикам переписать код, на многопоточный вариант, без существенных доработок и усложнения архитектуры.
Отдельным плюсов для расширения Jasmin, является использование rabbitMQ и redis. Это позволяет подключать внешние модули и хранилища без необходимости каких-либо манипуляций с кодом Jasmin.
Выводы
В отличие от Kannel, Jasmin скорее всего не подходит для серьезной промышленной работы из-за своей низкой производительности. Однако Jasmin представляет собой отличную программу для экспериментов и создании своих программ на основе кода Jasmin.