8.5 dmq — розподілений стан між інстансами¶
[!IMPORTANT] Усе описане в part 6 —
tm-транзакції,dialog-record'и,usrloc-контакти,htable-entries,dispatcher-state — живе в shm одного інстансу Kamailio. У момент, коли ви ганяєте два Kamailio-інстанси за load-balancer'ом, ті shm-області незалежні, і ніщо їх не синхронізує.dmq— Distributed Message Queue — це fabric, що пропагує зміни стану між інстансами.
Проблема кількох інстансів¶
Три Kamailio-інстанси за load-balancer'ом ділять inbound-трафік. REGISTER від Аліси потрапляє на інстанс #1. Контакт Аліси опиняється в usrloc-кеші інстансу #1. Через п'ять секунд Боб дзвонить Алісі. INVITE приходить на інстанс #2 (інший воркер, інша машина).
Інстанс #2 шукає Алісу у своєму usrloc — і не знаходить. Аліса зареєстрована, але лише на інстансі #1. Виклик провалюється.
Фікс — змусити стан реплікуватися. Кожна зміна стану на будь-якому інстансі має fan-out'итися на всі інші. dmq — шина, що це робить.
Що таке dmq насправді¶
Peer-to-peer-мережа над SIP. Кожен Kamailio-інстанс — dmq-нода. Ноди знають про одна одну (статично сконфігуровані чи виявлені) і обмінюються повідомленнями через спеціальний SIP-транспорт — насправді ті ж listening-сокети, просто з іншим SIP-методом/route'ом.
flowchart TB
LB[Load balancer]
LB --> K1[Kamailio #1]
LB --> K2[Kamailio #2]
LB --> K3[Kamailio #3]
K1 <-->|dmq події| K2
K2 <-->|dmq події| K3
K1 <-->|dmq події| K3
classDef lb fill:#bf8700,stroke:#bf8700,color:#fff
classDef k fill:#1f6feb,stroke:#1f6feb,color:#fff
class LB lb
class K1,K2,K3 k
Коли модуль на одній ноді хоче транслювати стан, він публікує повідомлення на іменованому dmq-«каналі» (usrloc, dialog, htable — кожен має свій). Кожна інша нода, підписана на канал, отримує повідомлення і застосовує оновлення у власному shm.
Транспорт — звичайний SIP. KDMQ (custom SIP-метод) запит йде з ноди в ноду. Receiver парсить, диспетчеризує по імені каналу до потрібного модуля, модуль оновлює in-memory-стан.
Канали і що реплікується¶
Кожен модуль, що підтримує dmq, реєструє свій channel-handler. Well-known:
| Модуль | Канал | Що реплікується |
|---|---|---|
usrloc |
usrloc |
Insert'и, update'и, delete'и контактів |
dialog |
dialog |
State-переходи dialog'а (early → confirmed → terminated) |
htable |
per-table-канал | Insert'и і delete'и entries (налаштовується per table) |
dmq_usrloc |
dedicated | Спеціалізована usrloc-only-реплікація з сильнішою впорядкованістю |
Список росте, по мірі того як модулі додають dmq-підтримку. Патерн той самий: коли модуль мутує in-shm-стан, серіалізує зміну і публікує; receivers десеріалізують і застосовують.
Топологія кластера і membership¶
dmq-кластер налаштовується:
- Списком peer-URI (кожна нода знає SIP-адреси кожної іншої).
- Самоідентифікацією (кожна нода має свій URI для публікації).
- Підпискою на канали (які модулі беруть участь у реплікації).
Коли нода стартує, вона вітає peer'ів KDMQ-реєстрацією. Peer'и додають її у свій recipient-список. Коли нода падає — peer'и помічають (probing чи failed-send) і перестають слати.
Немає leader-election, немає quorum'а — flat-mesh. Усі ноди рівні. Ціна — O(N²)-з'єднань для N нод; простота того варта до кількох десятків нод.
Для більших кластерів архітектурна відповідь — не складніша dmq-топологія, а шардинг load-balancer'а перед Kamailio (consistent-hashing inbound-трафіку по юзеру), щоб кожен shard був маленьким dmq-кластером.
Модель консистентності¶
dmq — eventually consistent. Немає транзакційної гарантії, що всі ноди застосують update в один момент. Порядок:
- Модуль на ноді A мутує свій shm.
- Модуль публікує зміну в dmq.
- Через мілісекунди ноди B і C отримують повідомлення і застосовують.
Між кроками 2 і 3 ноди B і C мають stale-state. Для більшості репліцоваого стану — реєстрацій, dialog-state — це нешкідливо. REGISTER, що ще не дійшов — просто трохи затриманий перший виклик від юзера. Termination dialog'а, що ще не дійшов — трохи затриманий cleanup на інших нодах.
Що не ок — це будь-що, що залежить від строго-консистентного view між нодами. dmq — не coordination-примітив; це replication-шина. Якщо потрібні крос-нодові локи — зовнішня система (Redis, ZooKeeper) — правильне місце. dmq не замінює.
Failure-режими¶
Кілька речей, що варто відстежувати:
[!WARNING] Network partition між dmq-peer'ами призводить до split-brain у стані. Обидві сторони продовжують мутувати свій shm і публікувати локально, але partition не дає пропагуватися. Коли мережа лікується — обидві сторони намагаються push'нути накопичені зміни — і порядок застосування невизначений. Стан може зійтися непередбачувано.
- Повільний dmq-peer може back-up'нути локальну чергу. Якщо нода B повільна — publish-to-B з ноди A займає довше. Поведінка модулів варіює — одні блокують, одні дроп'ять, одні черговують з bounded-розміром.
- Реплікація множить write-load. Кожен REGISTER, що ви обробляєте локально, також стріляє N-1 dmq-send'ів до peer'ів. Для високого CPS REGISTER'ів на багатьох нодах сам dmq-трафік може бути значним.
- Модулі не все реплікують.
dialogреплікує state-переходи, не повний вміст dialog'а. Якщо треба багатий per-call-стан на кожній ноді —dialog'ова dmq-реплікація може не вистачити;topos_redisчи справжній distributed-store.
Коли dmq, коли Redis¶
dmq правильний, коли:
- Ви ганяєте 2–10 Kamailio-інстансів.
- Стан для реплікації маленький per item (контакти, dialog-ідентифікатори, htable-ключі).
- Eventual-consistency норм.
- Не хочеться додавати зовнішню залежність.
Справжній зовнішній store (Redis зазвичай) правильний, коли: - Багато інстансів (20+). - Стан великий (повна call-recording-metadata, глибокі auth-кеші). - Потрібна сильніша consistency чи query-можливості (TTL, atomic-op, range-query). - Шарите стан з не-Kamailio-сервісами.
На практиці великі оператори використовують обидва: dmq для дешевого швидко-реплікованого стану (registrar-контакти, dialog-state), Redis (чи аналог) для того, що потребує справжньої persistence і сильних query.
Операційне використання¶
dmq.list_nodes — operational-еквівалент «чи живий кластер?» — показує known-state кожного peer'а (up, pending, disabled) і час останнього контакту. Peer, що довго мовчить — перший знак propagation-проблеми.
Новіші knob'и, про які варто знати¶
dmq — старий модуль, але його досі активно розширюють — пачка змін, що приземлилася в master-лінію (development Kamailio 6.x), робить його дешевшим в експлуатації і керованішим ззовні. Якщо ви на свіжому білді — ось ті, що мають значення.
sl_send — stateless replication-send'и. За замовчуванням кожне dmq-повідомлення йде statefully: tm-транзакція на send, щоб sender міг зматчити reply. На TCP-транспорті між нодами, при REGISTER-важкому CPS, той transaction-churn — чистий overhead, бо більшості replication-повідомлень reply не потрібен. modparam("dmq", "sl_send", 1) перемикає на stateless-send; statefully лишаються тільки повідомлення з реальним reply-callback'ом (membership-трафік notification_peer). Дешевий win на навантажених кластерах.
init_with_single — швидкий startup-sync. На старті нода зазвичай тягне стан з кожного peer'а зі свого списку одразу. У великому кластері це thundering herd проти щойно-стартованої ноди. modparam("dmq", "init_with_single", 1) змушує синхронізуватися з однією нодою — першою, що відповіла на ping — і довіритися mesh'у вирівняти решту згодом. Швидші, легші рестарти.
RPC-керування membership'ом — dmq.add і dmq.change_status. Membership раніше керувався майже повністю з конфіга (notification address) і внутрішнім probing'ом. Два RPC тепер дають оркеструвати кластер ззовні:
kamcmd dmq.add sip:10.0.0.7:5060 # додати ноду в список цієї ноди
kamcmd dmq.change_status sip:10.0.0.7:5060 1 # перемкнути status існуючої ноди
Це те, що треба, коли membership нод приходить з deploy-скрипта чи autoscaler'а, а не зі статичного конфіга — підняв ноду, зареєстрував її в кластері через RPC, без reload'а.
dmq_process_custom() — dmq як програмована шина. Донедавна обробити вхідне dmq-повідомлення міг лише C-модуль. Нова config-функція дає скрипту вручну віддати довільний payload локальному peer'у:
Це перетворює dmq з «модулі тихо синхронять власний стан» на replication-шину, яку ви можете драйвити з routing-скрипта — ганяти між нодами свої дані, не лише те, що передбачив автор модуля. Нішево, але це різниця між розширенням dmq і його форком.
[!NOTE] Це приземлилося через master-лінію — операційний polish від людей, що ганяють великі
dmq-кластери в бою. Перевіряйте версію модуля, перш ніж покладатися на будь-що з цього —kamcmd core.versionі README модуля.
Чому dmq — це scale-out-шов¶
dmq — архітектурна частина, що перетворює Kamailio з «потужний single-box SIP-сервер» на «scale-out SIP-платформу». Кожна інша частина, про яку ви читали — process model, shm, lumps, transactions, dialogs, KEMI, topos, async, htable, dispatcher — існує на масштабі одного інстансу. dmq — шов, що дозволяє оперувати багатьма з них так, ніби це один. Жодна з них не дизайнилася під distribution зі старту; dmq додає це як opt-in retrofit, що чесно і працює.
Решта фішок виходять за межі самого message-path: 8.6 анкорить медіа-план через rtpengine, а 8.7 захоплює зашифроване сигналізування через siptrace. Після цього йде control plane (RPC, kamcmd, event-routes), потім Частина 9 — безпека і hardening — Частина 10 про Kamailio в IMS, і завершальний reference-розділ (глосарій ролей процесів, карта термінів, що нового).