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

  • Tutorial
Всё больше и больше организаций выбирают для телефонии не астрономически дорогие, жутко запутанные и ограниченные по функционалу готовые 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)=public)         ; Устанавливаем группу, дабы считать занятые каналы
exten => +79310000000,n,Goto(to-internal,queue,1)
; Tele2
exten => +79520000000,1,Set(GROUP(gsm)=public)         ; Устанавливаем группу, дабы считать занятые каналы
exten => +79520000000,n,Goto(to-internal,queue,1)

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

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

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

; Звонки на сотовые
[to-gsm]
; Проверяем, есть ли свободные линии
exten => _89XX.,1,GotoIf($["${GROUP_COUNT(public@gsm)}" >= "2"]?noline)
; Вроде как есть свободные...
exten => _89XX.,n,Set(GROUP(gsm)=public)                       ; Устанавливаем группу, дабы считать занятые каналы
; 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 в случае занятости желаемой линии надо отправлять на некий экстеншн, который просто будет искать первую незанятую.

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

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 15

    0
    великолепно!
      0
      Первое время знакомства с asterisk, не знал как правильно балансировать исходящие линии, делал обычный перебором по порядку, ограничивая параметром call-limit. Позже нашел правильный вариант со счетчиком(как у вас). Хочу добавить что таким вариантом идеально балансировать нескольких провайдеров предоставляющими в транке несколько линий. Также насколько понимаю в будущем параметра call-limit не будет(deprecated) и останется использовать только счетчик линий для балансирования исходящих вызовов.
        0
        немного оффтопик, но все-же: хоть колл-лимит и деприкейтед. но в 1.8 еще есть, но непонятно. как сделать чтобы при звонке на внутренний сип-пир, у которого колл-лимит 1 и линия уже занята звонящему издавался стандартный сигнал занято, ну или предложение оставить мессадж на голосовую почту? в ael синтаксисе было бы вообще замечательно увидеть подсказку :)
          0
          При call-limit=1 вроде изначально будет выдавать сигнал занято (если, конечно, не было прописано иное действие).
          Вариант обработки с переходом на голосовую почту можно реализовать так:


          same => n,Dial(SIP/${EXTEN},30,tT)
          same => n,GotoIf($[${DIALSTATUS} = «BUSY»]?call_busy)
          same => n,Voicemail(${EXTEN}@default_context)


          А еще можете посмотреть файл «extensions.ael.sample». Там есть хороший пример, который вам подойдет:

          macro ael-std-exten-ael( ext, dev ) {
          Dial(${dev}/${ext},20);
          switch(${DIALSTATUS}) {
          case BUSY:
          Voicemail(${ext},b);
          break;
          default:
          Voicemail(${ext},u);
          };
          catch a {
          VoiceMailMain(${ext});
          return;
          };
          return;
          };
          –2
          Можно забивать микроскопом гвозди, а можно и молотком…
          Это все решается штатно, используя kamailio (sip-router).
            +2
            Ну так напишите об этом, если вам кажется что это проще!
              +1
              То есть вместо относительно легконастраиваемой офисной АТС лучше стоит использовать высокопроизводительный SIP-маршрутизатор, который идеально подходит для операторов?
              Тут Вы сильно ошибаетесь. С Asterisk начать очень легко, стоит только прочесть через строчку «Asterisk — будущее телефонии» и то не все главы. Как альтернативу можно предложить FreeSWITCH, но никак не Kamailio. Продукт, по которому нет толковой документации явно не для этих случаев.
                0
                То есть, если оператор использует quagga или bird, то я не могу его использовать в своей локальной сети?
                Тут Вы сильно ошибаетесь :-)
                У меня уже полгода бегает на raspberry pi связка asterisk + kamailio + 4 линии — я сделал преступление перед Человечеством?
                Kamailio не так уж сложен, как кажется. Серьезно…
                Например, вот статья, довольно старая, но тут все подробно обсосано донемогу: www.opennet.ru/base/net/asterisk_balance.txt.html
                И да, доков по kamailio тонна… Чем Вас не устроили штатные доки?
                  0
                  Разные задачи требуют разного подхода. И не говрю, что можно использовать, а что нет.
                  В данном случае задача не маршрутить over1000 вызовов, а всего 2. По ссылке сильно избыточно и много точек отказа. Для задачи озвученной ТС Ваш вариант как раз таки и является микроскопом для забивания гвоздей.

                  Kamailio не так уж сложен, как кажется. Серьезно…

                  А никто и не говорит, что он сложный.

                  И да, доков по kamailio тонна… Чем Вас не устроили штатные доки?

                  Своей непонятностью. Для документации там мало, а для комментариев избыточно. Сидим над Kamailio уже 2 месяца, пытаясь его скрестить с биллингом. Многие вещи вызывают вопросы.
                  Самый простой вариант: там нет указания на state и stateless режимы обработки запросов/диалогов. Первые несколько дней это вызывало негодование, пока в рассылке нам не пояснили про state и stateless. Или когда в конфиге убрали лишние ';' и информации по этому не нашли. Опять же в рассылке указали на ошибку. Если бы не стояло задачи по Kamailio так бы и пошёл он лесом до лучших времён со своей «непонятной» логикой обработки: тут ответил, тут не ответил, тут обработало, а тут выскочила «борода».
                  Это как раз все и должно быть в документации. Некоторые вещи проще даже понять не из оф.документации, а из исходного кода.
                  Ещё пример, который в рассылке поднимался уже не раз: одна и таже вещь в Asterisk делается в n*10 раз быстрее, чем в kamailio, т.к. в первом случае есть документация и есть полноценные рабочие примеры и показывают как их строить, а во втором случае есть example конфиг (рабочий) с далеко не всем функционалом и всё.
                    0
                    Интересно было бы почитать!
                +1
                Хотелось бы почитать как поднять ip телефонию с нуля. Основные моменты: оборудование, документы, договоры, особенности реализации сети.
                  0
                  Данная информация в сети имеется и в достаточном количестве. Минус только в том, что она разбросана. В вышеуказанной книге («Asterisk — будущее телефонии») большинство вопросов хорошо разобрано, потому данная книга для Вас — обязательна к прочтению.
                  Основные моменты: оборудование, документы, договоры, особенности реализации сети.
                  Тут уже нужно смотреть на требуемый масштаб телефонной сети. Ведь абонентов может быть как 5, так и 205. И требования к оборудованию при этом разное. Да и к сети тоже.
                  0
                  Очень хочется почитать использовал ли кто-то CEL и как он разбирал что там написано.
                    0
                    CEL для статистики звонков определенного абонента? Насколько понял ее логику, можно сделать и с обычной CDR правда логика отслеживания звонка будет не простая.
                      0
                      Идея в том что там можно отследить цепочку разговоров включая все переводы и переключения. Для статистики call-центров очень ценно. Мы уже даже начали с сотрудником делать скрипты и написали логику отслеживания этих цепочек для входящих звонков. Правда знаний SQL не хватило что бы написать грамотные запросы, а в продакшн это ставить было некуда. Так на уровне логики и осталось.

                  Only users with full accounts can post comments. Log in, please.