SMPP-провайдеры по умолчанию держат жёсткий потолок по скорости — 30 SMS в секунду (Vonage). Если в пик рассылка упирается в этот лимит, SMSC отвечает кодом 0x00000058 (Throttling error, десятиричное значение 88) и просто отбрасывает сообщение. Решение — graceful degradation: вместо бинарного «отправил/не отправил» система сама снижает скорость, переключает канал или складывает сообщения в очередь. В статье — практический сценарий, как собрать такую логику на стороне клиента без дорогих анализаторов и без потери сообщений.
Почему операторы вообще вводят лимиты и откуда берётся код 88
Каждый SMSC защищает собственные ресурсы: транзакции, базы пользователей, маршрутизаторы. У SMPP-аккаунта есть «потолок» — он называется TPS, transactions per second, сделки в секунду. По умолчанию у большинства провайдеров это 10–30 SMS в секунду, и цифра зависит от тарифа (Vonage, SMPP Center).
Когда клиент отправляет больше, SMSC возвращает NACK с кодом Throttling error. Сообщение теряется на стороне клиента, если тот не повторит попытку. Параллельно у SMSC есть ещё один лимит — число неподтверждённых PDU, которые клиент держит в окне (window size). Если поднимать TPS, не трогая размер окна, число ошибок только растёт (SMSGatewayCenter).
Что должна делать система при первом throttle-ответе
Главное — не паниковать и не выкидывать сообщение. Сценарий graceful degradation строится на трёх реакциях: пауза, повтор, откат на резервный канал.
- Пауза. Получили NACK с кодом 88 — ставим экспоненциальную задержку перед следующим submit_sm. Стартовая пауза 100 мс, удвоение при каждом повторе до потолка в 5 секунд.
- Повтор. Помечаем сообщение флагом retry_count, отправляем снова, но не больше трёх попыток. После трёх NACK переводим в dead-letter queue.
- Откат. Если retries исчерпаны и TPS не падает — переключаемся на резервный канал или снижаем скорость до 50% от текущей. Снижение делаем постепенно, чтобы не устроить «качели» между throttle и нормальной работой.
Как выбрать окно и TPS, чтобы не упереться в лимит
Перед настройкой важно понять, что именно сейчас узкое место. Их три, и путать их — самая частая ошибка (SMSGatewayCenter):
| Параметр | Что ограничивает | Типичный дефолт |
|---|---|---|
| TPS аккаунта | Скорость отправки, которую разрешил провайдер | 10–30 SMS/с |
| Window size | Число неподтверждённых submit_sm в очереди | Задаётся в bind_transceiver |
| Downstream capacity | Потолок SMSC по конкретному маршруту в пиковые часы | Ниже TPS аккаунта |
Если видите серию throttle-ошибок при умеренной нагрузке — проверьте, не упираетесь ли в downstream. Поднять window size, когда узкое место TPS аккаунта, бесполезно: получите больше ошибок, а не больше доставок.
Как собрать очередь с приоритетами, а не «всё в одну кучу»
В пиковой нагрузке помогает не одна большая очередь, а несколько с разным приоритетом. Транзакционные SMS (коды подтверждения, уведомления об оплате) уходят первыми. Маркетинговые рассылки ждут своей очереди и снимаются при появлении свободных слотов.
Минимальный набор очередей:
- High. OTP, пароли, уведомления по статусу заказа. Жёсткий SLA в секундах.
- Medium. Сервисные уведомления — доставка, баланс, изменение тарифа.
- Low. Маркетинговые кампании. Отправляются, когда есть запас по TPS, остаток — в очередь следующего окна.
Если приоритетная очередь занимает больше 80% доступного TPS, маркетинговая автоматически встаёт на паузу. Это и есть graceful degradation: ничего не теряется, просто меняется порядок и скорость.
Зачем нужен enquire_link и какие паузы реально опасны
Параллельно с throttling у SMPP есть второй тихий враг — inactivity timeout. Сервер закрывает сессию, если за 120 секунд не пришёл ни один PDU, включая enquire_link (Mobile Text Alerts). Без keepalive клиент теряет соединение и долго восстанавливает его в пик нагрузки.
Рабочий диапазон — отправлять enquire_link каждые 30–60 секунд. Если соединение рвётся чаще, чем раз в час, проверьте сетевой путь и таймауты на firewall: NAT любят «забывать» неактивные TCP-сессии.
Типичные ошибки, которые ломают graceful degradation
- Сразу резать TPS в ноль при первом throttle-ответе. Сеть «качается», и через минуту вы уже недоиспользуете канал.
- Хранить очередь только в памяти процесса. При рестарте контейнера сообщения исчезают, и клиент думает, что они доставлены.
- Игнорировать разницу между Throttling error и просто ошибкой маршрутизации. Код 0x00000058 лечится паузой, остальные — нет.
- Забывать про enquire_link во время длительной паузы. SMSC закрывает сессию, а выясняется это только по следующей submit_sm.
- Повышать window size, не проверив TPS аккаунта. Получаете больше throttle-ответов и больше нагрузки на собственный retry-loop.
3 шага, которые можно сделать на этой неделе
- Поднять в логах отметку по коду 0x00000058 и собрать статистику — в какие часы и на каких маршрутах он срабатывает чаще.
- Внедрить очередь с приоритетами и экспоненциальную паузу на retry, чтобы маркетинг не вытеснял транзакционные сообщения.
- Настроить enquire_link каждые 30–60 секунд и проверить таймауты на NAT — иначе пауза сама убьёт соединение.



