Anatomia de um encerramento de sessão: debugando LiveZilla, PHP e sockets no Linux
Voltar para blog

Anatomia de um encerramento de sessão: debugando LiveZilla, PHP e sockets no Linux

Atualizado em 20/09/2026 · Publicado em 19/09/2026 · 5 min · Infraestrutura

Ao lidar com servidores web em produção (Apache, PHP-FPM e Nginx), frequentemente surgem comportamentos que parecem simples na interface da aplicação, mas revelam uma mecânica complexa nas camadas inferiores do sistema operacional.

Recentemente, investiguei como o LiveZilla gerencia o encerramento de sessões e a liberação de conexões ativas. À primeira vista, parece apenas um botão de fechar chat no navegador, mas o ciclo real envolve gatilhos no PHP, transações no MySQL, estados de sockets no kernel Linux e gerenciamento de descritores de arquivo.

Abaixo, compartilho a análise técnica das chamadas de sistema (syscalls), o impacto em memória e I/O, e como diagnosticar conexões fantasmas que se recusam a morrer.


1. Como uma sessão é encerrada na prática#

No LiveZilla, uma sessão pode ser finalizada por três caminhos:

A. Comando explícito do atendente ou cliente#

  1. O navegador despacha um POST para server.php com parâmetros de ação (como p_act=close_chat).
  2. O PHP processa a instrução e executa um UPDATE no MySQL (tabelas como lz_chat_posts e lz_chat_sessions), marcando o atendimento como fechado.
  3. Se a comunicação utilizar Long Polling, a requisição pendente é respondida e o loop no navegador é interrompido via JavaScript.

B. Timeout de inatividade (garbage collection lógico)#

Quando o visitante fecha o navegador sem clicar em sair:

C. WebSockets e camada de transporte#

Quando o sistema opera com WebSockets:


2. Impacto em i/o, sistema de arquivos e arquivos de sessão#

Durante o encerramento da conversa, ocorrem operações que merecem atenção em ambientes movimentados:

  1. Gravação de histórico: O buffer de mensagens é serializado e gravado em tabelas de histórico ou enviado por e-mail. Se o nível de depuração estiver ativado, ocorrem chamadas síncronas fopen() e fwrite().
  2. Arquivos de sessão e Inodes: O session_destroy() do PHP remove o arquivo temporário de sessão em disco (sess_*). Se o garbage collection do PHP (session.gc_probability) estiver desativado ou mal ajustado, o acúmulo de arquivos antigos pode esgotar a tabela de inodes do disco.

3. Investigando conexões presas em CLOSE_WAIT#

Um dos problemas mais comuns em servidores é quando a aplicação diz que fechou o atendimento, mas a conexão TCP permanece aberta em CLOSE_WAIT.

Por que o estado CLOSE_WAIT acontece?#

Quando o cliente remoto encerra a conexão, o computador dele envia um pacote FIN. O kernel Linux do servidor responde com um ACK e coloca o socket local no estado CLOSE_WAIT. A partir desse momento, o sistema operacional espera que o processo da aplicação (Apache ou PHP-FPM) invoque a syscall close().

Se o PHP travar em uma consulta pesada no banco ou estourar o tempo de execução, o socket nunca é fechado, ocupando slots de conexão no servidor.

Comandos de diagnóstico no terminal#

Para identificar conexões presas em CLOSE_WAIT nas portas web de forma canônica:

ss -tanp state close-wait '( sport = :http or sport = :https )'

Para verificar quais descritores de rede estão abertos por um IP específico (capturando indistintamente processos Apache, PHP-FPM ou Nginx):

lsof -nPi | grep 192.0.2.10

Para inspecionar as chamadas de sistema em tempo real de um worker do PHP-FPM suspeito:

strace -p <PID_DO_PHP_FPM> -e trace=network,file,desc

Na saída do strace, você acompanha o ciclo completo: recvfrom() (recebendo a requisição de fechamento), sendto() (persistindo no MySQL) e o close() final no descritor do socket.


4. Ajustes práticos no servidor web e kernel#

Dois cenários merecem atenção redobrada:

Ajustes recomendados:#

A. Ajustes na camada da aplicação (o único remédio real para CLOSE_WAIT)#

Como o kernel Linux não possui timer para fechar sockets em CLOSE_WAIT, a liberação depende exclusivamente de a aplicação fechar o descritor de arquivo via close():

B. Ajuste de TCP no Kernel (sysctl)#

# /etc/sysctl.d/99-tcp-tuning.conf
net.ipv4.tcp_keepalive_time = 600
net.ipv4.tcp_fin_timeout = 30

Aplique as mudanças com sudo sysctl -p /etc/sysctl.d/99-tcp-tuning.conf.

Monitorar esse ciclo com ss, lsof e strace garante que a aplicação encerre conversas sem deixar sockets órfãos acumulando memória no servidor.


Conclusão#

Neste artigo analisei o ciclo completo de encerramento de uma sessão do LiveZilla — do UPDATE no MySQL ao close() no socket — e mostrei que um CLOSE_WAIT persistente quase nunca é problema de kernel, mas de aplicação que não devolve o descritor. Com timeouts bem calibrados no PHP-FPM e monitoramento de sockets ativos, o servidor mantém seu pool saudável mesmo sob tráfego concorrente intenso.

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários