Очереди Asterisk после миграции: где искать сбой
После перехода с Asterisk 16 на 18 очередь может загрузиться без ошибок, но не дозваниваться до части операторов. Я начинаю с интерфейсов участников, состояния устройств и обработчиков вызова, затем проверяю динамические записи и журнал. Ниже — команды и порядок проверки, который помогает найти причину до переключения телефонии.
Почему очереди ломаются именно при переходе 16 → 18
Когда после обновления Asterisk очередь ведёт себя странно, я сначала проверяю не стратегию обзвона, а всё, что соединяет очередь с телефоном: канальный драйвер, интерфейс участника, состояние устройства и обработчики вызова. Конфигурация может загрузиться, а имя SIP-канала уже не соответствовать реальному PJSIP-эндпоинту. Это не загадка app_queue: это две части конфигурации, которые перестали понимать друг друга. Переход с chan_sip на chan_pjsip — отдельная миграция, даже если её удобно провести в то же окно.
В таких проектах я первым делом фиксирую, что изменилось вместе с версией. «Только обновили» часто означает ещё и пересборку модулей через menuselect, перенос sip.conf в pjsip.conf и правки диалплана. Каждое изменение проверяю отдельно. Если телефония у вас не основная работа, можно поручить её ИТ-аутсорсингу телефонии и серверов; но даже без подрядчика полезно понимать, где искать причину, а не лечить всё перезапуском.
Важно и состояние самой ветки: Asterisk 18 достигла конца жизненного цикла 20 октября 2025 года, как и Asterisk 16 — 9 октября 2023 года. Поэтому 18-я ветка годится для разбора уже случившейся миграции, но выбирать её для новой установки не следует. Перед работами сверяйте поддерживаемые серии на сайте проекта. В Asterisk 21 удалены chan_sip и app_macro; это не означает, что очередь сама по себе «сломалась», но конфигурацию, завязанную на эти модули, нужно заранее переписать.
Интерфейсы участников: SIP/ против PJSIP/
Одна из частых причин — участник очереди описан через старый интерфейс. Например, в queues.conf или realtime-таблице осталось member => SIP/101, а телефон теперь доступен через эндпоинт PJSIP с именем 101. Очередь будет пытаться вызвать старый интерфейс; проверьте, существует ли он и может ли канал открыться. Если в стратегии ringall часть операторов переведена, а часть нет, результат зависит от того, какие интерфейсы реально доступны. Исправление — согласованно обновить интерфейсы в статическом конфиге, realtime, AddQueueMember и других местах диалплана. Формат набора проверьте на своей конфигурации: PJSIP/101 сработает лишь при соответствующем имени endpoint.
Отдельно проверяю state_interface — источник состояния, по которому app_queue получает уведомления о занятости участника. Если очередь набирает Local-канал, но состояние читается у старого SIP/101, диагностика может показывать не то, что происходит с телефоном. В выводе queue show смотрю интерфейс и статус каждого участника; core show hints помогает проверить хинты, а pjsip show endpoints — увидеть PJSIP-эндпоинты. Это разные представления состояния, и совпадение имён ещё не доказывает, что связка настроена верно. У Asterisk есть также hint: как источник состояния; выбирайте его только если понимаете, какой extension и context он использует.
Третий слой — подписки и BLF. Если хинт в диалплане по-прежнему ссылается на SIP/101, телефонная лампа может показывать неактуальный статус, хотя вызов через PJSIP уже работает. Я свожу интерфейс участника, источник состояния и хинт к одной проверяемой схеме, затем тестирую каждый телефон и сценарии очереди. Смешанные SIP/PJSIP-конфигурации допустимы, пока каждый интерфейс намеренно настроен; опасно оставлять старую ссылку без проверки и считать, что одинаковый внутренний номер всё исправит.
ringinuse, занятость и чужие звонки
ringinuse определяет, можно ли предлагать вызов участнику, чьё устройство уже занято. Он зависит от достоверного device state: если драйвер не сообщает состояние или state_interface указывает не туда, флаг в очереди не угадает занятость. В PJSIP параметр эндпоинта device_state_busy_at задаёт порог числа занятых каналов, после которого драйвер вернёт BUSY вместо INUSE. Неверный порог меняет наблюдаемое состояние, поэтому проверяйте его вместе с реальными сценариями вызовов, а не копируйте значение из чужого pjsip.conf.
Перед запуском я проверяю это двумя вызовами: сначала занимаю телефон оператора напрямую, затем направляю к нему звонок через очередь. Смотрю состояние участника в queue show и сопоставляю его с тем, что показывает канал. Если Asterisk считает аппарат свободным во время разговора, сначала выясняю, откуда app_queue читает device state и какой канал фактически занят. Участники, добавленные статически и динамически, могут иметь разные интерфейсы и параметры — сравнивайте их записи, а не только экран телефона.
Есть ещё сценарий с настольным телефоном и софтфоном одного человека. Это один endpoint с несколькими контактами или несколько endpoints — и состояние будет зависеть от того, как они описаны и объединены в вашей схеме. Я выбираю один понятный участник очереди на человека и явно проверяю, какое устройство служит источником состояния. Стратегию обзвона проверяю отдельно от занятости: о различиях между стратегиями ringall и ring random — отдельный разговор; смена стратегии не исправит неверный device state.
Макросы, Gosub и что очередь вызывает при соединении
Старые dialplan-конфигурации могут использовать Macro(), а в очередях — параметр membermacro. Макросы устарели: Asterisk рекомендует Gosub, а в Asterisk 21 удалён модуль app_macro. В Asterisk 18 модуль ещё есть в исходниках, но при сборке важно проверить, включён ли он и загружен ли фактически. Команда module show like macro показывает наличие модулей, а core show application Macro — зарегистрировано ли приложение. Если нужного модуля нет, не рассчитывайте на молчаливое выполнение: вызовы и связанные обработчики нужно проверить по CLI и журналу.
В queues.conf можно заменить membermacro на membergosub; в вызове Queue() предусмотрен отдельный аргумент gosub, а для Dial() макросному обработчику M соответствует gosub-обработчик U. Контекст вызова и форма аргументов у этих механизмов различаются — откройте справку именно для установленной версии. В подпрограмме, из которой нужно вернуться к вызывающему коду, используйте Return(). Аргументы Gosub доступны как ARG1, ARG2 и далее. Это не переименование: проверьте, на каком канале выполняется обработчик и в какой момент он запускается.
При переносе я ищу MACRO_CONTEXT и MACRO_EXTEN: логика, завязанная на эти переменные, не переносится в Gosub автоматически. Проверяю также переменные, которые раньше создавал обработчик, и действия до соединения, например проверку доступности или подготовку записи. Если обработчик писал метаданные или запускал MixMonitor, делаю контрольный тест и проверяю результат на диске. В статье разобрана диагностика пустых записей разговоров; после миграции важно подтвердить не только успешный вызов подпрограммы, но и появление файла и строки отчёта.
- queues.conf: membermacro → membergosub
- Queue(): аргумент macro → gosub
- Dial(): обработчик M → U
- В Gosub используйте ARG1, ARG2 и Return() при возврате
Realtime, перезагрузка конфигурации и queue_log
Если очереди или участники описаны через realtime, сверяю схему таблиц с документацией и примерами именно установленной ветки. В репозитории Asterisk для управления схемами базы есть contrib/ast-db-manage; набор миграций и поддерживаемые поля зависят от версии и backend. Не делайте вывод, что «колонка необязательна», только потому что конфигурация загрузилась: убедитесь, какое значение в итоге получил участник и что показывает queue show. Для первичной диагностики полезны и официальные образцы queues.conf: там описаны поля участника, включая state_interface.
Не всякая правка требует перезапуска процесса. Полный перезапуск — быстрый способ закончить звонок и начать диагностику заново. CLI Asterisk 18 поддерживает queue reload all sales, а также выбор parameters, members или rules и имя очереди. Исключение — rules: они перечитываются из общего queuerules.conf и могут повлиять на другие очереди, даже если указано одно имя. Команда перечитывает выбранные части конфигурации, но не обновляет автоматически любые внешние источники. Для динамических участников важна настройка persistentmembers: в образце Asterisk 18 она по умолчанию выключена; при yes динамические участники сохраняются в astdb и восстанавливаются после старта. Снимите список и проверьте, нет ли там устаревших интерфейсов.
Отдельно сохраняю queue_log и смотрю, что именно журналирует система. В образце Asterisk параметр log_membername_as_agent по умолчанию выключен; когда он включён, события ADDMEMBER и REMOVEMEMBER при заданном state_interface могут писать имя участника вместо интерфейса. Поэтому переименование участника способно изменить группировку отчёта. Если диалплану нужно знать вызванный интерфейс, setinterfacevar=yes выставляет MEMBERINTERFACE перед соединением; проверьте, что отчёт использует именно ожидаемый идентификатор. Зафиксируйте исходные значения, не меняйте формат журнала попутно с миграцией и отдельно сравните тестовые события до и после. Похожая настройка autofill по умолчанию выключена: это меняет поведение распределения ожидающих вызовов между свободными участниками; её стоит зафиксировать, если сравниваете очередь до и после обновления.
Разбор: «Омега-Логистик» и пропавшие звонки в отделе продаж
Покажу на условном разборе «Омега-Логистик», а не на реальном клиенте. Офис: восемь операторов продаж, четыре диспетчера, три очереди; сервер обновили с Asterisk 16 до 18 и одновременно перевели телефоны на PJSIP. После переключения часть вызовов уходила в резервную очередь, двое операторов не видели звонки, а одному поступал второй вызов во время разговора. В качестве масштаба потерь руководитель оценил пропущенные вызовы за первую половину дня; эту оценку здесь не выдаю за измеренную статистику.
Проверка queue show выявила старые SIP/ среди динамических участников, сохранённых в astdb. Переход на резервную очередь объяснился настроенным тайм-аутом; после восстановления доступности участников он перестал срабатывать на каждый такой случай. Для второго вызова нашли оставшийся state_interface, который ссылался на SIP через Local-канал. Отдельно проверили обработчик записи: membermacro ссылался на Macro, а нужный модуль не был загружен в новой сборке. Это разные неисправности, хотя в понедельник они выглядели как один общий «сбой очереди».
На стенде заменили неверные интерфейсы, настроили источник состояния, перенесли обработчик на Gosub и проверили запись разговора. Затем выполнили контрольные сценарии с занятым оператором и резервным маршрутом, а для участников из astdb сверили сохранённые записи. Время исправления и число пропущенных звонков в таком разборе зависят от исходной конфигурации; не обещаю универсальные три часа или одинаковый результат. В регламент добавили снимок queue show, astdb и queue_log до работ, а после переключения — повторную проверку.
План миграции очередей, чтобы не чинить в понедельник
До обновления я сохраняю конфиги, astdb и выборку queue_log, снимаю queue show по всем очередям и core show hints. Затем нахожу все ссылки на SIP/, PJSIP/, Local/, state_interface, membermacro и переменные макросов. Читаю UPGRADE.txt и CHANGES в исходниках целевой версии и промежуточных веток, которые действительно пересекает план обновления. На тестовом экземпляре проверяю входящий вызов, занятого оператора, отсутствие доступных участников, тайм-аут, резервный маршрут и запись разговора.
При переключении я меняю согласованный набор интерфейсов, а не переименовываю их наугад. После проверяю queue show, делаю тестовый вызов в каждую очередь и сверяю queue_log и результат записи. Операторам достаточно короткого правила: если звонок не пришёл, сообщить сразу, пока состояние участника и журнал можно сопоставить. Хорошая миграция заканчивается проверкой, а не фразой «конфиг вроде загрузился». Для общего чек-листа параметров очереди пригодится руководство по настройке Asterisk queues.
Частые вопросы
Почему после обновления очередь не звонит части операторов?
Чаще всего у них остались интерфейсы SIP/ при телефонах, переведённых на PJSIP, особенно у динамических участников в astdb. Проверьте статус участников командой queue show.
Нужно ли переходить с chan_sip на chan_pjsip при обновлении до 18?
В 18-й ветке chan_sip ещё есть, но он объявлен устаревшим и в новых версиях удалён. Переход всё равно предстоит, удобнее сделать его осознанно и целиком, а не наполовину.
Чем заменить membermacro в queues.conf?
Параметром membergosub с подпрограммой, которая заканчивается Return(). Переменных MACRO_CONTEXT и MACRO_EXTEN в Gosub нет, логику на них нужно переписать.
Можно ли перечитать очереди без перезапуска Asterisk?
Да, команда queue reload перечитывает параметры, участников или правила, в том числе для отдельной очереди. Активные разговоры при этом не обрываются.
Почему оператору приходит второй звонок во время разговора?
Asterisk не видит, что устройство занято: неверный state_interface или хинт, либо не настроена занятость эндпоинта в PJSIP. ringinuse=no работает только при правильном состоянии устройства.
Источники
- Asterisk Versions — Сроки поддержки веток: Asterisk 16 и 18 достигли EOL; проверено по таблице релизов проекта — https://docs.asterisk.org/About-the-Project/Asterisk-Versions/
- Asterisk Module Deprecations — Версии устаревания и удаления chan_sip и app_macro — https://docs.asterisk.org/Development/Asterisk-Module-Deprecations/
- Asterisk 18 queues.conf.sample — Сверены membermacro/membergosub, persistentmembers, ringinuse, state_interface, autofill, log_membername_as_agent и синтаксис queue reload с выбором очереди — https://github.com/asterisk/asterisk/blob/18/configs/samples/queues.conf.sample и https://github.com/asterisk/asterisk/blob/18/apps/app_queue.c
- Pre-Bridge Handlers — Проверены обработчики Macro и Gosub для Dial и Queue, включая M, U и аргумент gosub — https://docs.asterisk.org/Configuration/Dialplan/Subroutines/Pre-Bridge-Handlers/
- Asterisk Gosub — Сверены Return(), синтаксис контекста/расширения/приоритета и ARG1…ARGn — https://docs.asterisk.org/Configuration/Dialplan/Subroutines/Gosub/
- res_pjsip configuration options — Проверен смысл параметра endpoint device_state_busy_at — https://docs.asterisk.org/Latest_API/API_Documentation/Module_Configuration/res_pjsip/



