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
- Índice Completo da Documentação - Comece aqui para toda a documentação
- Referência de Configuração - Opções de configuração completas
- Referência da API - Documentação da API REST
- Guia de Operações - Operações e monitoramento do dia a dia
- Guia de Roteamento de SMS - Gerenciamento e configuração de rotas
- Guia de Tradução de Números - Normalização e reescrita de números
- Ajuste de Desempenho - Otimização para diferentes cargas de trabalho
- Guia de Métricas - Métricas e monitoramento do Prometheus
- Guia de Solução de Problemas - Problemas comuns e soluções
- Esquema CDR - Formato de registro de detalhes de chamadas
Documentação de Conformidade
- Conformidade de Interceptação ANSSI R226 - Especificações técnicas de interceptação legal na França
Desempenho
- Benchmarks - Testes de 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(ouhttp://localhost:8080sem 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:
1440minutos (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 destinomessage_body- Conteúdo do texto da mensagemsource_msisdn- Número de telefone de origemsource_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 IMSItp_dcs_character_set- Codificação de caracteres (gsm7, 8bit, latin1, ucs2)tp_dcs_coding_group- Grupo de codificação DCStp_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 mensagemtp_user_data_header- Cabeçalho de dados do usuário (mapa)message_part_number,message_parts- Campos de mensagem multipartexpires- Timestamp de expiração (padrão para 24 horas)deliver_after- Timestamp de entrega atrasadadeadletter,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_smsccorresponde ao valor do cabeçalho OUdest_smscé nulodeliver_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:
| Tentativa | Fórmula de Backoff | Atraso | Tempo Total |
|---|---|---|---|
| 1ª | 2^1 minutos | 2 min | 2 min |
| 2ª | 2^2 minutos | 4 min | 6 min |
| 3ª | 2^3 minutos | 8 min | 14 min |
| 4ª | 2^4 minutos | 16 min | 30 min |
| 5ª | 2^5 minutos | 32 min | 1h 2min |
| 6ª | 2^6 minutos | 64 min | 2h 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:
- Intervalo de Heartbeat: Frontends devem registrar a cada 30-60 segundos
- Tempo de Expiração: Registros expiram após 90 segundos sem uma atualização
- 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 frontendfrontend_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 frontenduptime_seconds- Segundos desde que o frontend foi iniciadoconfiguration- 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_URLestá 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:
- Logs do frontend para erros de envio
- Conectividade da rede externa
- Configuração do frontend (credenciais, endereços)
- 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:
- Intervalo de polling do frontend (pode ser necessário diminuir para polling mais frequente)
- Desempenho do banco de dados
- Latência de rede para sistemas externos
Monitorar profundidade da fila:
watch -n 5 'curl -s https://localhost:8443/api/messages | jq ".data | length"'