A partir do sgcWebSockets 2022.9.0 há um novo IOHandler para Linux: usando EPOLL você pode evitar o problema "uma thread por cliente", em que o desempenho cai muito à medida que mais conexões são tratadas pelo servidor. O EPOLL disponibiliza algumas threads que gerenciam múltiplos clientes. As threads ficam suspensas e não consomem ciclos de CPU até que haja algo para processar.
O EPOLL IOHandler está disponível apenas no pacote sgcWebSockets Enterprise.
Configuração
O EPOLL para Linux é uma API que permite gerenciar milhares de conexões usando um pool limitado de threads, em vez de usar uma thread por conexão como o Indy faz por padrão.
Para habilitar o EPOLL nos servidores Indy, vá à propriedade IOHandlerOptions e selecione iohIEPOLL como IOHandler Type.
Server.IOHandlerOptions.IOHandlerType := iohEPOLL; Server.IOHandlerOptions.EPOLL.EPOLLThreads := 0; // the number of EPOLL threads will be calculated automatically using the number of processors. Server.IOHandlerOptions.EPOLL.WorkOpThreads := 0;
1. EPOLLThreads são as threads usadas para requisições assíncronas do EPOLL (operações sobrepostas). Por padrão, o valor é zero, o que significa que o número de threads é calculado com base no número de processadores (exceto para Delphi 7 e 2007, onde o número de threads é definido como 32 porque a função cpucount não é suportada). Você pode ajustar o número de threads manualmente.
2. WorkOpThreads deve ser habilitado apenas se você quiser que as conexões sejam sempre processadas na mesma thread. Ao usar EPOLL, as requisições são processadas por um pool de threads e cada requisição (da mesma conexão) pode ser processada em threads diferentes. Se quiser tratar cada conexão sempre na mesma thread, defina em WorkOpThreads o número de threads usadas para isso. Isso impacta o desempenho do servidor e só é recomendado definir um valor maior que zero se você realmente precisar desse recurso.
Habilitar o EPOLL nos servidores Linux é recomendado quando você precisa gerenciar milhares de conexões. Se o seu servidor trata no máximo 100 conexões simultâneas, você pode manter o modelo de threads padrão do Indy.
Teste de Desempenho
Um teste simples mostrará as diferenças entre o modelo de threads do Indy e o EPOLL. O teste conectará ao servidor usando o protocolo WebSocket e o uso de CPU e o consumo de memória serão mostrados na tabela a seguir. O percentual de uso de CPU é medido enquanto o servidor está em estado IDLE.
| Num. de Conexões | Indy IOHandler Default | Indy IOHandler EPOLL |
| 100 | 1% (1.4MB) | 0% (1.4MB) |
| 500 | 4% (5.6MB) | 0% (4.8MB) |
| 1000 | 10% (10.5MB) | 0% (8.9MB) |
| 1500 | -- (Failed) | 0% (13.3MB) |
| 2000 | -- (Failed) | 0% (17.4MB) |
O servidor Indy não conseguiu abrir mais de 1024 conexões simultâneas devido à limitação do método Select de aceitar mais de 1024 conexões simultâneas. Comparando os resultados até 1000 conexões, o servidor Indy padrão consumia mais CPU e mais RAM do que o servidor EPOLL. O servidor EPOLL não consumia CPU enquanto estava em estado idle e o consumo de memória também era menor.
O próximo teste mede o tempo necessário para conectar X clientes.
| Num. de Conexões | Indy IOHandler Default | Indy IOHandler EPOLL |
| 1000 | 16.5 seconds | 9.49 seconds |
| 10000 | -- (Failed) | 1min 55 seconds |
Os testes foram realizados no Ubuntu 20.0.4 com 2 processadores e 8 GB de RAM rodando em uma máquina virtual.
Documentação do EPOLL
https://man7.org/linux/man-pages/man7/epoll.7.html
Ótimos projetos Delphi implementando EPOLL
https://github.com/winddriver/Delphi-Cross-Socket
https://github.com/grijjy/GrijjyFoundation
