Pular para o conteúdo principal

SMSc - Aplicativo do Centro de Serviço de SMS

Aplicativo do Centro de SMS construído com Phoenix/Elixir, fornecendo uma fila de mensagens centralizada e uma API REST para roteamento e entrega de SMS.

Documentação

Documentação Principal

Documentação de Conformidade

Desempenho

Visão Geral da Arquitetura

O núcleo do SMS_C fornece uma fila de mensagens independente de protocolo e uma API REST. Frontends externos de SMSC (SMPP, IMS, SS7/MAP) se conectam como gateways independentes que se comunicam com o núcleo via API REST.

Fluxo de Mensagens

Fluxo de Mensagens de Saída (MT - Mobile Terminated)

Fluxo de Mensagens de Entrada (MO - Mobile Originated)

Principais Recursos

1. Design Independente de Protocolo

  • O núcleo SMS_C lida com persistência de mensagens, roteamento e API
  • Frontends externos (SMPP, IMS, SS7/MAP) lidam com comunicação específica do protocolo
  • Todos os frontends se comunicam via API REST unificada
  • Adicione novos protocolos sem alterar o núcleo
  • Cada um pode ser escalado independentemente

2. Roteamento de Mensagens

  • Motor de roteamento dinâmico com configuração em tempo de execução
  • Roteamento baseado em prefixo (números chamadores/chamados)
  • Filtragem de SMSC e tipo de origem (IMS/Circuit Switched/SMPP)
  • Roteamento baseado em prioridade com balanceamento de carga baseado em peso
  • Capacidade de roteamento de resposta automática e descarte de mensagens
  • Controle de cobrança por rota
  • UI Web para gerenciamento de rotas
  • Atualizações de rotas em tempo real sem interrupção do serviço

📖 Veja Guia de Roteamento de SMS para documentação abrangente

3. Lógica de Retentativa com Backoff Exponencial

  • Retentativa automática em caso de falha na entrega
  • Backoff exponencial: 1min, 2min, 4min, 8min, etc.
  • Tentativas máximas de retentativa configuráveis
  • Tratamento de expiração de mensagens
  • Rastreamento de retentativa por mensagem

Guia de Operações

Pontos de Acesso:

  • API REST: https://localhost:8443 (ou http://localhost:8080 sem TLS)
  • Painel de Controle: https://localhost:8086
  • Documentação da API (Swagger UI): https://localhost:8443/api/docs

Iniciar Frontends Externos: Cada frontend de protocolo é um aplicativo independente. Veja a documentação individual do frontend para instruções de inicialização.

Configuração

Toda a configuração é gerenciada diretamente em config/runtime.exs. Nenhuma variável de ambiente é utilizada.

Configuração do Núcleo

Nenhuma variável de ambiente de configuração do aplicativo núcleo está atualmente em uso. As portas do servidor e os nomes dos hosts são configurados em config/runtime.exs:

  • Servidor API: Porta 8443 (HTTPS), ouvindo em 0.0.0.0
  • Painel de Controle: Porta 80 (HTTP), ouvindo em 0.0.0.0

Configuração do Banco de Dados

As configurações do banco de dados são configuradas em config/runtime.exs:

  • Nome de usuário: omnitouch
  • Senha: omnitouch2024
  • Nome do host: localhost
  • Porta: 3306
  • Nome do banco de dados: smsc_new
  • Tamanho do pool: 1

Configuração do Cluster

As configurações do cluster são configuradas em config/runtime.exs:

  • Nós do cluster: "" (vazio - sem clustering por padrão)
  • Consulta de cluster DNS: nil

Configuração da Fila de Mensagens

As configurações da fila de mensagens são configuradas em config/runtime.exs:

  • Tempo de carta morta: 1440 minutos (24 horas antes da expiração da mensagem)

Integração de Cobrança

As configurações de cobrança são configuradas em config/runtime.exs:

  • URL: http://localhost:2080/jsonrpc
  • Inquilino: mnc057.mcc505.3gppnetwork.org
  • Destino: 55512341234
  • Origem: 00101900000257
  • Assunto: 00101900000257
  • Conta: 00101900000257

Configuração de Roteamento de SMS

O sistema de roteamento de SMS utiliza rotas dinâmicas, apoiadas por banco de dados, que podem ser gerenciadas via UI Web ou arquivo de configuração. As rotas são carregadas de config/runtime.exs na primeira inicialização.

Exemplo de Configuração:

config :sms_c, :sms_routes, [
%{
called_prefix: "+44",
dest_smsc: "InternationalGW",
weight: 100,
priority: 100,
description: "SMS Internacional do Reino Unido",
enabled: true
},
%{
called_prefix: "1900",
dest_smsc: "PremiumGW",
charged: :yes,
priority: 50,
description: "Números Premium dos EUA",
enabled: true
}
]

Recursos:

  • Correspondência baseada em prefixo (números chamadores/chamados)
  • Filtragem de SMSC de origem e tipo
  • Roteamento baseado em prioridade e peso
  • Capacidade de resposta automática e descarte
  • Controle de cobrança por rota
  • Gerenciamento em tempo de execução via UI Web em /routing

📖 Veja Guia de Roteamento de SMS para documentação completa, exemplos e referência da API.

Endpoints da API REST

Operações na Fila de Mensagens

Enviar SMS (Criar Mensagem)

POST /api/messages
Content-Type: application/json

{
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Olá, Mundo!",
"source_smsc": "web-app",
"dest_smsc": "smpp-provider", # Opcional - motor de roteamento atribui se nulo
"tp_dcs_character_set": "gsm7", # Opcional: gsm7, 8bit, latin1, ucs2
"tp_dcs_coding_group": "general_data_coding",
"expires": "2025-10-17T10:30:00Z" # Opcional - padrão para 24h a partir de agora
}

Campos Obrigatórios:

  • destination_msisdn - Número de telefone de destino
  • message_body - Conteúdo do texto da mensagem
  • source_msisdn - Número de telefone de origem
  • source_smsc - Identificador do sistema de origem

Campos Opcionais:

  • dest_smsc - Gateway de destino (motor de roteamento atribui se não fornecido)
  • source_imsi, dest_imsi - Identificadores IMSI
  • tp_dcs_character_set - Codificação de caracteres (gsm7, 8bit, latin1, ucs2)
  • tp_dcs_coding_group - Grupo de codificação DCS
  • tp_dcs_compressed - Sinalizador de compressão (booleano)
  • tp_dcs_has_message_class - Sinalizador de classe de mensagem (booleano)
  • tp_dcs_message_class - Valor da classe de mensagem
  • tp_user_data_header - Cabeçalho de dados do usuário (mapa)
  • message_part_number, message_parts - Campos de mensagem multipart
  • expires - Timestamp de expiração (padrão para 24 horas)
  • deliver_after - Timestamp de entrega atrasada
  • deadletter, raw_data_flag, raw_sip_flag - Sinalizadores booleanos

Resposta:

{
"status": "success",
"data": {
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"dest_smsc": "smpp-provider",
"message_body": "Olá, Mundo!",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
}

Obter Mensagens para SMSC

GET /api/messages/get_by_smsc?smsc=meu-nome-smsc

Retorna todas as mensagens não entregues onde:

  • destination_smsc é nulo OU corresponde ao nome SMSC fornecido
  • A mensagem não está expirada
  • Pronta para enviar (deliver_after é nulo ou no passado)

Resposta:

{
"status": "success",
"data": [
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Olá",
"destination_smsc": "meu-nome-smsc",
"delivery_attempts": 0
}
]
}

Listar Mensagens com Filtragem Opcional de SMSC

# Listar todas as mensagens na fila
GET /api/messages

# Listar mensagens para SMSC específico (com filtragem de cabeçalho)
GET /api/messages
SMSc: meu-nome-smsc

Sem Cabeçalho SMSc: Retorna todas as mensagens na fila, independentemente do status de entrega ou expiração.

Com Cabeçalho SMSc: Retorna mensagens não entregues onde:

  • dest_smsc corresponde ao valor do cabeçalho OU dest_smsc é nulo
  • deliver_time é nulo (não entregue ainda)
  • deliver_after é nulo ou antes/igual ao tempo atual (pronto para entrega)
  • expires é após o tempo atual (não expirada)
  • Ordenado pelo tempo de inserção (mais antigo primeiro)

Nota: A abordagem do cabeçalho SMSc permite que frontends externos consultem suas mensagens usando o mesmo padrão de endpoint, com o cabeçalho controlando o comportamento de filtragem.

Resposta:

[
{
"id": 123,
"source_msisdn": "+1234567890",
"destination_msisdn": "+0987654321",
"message_body": "Olá, Mundo!",
"dest_smsc": "meu-nome-smsc",
"deliver_time": null,
"delivery_attempts": 0,
"expires": "2025-10-17T10:30:00Z",
"inserted_at": "2025-10-16T10:30:00Z"
}
]

Obter Mensagem Única

GET /api/messages/{id}

Atualizar Mensagem

PATCH /api/messages/{id}
Content-Type: application/json

{
"status": "entregue",
"delivered_at": "2025-10-16T10:30:00Z"
}

Deletar SMS

DELETE /api/messages/{id}

Lidar com Falha na Entrega (Incrementar Contador de Retentativas)

Quando a entrega de uma mensagem falha temporariamente, incremente o contador de tentativas de entrega e agende uma retentativa com backoff exponencial.

Método 1: Usando PUT (Recomendado)

# Simples e semântico - PUT indica atualização do estado de entrega
PUT /api/messages/{id}

Método 2: Usando Endpoint Explícito

# Endpoint alternativo explícito
POST /api/messages/{id}/increment_delivery_attempt

Ambos os métodos incrementam delivery_attempts e definem o atraso de backoff exponencial via deliver_after:

TentativaFórmula de BackoffAtrasoTempo Total
2^1 minutos2 min2 min
2^2 minutos4 min6 min
2^3 minutos8 min14 min
2^4 minutos16 min30 min
2^5 minutos32 min1h 2min
2^6 minutos64 min2h 6min

Resposta:

{
"id": 123,
"delivery_attempts": 1,
"deliver_after": "2025-10-20T19:05:00Z",
"deliver_time": null,
"expires": "2025-10-21T19:03:00Z",
...
}

Nota: Mensagens com deliver_after futuro são automaticamente filtradas de solicitações GET até que o período de backoff expire.

Atualizar Mensagem (Atualização Parcial)

Para atualizar campos específicos da mensagem (comportamento inalterado):

PATCH /api/messages/{id}
Content-Type: application/json

{
"dest_smsc": "gateway-atualizado",
"status": "entregue"
}

Importante: PUT e PATCH se comportam de maneira diferente:

  • PUT → Incrementa tentativas de entrega com backoff (sem corpo necessário)
  • PATCH → Realiza atualizações parciais de campo (corpo necessário)

Monitoramento de Saúde do Frontend

O núcleo SMS_C rastreia a saúde e a disponibilidade dos frontends externos por meio de um sistema de registro. Isso permite monitorar o tempo de atividade do frontend, detectar falhas e manter dados históricos de disponibilidade.

Nota: O registro do frontend NÃO é usado para entrega ou roteamento de mensagens. As mensagens são roteadas com base no campo dest_smsc. O sistema de registro existe apenas para monitoramento de saúde e visibilidade operacional.

Como Funciona o Registro do Frontend

Cada frontend externo (gateway SMPP, IMS, SS7/MAP) envia periodicamente um registro de heartbeat para o núcleo SMS_C:

  1. Intervalo de Heartbeat: Frontends devem registrar a cada 30-60 segundos
  2. Tempo de Expiração: Registros expiram após 90 segundos sem uma atualização
  3. Gerenciamento Automático de Estado:
    • Novos frontends criam um novo registro de registro
    • Frontends ativos existentes atualizam seu registro (estendem a expiração)
    • Frontends expirados que voltam online criam um novo período de registro

Endpoints de Registro do Frontend

Registrar/Atualizar Frontend (Heartbeat)
POST /api/frontends
Content-Type: application/json

{
"frontend_name": "gateway-sMPP-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"configuration": "{\"port\": 2775, \"system_id\": \"usuario_sMPP\"}"
}

Campos Obrigatórios:

  • frontend_name - Identificador único para a instância do frontend
  • frontend_type - Tipo de frontend (SMPP, IMS, MAP, etc.)

Campos Opcionais:

  • ip_address - Endereço IP do frontend (detectado automaticamente a partir da origem da solicitação se não fornecido)
  • hostname - Nome do host do servidor frontend
  • uptime_seconds - Segundos desde que o frontend foi iniciado
  • configuration - String JSON com configuração específica do frontend

Nota: Se ip_address não for fornecido, o núcleo SMS_C usará automaticamente o IP de origem da solicitação HTTP. Isso funciona com conexões diretas e solicitações proxy (via cabeçalho X-Forwarded-For).

Resposta:

{
"id": 42,
"frontend_name": "gateway-sMPP-1",
"frontend_type": "SMPP",
"ip_address": "10.0.1.5",
"hostname": "smpp-gw-01",
"uptime_seconds": 3600,
"status": "ativo",
"last_seen_at": "2025-10-20T10:30:00Z",
"expires_at": "2025-10-20T10:31:30Z",
"inserted_at": "2025-10-20T10:00:00Z"
}
Listar Todos os Registros de Frontend
GET /api/frontends

Retorna todos os registros de frontend (ativos e expirados), ordenados pela atividade mais recente.

Listar Apenas Frontends Ativos
GET /api/frontends/active

Retorna apenas frontends atualmente ativos (não expirados).

Obter Estatísticas do Frontend
GET /api/frontends/stats

Retorna estatísticas resumidas:

{
"active": 5,
"expired": 12,
"unique_frontends": 8
}
Obter Histórico do Frontend
GET /api/frontends/history/{frontend_name}

Retorna todos os registros históricos para um frontend específico, útil para analisar padrões de tempo de atividade/inatividade.

Exemplo:

GET /api/frontends/history/gateway-sMPP-1
Obter Registro Específico
GET /api/frontends/{id}

Implementação em Frontends Externos

Frontends externos devem implementar uma tarefa em segundo plano que envia heartbeats:

Exemplo (pseudocódigo):

import time
import requests

def send_heartbeat():
"""Enviar heartbeat a cada 30 segundos"""
while True:
try:
data = {
"frontend_name": "meu-gateway-sMPP",
"frontend_type": "SMPP",
"ip_address": get_local_ip(),
"hostname": get_hostname(),
"uptime_seconds": get_uptime()
}

response = requests.post(
"https://smsc-core:8443/api/frontends",
json=data,
timeout=5
)

if response.status_code in [200, 201]:
logger.debug("Heartbeat enviado com sucesso")
else:
logger.error(f"Falha no heartbeat: {response.status_code}")

except Exception as e:
logger.error(f"Erro no heartbeat: {e}")

time.sleep(30) # Enviar a cada 30 segundos

# Iniciar heartbeat em thread de fundo
threading.Thread(target=send_heartbeat, daemon=True).start()

Monitoramento da Saúde do Frontend

Painel de Controle - A UI web em https://localhost:8086 mostra:

  • Frontends atualmente ativos
  • Timestamp da última visualização para cada frontend
  • Rastreamento de tempo de atividade
  • Disponibilidade histórica

Consultas da API:

# Obter todos os frontends ativos
curl https://localhost:8443/api/frontends/active

# Verificar se um frontend específico está ativo
curl https://localhost:8443/api/frontends/history/gateway-sMPP-1 | jq '.[0].status'

# Obter estatísticas de saúde
curl https://localhost:8443/api/frontends/stats

Outros Endpoints

Status

GET /api/status

Localizações

GET /api/locations
POST /api/locations
GET /api/locations/{id}
PATCH /api/locations/{id}
DELETE /api/locations/{id}

Eventos SS7

GET /api/ss7_events
POST /api/ss7_events
GET /api/ss7_events/{id}
PATCH /api/ss7_events/{id}
DELETE /api/ss7_events/{id}

Fila de Mensagens MMS

GET /api/mms_message_queues
POST /api/mms_message_queues
GET /api/mms_message_queues/{id}
PATCH /api/mms_message_queues/{id}
DELETE /api/mms_message_queues/{id}

Desempenho

O núcleo SMS_C oferece um desempenho excepcional usando Mnesia para armazenamento de mensagens em memória com arquivamento automático para SQL para retenção de CDR a longo prazo.

Resultados de Benchmark

Medido em Intel i7-8650U @ 1.90GHz (8 núcleos):

Desempenho de Inserção de Mensagens:

  • insert_message (com roteamento): 1.750 msg/sec (0,58ms de latência média)
  • insert_message (simples): 1.750 msg/sec (0,57ms de latência média)
  • Capacidade de ~150 milhões de mensagens por dia

Desempenho de Consulta:

  • get_messages_for_smsc: 800 msg/sec (1,25ms em média)
  • list_message_queues: Acesso rápido em memória
  • Uso de memória: 62 KB por operação de inserção

Arquitetura

Estratégia de Armazenamento:

  • Mensagens Ativas: Armazenadas em Mnesia (em memória + disco) para acesso ultra-rápido
  • Arquivo de Mensagens: Arquivadas automaticamente para SQL para armazenamento de CDR a longo prazo
  • Retenção: Período de retenção configurável (padrão: 24 horas)
  • Sem gargalo SQL: Todas as operações de mensagens ativas ignoram SQL

Configuração

Armazenamento e retenção de mensagens configurados em config/runtime.exs:

config :sms_c,
message_retention_hours: 24, # Arquivar mensagens mais antigas que 24 horas
batch_insert_batch_size: 100, # Tamanho do lote de CDR para arquivamento SQL
batch_insert_flush_interval_ms: 100 # Intervalo de flush de CDR

Para orientações detalhadas de ajuste, veja: docs/PERFORMANCE_TUNING.md

Monitoramento

Painel de Controle - UI Web em https://localhost:8086

  • Visualizar fila de mensagens
  • Enviar mensagens de teste
  • Gerenciar roteamento de SMS (veja Guia de Roteamento)
  • Simular decisões de roteamento
  • Visualizar recursos do sistema
  • Rastrear estatísticas de trabalhadores em lote

Estatísticas do Trabalhador em Lote:

# Obter estatísticas atuais do trabalhador em lote
SmsC.Messaging.BatchInsertWorker.stats()

Retorna:

%{
total_enqueued: 10000,
total_flushed: 9900,
current_queue_size: 100,
last_flush_duration_ms: 45
}

Logs - Logs da aplicação escritos no stdout

# Visualizar logs em tempo real
tail -f log/dev.log

Solução de Problemas

Porta Já em Uso

# Encontrar processo usando a porta
lsof -i :4000

# Matar o processo
kill -9 <PID>

Frontend Externo Não Conectando

Sintomas: Mensagens presas na fila, logs do frontend mostram erros de conexão

Verifique:

  • Verifique se API_BASE_URL está configurado corretamente no frontend
  • Verifique se o núcleo SMS_C está em execução e acessível
  • Revise as regras de rede/firewall
  • Verifique a configuração do frontend

Solução:

# Testar conectividade da API a partir do frontend
curl http://localhost:4000/api/status

# Reiniciar frontend
export API_BASE_URL="http://localhost:4000"
# Iniciar aplicação frontend

Mensagens Não Sendo Entregues

Sintomas: Mensagens permanecem não entregues, tentativas de retentativa aumentando

Verifique:

  1. Logs do frontend para erros de envio
  2. Conectividade da rede externa
  3. Configuração do frontend (credenciais, endereços)
  4. Compatibilidade do formato da mensagem

Visualizar mensagens com falha:

# Obter mensagens com tentativas de retentativa
curl https://localhost:8443/api/messages | jq '.data[] | select(.delivery_attempts > 0)'

Alta Latência de Mensagem

Sintomas: Mensagens levando mais tempo que o esperado, backlog na fila

Verifique:

  1. Intervalo de polling do frontend (pode ser necessário diminuir para polling mais frequente)
  2. Desempenho do banco de dados
  3. Latência de rede para sistemas externos

Monitorar profundidade da fila:

watch -n 5 'curl -s https://localhost:8443/api/messages | jq ".data | length"'