Como o Waitress lida com tarefas simultâneas?

8

Estou tentando criar um servidor web python usando Django e Waitress, mas gostaria de saber como o Waitress lida com solicitações simultâneas e quando o bloqueio pode ocorrer.


Embora a documentação do Waitress mencione que vários threads de trabalho estão disponíveis, ela não fornece muitas informações sobre como eles são implementados e como o GIL do python os afeta (ênfase minha):

Quando um canal determina que o cliente enviou pelo menos uma solicitação HTTP válida completa, ele agenda uma "tarefa" com um "despachante de encadeamentos". O distribuidor de encadeamentos mantém um conjunto fixo de encadeamentos de trabalho disponíveis para realizar o trabalho do cliente (por padrão, 4 encadeamentos). Se um segmento de trabalho estiver disponível quando uma tarefa for agendada, o segmento de trabalho executará a tarefa. A tarefa tem acesso ao canal e pode gravar de volta no buffer de saída do canal. Quando todos os segmentos de trabalho estiverem em uso , as tarefas agendadas aguardarão na fila para que um segmento de trabalho fique disponível.

Também não parece haver muita informação sobre o Stackoverflow. Da pergunta "O trabalhador assíncrono de Gunicorn é análogo a Garçonete?" :

A garçonete possui um encadeamento assíncrono mestre que armazena em buffer as solicitações e enfileira cada solicitação a um de seus encadeamentos de trabalho de sincronização quando a E / S da solicitação é concluída.


Essas declarações não abordam o GIL (pelo menos no meu entendimento) e seria ótimo se alguém pudesse elaborar mais sobre como os threads de trabalho funcionam para a Waitress. Obrigado!

MoltenMuffins
fonte
Você conseguiu uma solução para isso?
variável
@ variável Infelizmente não. Ao olhar brevemente para o repositório de garçons do github , parece que eles não fizeram nada para contornar o GIL, embora eu não possa dizer com certeza. No momento, minha equipe continua com o Waitress, pois nosso aplicativo não exige um nível de simultaneidade muito alto.
MoltenMuffins 6/03
Ao usar o servidor padrão do balão de desenvolvimento, podemos definir o número de processos usando werkzeug.palletsprojects.com/en/1.0.x/serving/… - isso não existe na garçonete?
variável
Sim, o número de trabalhadores pode ser configurado, mas isso não diz nada sobre seu comportamento de bloqueio
MoltenMuffins
Se um trabalhador significa um processo independente, isso significa que cada processo tem seu próprio interpretador python. não é?
variável

Respostas:

1

Veja como os servidores assíncronos controlados por eventos geralmente funcionam:

  • Inicie um processo e ouça as solicitações recebidas. A utilização da API de notificação de eventos do sistema operacional facilita muito o atendimento a milhares de clientes a partir de um único encadeamento / processo.
  • Como existe apenas um processo gerenciando todas as conexões, você não deseja executar nenhuma tarefa lenta (ou bloqueadora) nesse processo. Porque então ele irá bloquear o programa para cada cliente.
  • Para executar tarefas de bloqueio, o servidor delega as tarefas para "trabalhadores". Os trabalhadores podem ser threads (executando no mesmo processo) ou processos separados (ou subprocessos). Agora, o processo principal pode continuar atendendo os clientes enquanto os trabalhadores executam as tarefas de bloqueio.

Como o Waitress lida com tarefas simultâneas?

Praticamente da mesma maneira que acabei de descrever acima. E para os trabalhadores, ele cria threads, não processos.

como o python GIL os afeta

Garçonete usa threads para trabalhadores. Então, sim, eles são afetados pelo GIL, pois não são realmente concorrentes, embora pareçam ser. "Assíncrono" é o termo correto.

Os threads no Python são executados em um único processo, em um único núcleo da CPU e não são executados em paralelo. Um encadeamento adquire o GIL por um período muito pequeno e executa seu código e, em seguida, o GIL é adquirido por outro encadeamento.

Mas como o GIL é lançado na E / S de rede, o processo pai sempre o adquirirá sempre que houver um evento de rede (como uma solicitação de entrada) e, dessa forma, você pode ter certeza de que o GIL não afetará as operações ligadas à rede ( como receber solicitações ou enviar respostas).

Por outro lado, os processos Python são realmente concorrentes: eles podem ser executados em paralelo em vários núcleos. Mas a garçonete não usa processos.

Você deveria estar preocupado?

Se você está apenas executando pequenas tarefas de bloqueio, como leitura / gravação em banco de dados e atendendo apenas algumas centenas de usuários por segundo, o uso de threads não é tão ruim assim.

Para atender a um grande volume de usuários ou executar tarefas de bloqueio de longa execução, você pode usar filas de tarefas externas como o Aipo . Isso será muito melhor do que gerar e gerenciar processos você mesmo.

xyres
fonte
É melhor usar um servidor de aplicativos baseado em processo para processar mais solicitações?
variável
@ variável Se você estiver executando tarefas vinculadas à CPU (também conhecidas como tarefas de bloqueio) como cálculos pesados, sim, é melhor usar trabalhadores do processo. Mas existem projetos como o Aipo que ajudam a executar tarefas de bloqueio em "filas de tarefas" separadas. Portanto, não importa que tipo de servidor de aplicativos você esteja usando. Mas apenas para executar tarefas ligadas à rede (como aguardar solicitações de clientes ou buscar dados de API de terceiros), você não precisa de trabalhadores.
xyres 13/03
@variable E se por servidor "baseado em processo" você quis dizer um servidor que cria um novo processo para cada solicitação, então não, essa é a maneira menos escalável. A maneira mais eficiente (e comum) é o que descrevi na parte superior da resposta: atender a todas as solicitações de um único processo principal e delegar tarefas de bloqueio aos trabalhadores (threads ou subprocessos).
xyres 13/03
Por "delegar tarefas de bloqueio a trabalhadores (threads ou subprocessos)" - você quer dizer aipo?
variável
@ variável Você pode manter um conjunto de subprocessos em seu programa e passar a eles as tarefas de bloqueio. Para projetos menores, essa abordagem é boa. O aipo lhe dará a vantagem da fácil escalabilidade. Você pode executá-lo facilmente em um único servidor ou em um cluster de servidores, dependendo de suas necessidades. Para projetos menores, pode ser um exagero. Você pode mudar para o Aipo, se e quando precisar.
xyres 13/03