Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

Напэўна ў многіх з вас, як і ў мяне, была ідэя зрабіць што-небудзь унікальнае. У гэтым артыкуле я апішу тэхнічныя праблемы і рашэнні, з якімі прыйшлося сутыкнуцца пры распрацоўцы АТС. Магчыма, гэта камусьці дапаможа вырашыцца на сваю ідэю, а камусьці прайсці па пратаптанай дарожцы, бо я таксама карыстаўся досведам першапраходцаў.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

Ідэя і ключавыя патрабаванні

А пачалося ўсё банальна з кахання да зорачка (framework для пабудовы камунікацыйны прыкладанняў), аўтаматызацыі тэлефаніі і установак Бясплатная АТС (вэб інтэрфейс для зорачка). Калі запатрабаванні кампаніі былі без асаблівасцяў і ўкладваліся ў магчымасці Бясплатная АТС - усё супер. Уся ўстаноўка праходзіла за суткі, кампанія атрымлівала настроеную АТС, зручны інтэрфейс і кароткае навучанне плюс суправаджэнне па жаданні.

Але самыя цікавыя задачы былі нестандартныя і тады было не так казачна. зорачка можа шмат, але каб захаваць у працоўным выглядзе вэб-інтэрфейс, даводзілася выдаткаваць у разы больш часу. Так невялікая дробязь магла заняць чакай значна больш, чым усталёўка ўсёй астатняй АТС. І справа не ў тым, што пісаць вэб інтэрфейс доўга, а хутчэй справа ў асаблівасцях архітэктуры. Бясплатная АТС. Падыходы і метады архітэктуры Бясплатная АТС закладвалася ў часы php4, а ў той момант ужо быў php5.6, на якім усё можна было зрабіць прасцей і зручней.

Апошняй кропляй сталі графічныя дыялпланы ў выглядзе схемы. Калі паспрабаваў падобнае пабудаваць для Бясплатная АТС, зразумеў, што давядзецца істотна яго перапісаць і прасцей ужо пабудаваць што-небудзь новае.

Ключавымі патрабаваннямі сталі:

  • простая налада, інтуітыўна даступная нават пачаткоўцу адміністратару. Тым самым кампаніям не патрабуецца абслугоўванне АТС на нашым баку,
  • лёгкая дапрацоўка, каб задачы вырашаліся за адэкватны час,
  • зручнасць інтэграцыі з АТС. У Бясплатная АТС не было API для змены налад, г.зн. нельга, напрыклад, ствараць групы або галасавыя меню з іншага прыкладання, толькі API самога зорачка,
  • opensource - для праграмістаў гэта вельмі важна для дапрацовак пад кліента.

Ідэя хутчэйшай распрацоўкі была ў тым, каб увесь функцыянал складаўся з модуляў у выглядзе аб'ектаў. Усе аб'екты павінны былі мець агульны бацькоўскі клас, а значыць назвы ўсіх асноўных функцый ужо вядомыя і значыць ужо ёсць рэалізацыі па змаўчанні. Аб'екты дазволяць рэзка скараціць колькасць аргументаў у выглядзе асацыятыўных масіваў са радковымі ключамі, даведацца якія ў Бясплатная АТС можна было, даследаваўшы ўсю функцыю і ўкладзеныя функцыі. У выпадку аб'ектаў банальнае аўтадапаўненне пакажа ўсе ўласцівасці, ды і цэлым у шмат разоў спросціць жыццё. Плюс атрыманне ў спадчыну і пераазначэнне ўжо закрывае мноства праблем з дапрацоўкамі.

Наступнае, што запавольвала час дапрацоўкі і чаго варта было пазбегнуць - гэта дубліраванне. Калі ёсць модуль адказны за дазвон да супрацоўніка, то ўсе астатнія модулі, якім трэба даслаць званок супрацоўніку, павінны выкарыстоўваць менавіта яго, а не ствараць свае ўласныя копіі. Так, калі трэба што-небудзь памяняць, то мяняць давядзецца толькі ў адным месцы і пошук "як гэта працуе" праводзіць аднаго месца, а не ажыццяўляць пошук па ўсім праекце.

Першая версія і першыя памылкі

Першы прататып быў гатовы ўжо праз год. Уся АТС, як і планавалася, была модульная, і модулі маглі не толькі дадаваць новы функцыянал для апрацоўкі званкоў, але і мяняць сам вэб-інтэрфейс.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php
Так, ідэя пабудовы диалплана ў выглядзе такой схемы не мая, але яна вельмі зручная і я зрабіў тое ж самае для зорачка.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

З дапамогай напісання модуля, праграмісты ўжо маглі:

  • ствараць для апрацоўкі званка ўласны функцыянал, які можна было размясціць на схеме, а таксама ў меню элементаў злева,
  • ствараць уласныя старонкі для вэб-інтэрфейсу і дадаваць свае шаблоны на існуючыя старонкі (калі распрацоўшчык старонкі гэта прадугледзеў),
  • дадаваць свае наладкі на ўкладку асноўных налад або ствараць уласную ўкладку з наладамі,
  • праграміст можа атрымаць у спадчыну ад існуючага модуля, змяніць частку функцыяналу і зарэгістраваць пад новым імем або замяніць арыгінальны модуль.

Напрыклад, вось так можна стварыць сваё галасавое меню:

......
class CPBX_MYIVR extends CPBX_IVR
{
 function __construct()
 {
 parent::__construct();
 $this->_module = "myivr";
 }
}
.....
$myIvrModule = new CPBX_MYIVR();
CPBXEngine::getInstance()->registerModule($myIvrModule,__DIR__); //Зарегистрировать новый модуль
CPBXEngine::getInstance()->registerModuleExtension($myIvrModule,'ivr',__DIR__); //Подменить существующий модуль

Першыя складаныя ўкараненні прынеслі першы гонар і першыя расчараванні. Цешыла тое, што яно працавала, што я ўжо змог прайграць асноўныя магчымасці Бясплатная АТС. Цешыла, што людзям ідэя схемы прыйшлася па душы. Было яшчэ шмат варыянтаў спрасціць распрацоўку, але і на той момант частка задач ужо рабілася прасцей.

Расчараваннем стала API для змены канфігурацыі АТС – атрымалася зусім не тое, што хацелася. Я ўзяў той жа прынцып, што і ва Бясплатная АТС, па націску кнопкі Apply пераствараецца ўся канфігурацыя і перазапускаюцца модулі.

Выглядае гэта так:

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php
*Дыалплан - правіла (алгарытм), па якім апрацоўваецца званок.

Але пры такім варыянце немагчыма напісаць нармальнае API для змены налад АТС. Па-першае, аперацыя прымянення змяненняў да зорачка занадта доўгая і рэсурсаёмістая.
Па-другое, нельга выклікаць дзве функцыі адначасова, т.я. абедзве будуць ствараць канфігурацыю.
Па-трэцяе, прымяняе ўсе налады ў тым ліку зробленыя адміністратарам.

У гэтай версіі, як і ў Askozia, можна было генераваць канфігурацыю толькі змененых модуляў і перазапускаць толькі неабходныя модулі, але гэта ўсё паўмеры. Неабходна было мяняць падыход.

Другая версія. Нос выцягнуў хвост уграз

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

У выніку не спатрэбілася нават перазапускаць. зорачка пры змене налад і ўсе налады сталі прымяняцца адразу да зорачка.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

Адзіныя змены дыялплана - гэта дадання ўнутраных нумароў і падказкі. Але гэта былі маленькія кропкавыя змены

exten=>101,1,GoSub(‘sub-callusers’,s,1(1)); - точечное изменение, добавляется/изменяется через ami

; sub-callusers – универсальная функция генерится при установке модуля.
[sub-callusers]
exten =>s,1,Noop()
exten =>s,n,Set(LOCAL(TOUSERID)=${ARG1})
exten =>s,n,ClearHash(TOUSERPARAM)
exten =>s,n,Set(HASH(TOUSERPARAM)=${REALTIME_HASH(rl_users,id,${LOCAL(TOUSERID)})})
exten =>s,n,GotoIf($["${HASH(TOUSERPARAM,id)}"=""]?return)
...

Дадаць або памяняць радок у дыялплане лёгка можна праз амі (інтэрфейс кіравання зорачка) і перазагрузкі за ўсё дыялплана не патрабуецца.

Так была вырашана праблема з API для канфігурацыі. Можна было нават наўпрост зайсці ў базу і дадаць новую групу ці памяняць, напрыклад, час дазвону ў поле "dialtime" у групы і наступны званок ужо будзе доўжыцца паказаны час (Гэта не рэкамендацыя да дзеяння, бо для некаторых API аперацый патрабуюцца амі выклікі).

Першыя складаныя ўкараненні зноў прынеслі першы гонар і расчараванне. Цешыла тое, што гэта працуе. База дадзеных стала крытычна важным звяном, вырасла залежнасць ад дыска, рызык больш, але ўсё працавала стабільна і без праблем. А галоўнае зараз усё, што можна было зрабіць праз вэб-інтэрфейс, можна было зрабіць і праз API і пры гэтым выкарыстоўваліся адны і тыя ж метады. Дадаткова, вэб-інтэрфейс пазбавіўся ад кнопкі "ўжыць налады да АТС", пра якую адміністратары часта забывалі.

Расчараваннем стала ўскладненне распрацоўкі. Яшчэ з першай версіі мова php генеруе дыялплан на мове зорачка і выглядае гэта зусім нечытэльна, плюс сама мова зорачка для напісання дыялплана вельмі прымітыўны.

Як гэта выглядала:

$usersInitSection = $dialplan->createExtSection('usersinit-sub','s');
$usersInitSection
 ->add('',new Dialplanext_gotoif('$["${G_USERINIT}"="1"]','exit'))
 ->add('',new Dialplanext_set('G_USERINIT','1'))
 ->add('',new Dialplanext_gosub('1','s','sub-AddOnAnswerSub','usersconnected-sub'))
 ->add('',new Dialplanext_gosub('1','s','sub-AddOnPredoDialSub','usersinitondial-sub'))
 ->add('',new Dialplanext_set('LOCAL(TECH)','${CUT(CHANNEL(name),/,1)}'))
 ->add('',new Dialplanext_gotoif('$["${LOCAL(TECH)}"="SIP"]','sipdev'))
 ->add('',new Dialplanext_gotoif('$["${LOCAL(TECH)}"="PJSIP"]','pjsipdev'))

У другой версіі дыялплан стаў універсальным, у яго былі закладзены ўсе магчымыя варыянты апрацоўкі ў залежнасці ад параметраў і яго памер значна вырас. Усё гэта моцна запавольвала час распрацоўкі, і сама думка што ў чарговы раз трэба ўмешвацца ў диалплан наводзіла сум.

Трэцяя версія

Ідэяй для вырашэння праблемы стала не генераваць зорачка дыялплан з php, а выкарыстоўваць FastAGI і ўсе правілы апрацоўкі пісаць ужо на самай php. FastAGI дазваляе зорачка, для апрацоўкі званка, падлучыцца да сокету. Атрымліваць адтуль каманды і дасылаць вынікі. Такім чынам логіка дыялплана знаходзіцца ўжо за межамі зорачка і можа быць напісана на любой мове, у маім выпадку на php.

Тут было шмат спроб і памылак. Галоўнай праблемай з'яўлялася, што ў мяне ўжо было шмат класаў/файлаў. На стварэнне аб'ектаў, ініцыялізацыю і ўзаемную рэгістрацыю паміж імі спатрэбілася каля 1,5 секунд, і гэта затрымка на кожны званок не тое, што можна ігнараваць.

Ініцыялізацыя павінна была быць толькі 1 раз і таму пошукі рашэння пачаліся з напісання сэрвісу на php з выкарыстаннем Pthreads. Праз тыдзень эксперыментаў гэты варыянт быў адкладзены праз тонкасці працы гэтага пашырэння. Ад асінхроннага праграмавання на php пасля месяца тэстаў таксама прыйшлося адмовіцца, трэба было нешта простае, знаёмае любому пачаткоўцу php, ды і шматлікія пашырэнні для php сінхронныя.

Рашэннем стаў свой шматструменны сэрвіс на 'сі', які кампіляваўся з PHPLIB. Ён падгружае ўсе php файлы АТС, чакае, калі ўсе модулі ініцыялізуюцца, дададуць коллбэк сябар да сябра і калі ўсё гатова – кэшуе. Пры запыце па FastAGI ствараецца паток, у ім прайграваецца копія з кэша ўсіх класаў і даных і запыт перадаецца ў php функцыю.

Пры такім рашэнні час ад адпраўкі званка ў наш сэрвіс да першай каманды зорачка скарацілася з 1,5, 0,05с да XNUMX, XNUMXс і гэты час слаба залежыць ад памеру праекта.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

У выніку, час на распрацоўку дыялплана скарацілася істотна, і я магу гэта ацаніць паколькі мне прыйшлося перапісаць увесь дыялплан усіх модуляў на php. Па-першае, у php ужо павінны быць напісаны метады для атрымання аб'екта з базы, яны былі патрэбныя для адлюстравання ў вэб-інтэрфейсе, а па-другое, і гэта галоўнае - нарэшце-то з'явілася магчымасць зручнай працы са радкамі з лікамі з масівамі з базай даных плюс мноства пашырэнняў php.

Для апрацоўкі дыялплана ў класе модуля трэба рэалізаваць функцыю dialplanDynamicCall і аргумент pbxCallRequest будзе змяшчаць аб'ект для ўзаемадзеяння з зорачка.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

У дадатку з'явілася магчымасць адладжваць диалплан (у php ёсць xdebug і для нашага сэрвісу яно працуе), можна рухацца па кроках праглядаючы значэнні зменных.

Дадзеныя па званках

Для любой аналітыкі і справаздач патрэбныя правільна сабраныя дадзеныя і гэты блок АТС таксама праходзіў шмат спроб і памылак з першай па трэцюю версію. Часцяком дадзеныя па званках - гэта таблічка. Адзін званок = адзін запіс: хто тэлефанаваў, хто адказаў, колькі прагаварылі. У цікавейшых варыянтах ёсць яшчэ дадатковая таблічка, каго з супрацоўнікаў АТС выклікала падчас званка. Але ўсё гэта закрывае толькі частку запатрабаванняў.

Першапачатковымі патрабаваннямі сталі:

  • захоўваць не толькі каму тэлефанавала АТС, але і хто адказаў, т.я. існуюць перахопы і пры аналізе званкоў гэта трэба будзе ўлічваць,
  • час да злучэння з супрацоўнікам. Во Бясплатная АТС і некаторых іншых АТС, званок лічыцца адказам, як толькі АТС падніме трубку. Але для галасавога меню ўжо трэба падняць трубку, такім чынам усе званкі становяцца адказнымі і час чакання адказу становіцца 0-1 секунду. Таму вырашана было захоўваць не толькі час да адказу, але час да злучэння з ключавымі модулямі (модуль сам усталёўвае ў сябе гэта сцяг. Цяпер гэта "Супрацоўнік", "Знешняя лінія"),
  • для больш складанага диалплана, калі званок шпацыруе паміж рознымі групамі, патрэбна была магчымасць кожны элемент даследаваць па асобнасці.

Лепшым варыянтам аказаўся варыянт, калі модулі АТС самі пра сябе адпраўляюць інфармацыю па званках і ў выніку захоўваць інфармацыю ў выглядзе дрэва.

Выглядае гэта наступным чынам:

Для пачатку агульная інфармацыя аб званку (як ва ўсіх – нічога асаблівага).

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

  1. Паступіў званок па знешняй лініі.для тэсту» у 05:55:52 з нумара 89295671458 на нумар 89999999999, у выніку на яго адказаў супрацоўнік «Сакратар2» з нумарам 104. Кліент прачакаў 60 секунд і размаўляў 36 секунд.
  2. Супрацоўнік «Сакратар2» робіць званок на нумар 112 і на яго адказвае супрацоўнік «Менеджэр1» праз 8 секунд. Размаўляюць 14 секунд.
  3. Кліента пераводзяць на Супрацоўнікаменеджэр1» дзе яны працягваюць размаўляць яшчэ 13 секунд

Але гэта вяршыня айсберга, па кожным запісе можна атрымаць падрабязнае праходжанне званка па АТС.

Гісторыя аднаго праекту ці як я 7 гадоў ствараў АТС на базе Asterisk і Php

Уся інфармацыя падаецца ў выглядзе ўкладзенасці выклікаў:

  1. Паступіў званок па знешняй лініі.для тэсту» у 05:55:52 з нумара 89295671458 на нумар 89999999999.
  2. У 05:55:53 знешняя лінія адпраўляе званок на Уваходную схему.тэст»
  3. Падчас апрацоўкі званка па схеме выклікаецца модульвыклік мэнэджэра», у якім званок знаходзіцца 16 секунд. Гэта распрацаваны пад кліента модуль.
  4. Модуль «выклік мэнэджэра» адпраўляе званок на адказнага за нумар (кліента) супрацоўніка «Менеджэр1» і чакае адказу 5 секунд. Мэнэджар не адказаў.
  5. Модуль «выклік мэнэджэра» адпраўляе званок на групу «Мэнэджары КОРП». Гэта іншыя менеджэры такога ж напрамку (сядзяць у адным пакоі) і чакае адказу 11 секунд.
  6. група «Мэнэджары КОРП» выклікае супрацоўнікаў «Менеджэр1, Менеджэр2, Менеджэр3» адначасова па 11 секунд. Адказу няма.
  7. Выклік мэнэджэра завяршаецца. І схема званок адпраўляе на модульВыбар маршруту з 1с». Таксама напісаны пад кліента модуль. Тут званок апрацоўваўся 0 сэкунд.
  8. Схема адпраўляе званок на галасавое меню.сн з данаборам». Кліент у ім чакаў 31 секунду, данабору не было.
  9. Схема адпраўляе званок на ГрупуСакратары», дзе кліент прачакаў 12 секунд.
  10. У групе выклікаецца адначасова 2 супрацоўнікі.Сакратар1»І«Сакратар2» і праз 12 секунд адказвае супрацоўнік «Сакратар2». Адказ на выклік дублюецца ў бацькоўскія выклікі. Атрымліваецца і ў групе адказаўСакратар2», пры выкліку схемы адказаў «Сакратар2» і на званок па знешняй лініі адказаў «Сакратар2.

Менавіта захаванне інфармацыі аб кожнай аперацыі і іх укладзенасці дазволіць проста зрабіць справаздачы. Справаздача па галасавым меню дапаможа высветліць, наколькі яно дапамагае ці замінае. Пабудаваць справаздачу аб прапушчаных супрацоўнікамі званках з улікам, што званок перахапілі і значыць не лічыцца прапушчаным, і з улікам, што гэта быў групавы званок, і хто-небудзь іншы ўзяў раней, а значыць таксама званок не прапушчаны.

Такое захоўванне інфармацыі дазволіць узяць кожную групу паасобку і вызначыць наколькі яна эфектыўна працуе, пабудаваць графік адказаных і прапушчаных групы па гадзінах. Таксама можна праверыць, наколькі адгадвае злучэнне з адказным мэнэджарам, аналізуючы пераклады пасля злучэння з мэнэджарам.

У тым ліку можна праводзіць дастаткова нетыповыя даследаванні, напрыклад, як часта нумары, якіх няма ў базе, набіраюць правільны дадатковы або які працэнт выходных званкоў з'яўляецца пераадрасацыяй на мабільны.

Што ў выніку?

Для абслугоўвання АТС не патрабуецца спецыяліст, з гэтым спраўляецца самы звычайны адміністратар - праверана на практыцы.

Для дапрацовак не патрэбны спецыялісты з сур'ёзнай кваліфікацыяй дастаткова ведаў php, т.я. ужо напісаны модулі і для sip пратаколу, і для чаргі, і для выкліку супрацоўніка і іншыя. Ёсць клас абгортка для зорачка. Праграміст для распрацоўкі модуля можа (і па-добраму павінен) выклікаць ужо гатовыя модулі. І веды зорачка зусім не патрэбныя, калі кліент просіць дадаць старонку з якой-небудзь новай справаздачай. Але практыка паказвае, што іншыя праграмісты хоць і спраўляюцца, але без дакументацыі і нармальнага пакрыцця каментарамі пачуваюцца няўпэўнена, таму яшчэ ёсць куды рухацца.

Модулі могуць:

  • ствараць новыя магчымасці па апрацоўцы званка,
  • дадаваць новыя блокі ў вэб-інтэрфейс,
  • успадкавацца ад любога з існуючых модуляў, перавызначыць функцыі і падмяніць яго ці проста быць злёгку змененай копіяй,
  • дадаваць свае наладкі ў шаблон налад іншых модуляў і многае іншае.

Настройкі АТС праз API. Як апісана вышэй, усе налады захоўваюцца ў базе і чытаюцца ў момант выкліку, таму праз API можна мяняць усе налады АТС. Пры выкліку API не пераствараецца канфігурацыя і не перазапускаюцца модулі, такім чынам, не важна наколькі шмат у вас налад і супрацоўнікаў. API запыты выконваюцца хутка і не блакуюць адзін аднаго.

АТС захоўвае ўсе ключавыя аперацыі са званкамі з працягласцямі (чаканні/размовы), укладзенасцямі і ў тэрмінах АТС (супрацоўнік, група, знешняя лінія, а не канал, нумар). Гэта дазваляе будаваць розныя справаздачы пад канкрэтных кліентаў і большая частка працы - зрабіць зручны інтэрфейс.

Што будзе далей пакажа час. Ёсць яшчэ шмат нюансаў, якія варта перарабіць, ёсць яшчэ шмат планаў, але ад стварэння 3. Версіі прайшоў ужо год і ўжо можна сказаць, што ідэя працуе. Асноўны мінус 3-й версіі - гэта апаратныя рэсурсы, але за зручнасць распрацоўкі звычайна заўсёды менавіта так і даводзіцца плаціць.

Крыніца: habr.com

Дадаць каментар