Asterisk в примерах: балансировка каналов

от автора

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

К сожалению для системных администраторов, Asterisk недалеко ушёл от корпоративных АТС в плане простоты настройки. Безусловно, Asterisk может, пожалуй, всё, что только возможно вообразить, но ценой этому является далеко не тривиальная настройка.

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

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

Предполагается, что вы умеет базово настраивать Asterisk, знаете, как пользоваться диалпланом и т.д. Писать очередную статью для новичков, а-ля как поставить Asterisk, было бы глупо.

С другой стороны, полноценное решение проблемы балансировки исходя из количества минут предполагает настройку БД с ведением статистики по длительности разговоров, AGI скрипты и прочие достаточно сложные и тяжёлые навароты. Я же опишу более простое, хоть и не до конца всеобъемлющее решение, которое, тем не менее, крайне хорошо себя зарекомендовало.

Итак: есть две SIP линии, каждая ведёт на свой GSM канал. Операторы Tele2 и Мегафон. На Tele2 у нас 300 минут, на мегафоне — всего 150. Соответственно нужно, чтобы на Tele2 поступало в два раза больше звонков. Кроме этого исходящий вызов должен идти через незанятую симку, а если оба канала заняты — то вызывающему абоненту об этом должна сообщать добрая тётенька, которая к тому же должна предложить подождать освобождения.

Итак, в sip.conf имеет примерно такое описание линий:

; ################################ ; GSM каналы ; ################################ [gsm-lines](!) deny=0.0.0.0/0 permit=10.42.42.42/32 type=friend secret=******* qualify=yes host=dynamic callcounter=yes         ; активируем возможность использовать DEVICE_STATE call-limit=1                  ; максимум 1 соединение на линию group = 1 context = from-gsm  ; куда поступают входящие звонки insecure=invite canreinvite=no nat=no  ; МегаФон [gsmline1](gsm-lines)  ; Tele2 [gsmline2](gsm-lines) 

Важными тут являются параметры callcounter, call-limit и context. Думаю, с ними всё понятно.

Собственно, в extensions.conf указанный контекст описан так:

; Звонки с GSM линий [from-gsm] ; МегаФон (первая линия) exten => +79310000000,1,Set(GROUP(gsm)=publick)         ; Устанавливаем группу, дабы считать занятые каналы exten => +79310000000,n,Goto(to-internal,queue,1) ; Tele2 exten => +79520000000,1,Set(GROUP(gsm)=publick)         ; Устанавливаем группу, дабы считать занятые каналы exten => +79520000000,n,Goto(to-internal,queue,1) 

Безусловно, имена екстеншенов у вас будут другие (я использовал номера соответствующих телефонов, чтобы проще было). Они настраиваются на GSM шлюзе в настройках SIP подключения для каждого канала.

Тут нам важно, что для каждого входящего звонка устанавливается группа publick в категории gsm. Это нужно для подсчёта текущего количества занятых каналов.

Теперь самое интересное — исходящий контекст:

; Звонки на сотовые [to-gsm] ; Проверяем, есть ли свободные линии exten => _89XX.,1,GotoIf($["${GROUP_COUNT(publick@gsm)}" >= "2"]?noline) ; Вроде как есть свободные... exten => _89XX.,n,Set(GROUP(gsm)=publick)                       ; Устанавливаем группу, дабы считать занятые каналы ; Ok, проверяем - не мегафоновский ли номер вызываем? exten => _89XX.,n,Set(PR=${EXTEN:1:3}) exten => _89XX.,n,GotoIf($[$["${PR}"="921"] | $["${PR}"="931"] | $["${PR}"="929"]]?prefer-megafon) ; Распределяем нагрузку по симкам:  ; На Tele2 у нас 300 бесплатных минут, на МегаФоне - 150. ; Берём рандом от суммы, и если он меньше 300 - значит отдаём предпочтение tele2, инчаче - мегафону exten => _89XX.,n,Set(BALANCE=${RAND(0,450)}) exten => _89XX.,n,GotoIf($[${BALANCE}<=300]?prefer-tele2:prefer-megafon) ; Предпочитаем Tele2 и проверяем, не занята ли его линия exten => _89XX.,n(prefer-tele2),GotoIf($["${DEVICE_STATE(SIP/gsmline2)}" = "NOT_INUSE"]?tele2:megafon) ; Или всё же мегафон... exten => _89XX.,n(prefer-megafon),GotoIf($["${DEVICE_STATE(SIP/gsmline1)}" = "NOT_INUSE"]?megafon:tele2) ; Соединяемся с возможностью перевода и продолжением выполнения диалплана. exten => _89XX.,n(tele2),Dial(SIP/gsmline2/${EXTEN},120,Tg) exten => _89XX.,n,Goto(after-dial,${EXTEN},1)                   ; Переходим к пост-обработке звонка exten => _89XX.,n(megafon),Dial(SIP/gsmline1/${EXTEN},120,Tg) exten => _89XX.,n,Goto(after-dial,${EXTEN},1)                   ; Переходим к пост-обработке звонка ; Если все каналы заняты exten => _89XX.,n(noline),Set(__CALLED_GSM_NUM=${EXTEN})        ; Запоминаем вызываемый номер exten => _89XX.,n,Goto(ivr-gsm,no-line,1) ; Если повесил трубку вызывающий абонент нам надо сделать постобработку exten => h,1,Goto(after-dial,h,1) 

В целом, для всех операций присутствуют комментарии. За само распределение отвечает хитрый тюк с рандомом. Сразу скажу — на практике такое, весьма приближённое, решение для балансировки показало результаты не хуже, чем полноценная БД с подсчётом продолжительности каждого звонка. Вы ведь покупаете бесплатные минуты с запасом, я надеюсь? Т.е. вам всего лишь нужно примерно соблюдать баланс, что с успехом и делает предложенное решение.

Контекст after-dial нужен для пост-обработки звонка. Там могут быть всякие действия с записью, тем же подсчётом длительности и т.д. Для данной статьи это всё неактуально.

А вот ivr-gsm представляет некий интерес:

; IVR для GSM линий [ivr-gsm] ; Проигрываем предупреждение об отсутсвии свободных линий и ждём exten => no-line,1,Background(no-gsm-line,,custom) exten => no-line,n,Wait(10) ; Если есть запомненный номер - снова пытаемся его вызвать exten => no-line,n,GotoIf($["${CALLED_GSM_NUM}" != ""]?to-gsm,${CALLED_GSM_NUM},1) ; Нету - просто тихо выходим exten => no-line,n,Hangup() 

В файлике no-gsm-line девушка должна говорить приятным голосом примерно следующее: Все исходящие линии заняты. Можете подождать или попробовать перезвонить позже.

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

Если будет заинтересованность — могу продолжить серию и выложить, например, конфигурацию для цифрового факса.

Нужны ли подобные мини-статьи с краткими примерами диалплана?

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Никто ещё не голосовал. Воздержавшихся нет.

ссылка на оригинал статьи http://habrahabr.ru/post/180445/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *