Este é um artigo do mundo Python, mas ainda é aplicável a todo o campo de programação, embora o multithreading nos permita processar requisições mais rápido, mas também há um teto, threads verdes (microthreads) são a solução.
O desenvolvimento de software multithread resolve uma grande quantidade de problemas, especialmente para aplicações centradas em rede que exigem desempenho exigente para responder rapidamente aos usuários. Infelizmente, multithreading não é suficiente para resolver grandes escalasConcorrênciaProblemas sexuais.
Resolver essas questões requer mudanças nos modelos de programação, utilizando eventos assíncronos e mecanismos baseados em callback. Na Druva, criamos uma biblioteca em Python chamada Dhaga para resolver questões em grande escalaConcorrência, enquanto o modelo de programação não exige mudanças significativas.
Desenvolvedores de software vivem em um sóConcorrênciamundo. Os Threads são cidadãos de primeira classe hoje, especialmente durante o desenvolvimento, especialmente quando sua aplicação realiza operações intensivas de rede, como o sistema inSync (produto de sincronização de segurança de rede) como o Druva. O multithreading facilita e ordenado o fluxo do código de programação para operações de rede. Quando nossa aplicação precisa de melhorias ou melhorias de desempenho, ela pode ser aprimoradaElasticidade, podemos aumentar o número de threads.
Mas quando se trata de milhares de balançasConcorrênciaPedidos, tópicos não são suficientes.
Descobrimos que o multithreading apresenta as seguintes desvantagens: 1. O cliente do sistema inSync precisa fazer backup de um grande número de arquivos para o servidor por meio de chamadas RPC na rede. Uma forma típica dos desenvolvedores acelerarem as coisas é usando threads. No entanto, o desempenho trazido pelo multithreading aumenta o custo de memória e CPU; Os desenvolvedores precisam manter um equilíbrio entre velocidade e contagem de threads.
2. Nossos servidores precisam lidar entre o sistema inSync e milhares de clientesConcorrênciaConexões e notificações. Para gerenciar conexões de forma eficiente, usamos threads para lidar com requisições. Mas o número crescente de clientes do sistema inSync também significa que precisamos continuar aumentando o número de threads, o que consome muita memória do servidor e CPU.
3. Nosso servidor web precisa lidar com milhares de requisições HTTP paralelas. A maior parte do trabalho está nos sockets de rede que recebem e enviam dados e os encaminham para o backend do sistema inSync. Isso faz com que a maioria das threads espere pelas operações da rede. O problema do C10K é que, quando há milhares de requisições síncronas para o servidor web, gerar um thread para cada requisição é bastante não escalável (Scale).
Limitações dos frameworks assíncronos Muitos frameworks assíncronos, incluindo Twisted, Tornado Tornado e asyncore, podem ajudar os desenvolvedores a se afastarem das formas populares de usar threads. Esses frameworks dependem de sockets não bloqueantes e mecanismos de callback (semelhantes aos Node.js). Se usarmos esses frameworks como estão, as principais partes do nosso código Druva terão que ser refatoradas. Não é isso que queremos fazer. Refatorar código aumenta os ciclos de desenvolvimento e testes, impedindo-nos de atender aos nossos requisitos de escala. Considerando que múltiplas partes do produto precisam ser massivas, cada um de nós terá que refatorá-las – daí o esforço de dobrar ou triplicar.
Para evitar alterar tanto código, tivemos que deixar de usar diretamente o framework existente. Felizmente, encontramos algumas ferramentas úteis.
Como queremos controlar a execução do código na E/S da rede, precisamos de uma forma de dividir uma thread em micro-threads. EncontramosGreenlets。 Ela fornece um escalonamento de microthreads não implícito chamado corrotina de co-rotina. Em outras palavras. É útil quando você quer controlar seu código rodando. Você pode construir microthreads para agendamentos personalizados porque pode controlar quando greenlets geram pausas. Isso é perfeito para nós porque nos dá controle total sobre o agendamento do nosso código.
Tornado é um framework simples e não bloqueador de servidor web escrito em Python, projetado para lidar com milhares de requisições assíncronas. Usamos seu componente principal, IOLoop IOStream. IOLoop é um loop de eventos de E/S de socket não bloqueante; Ele usa epoll (no Linux) ou filas (BSD e Mac OS X), caso contrário, selecione (no Windows) se elas estão disponíveis. O IOStream oferece soquetes não bloqueantes, como embalagens convenientes para leitura e escrita. Delegamos todas as operações de socket para o Tornado e então usamos callbacks para acionar operações de código para serem concluídas (nota banq: muito parecido com Node.js mecanismo).
É um bom começo, mas precisamos de mais. Se usarmos o módulo acima diretamente no nosso código, muito do nosso código RPC terá que mudar, agendando RPC através dos greenlets, garantindo que os greenlets não bloqueiem (se os greenlets ficarem entupidos, vai entupir toda a thread e todos os outros), gerenciar as funções de callback do tornado.
Precisamos de uma abstração para gerenciar e organizar greenlets para evitar entupi-los com chamadas externas, e essa abstração pode ser massivamente escalável além das threads. Essa abstração é o Dhaga, que permite que o fluxo de código da aplicação seja programado como uma sequência síncrona tradicional, mas a execução é assíncrona.
Dhaga (do hindi, que significa thread) é um framework de execução para um thread leve que abstraímos. A classe Dhaga é derivada de greenlets e utiliza comutação de pilha para executar múltiplos fluxos de código em uma única thread do sistema operacional. Threads de um sistema operacional executam múltiplos dhagas usando agendamento colaborativo. Sempre que um dhaga espera (principalmente aguardando o retorno de uma chamada RPC), ele cede o controle ao nível pai (ou seja, o contexto de execução da thread em nível de SO que o criou). O nível pai então agenda outro dhaga para estar pronto para rodar. A chamada RPC será passada para o servidor web tornado para escrever o Socket assíncronamente, e então registrar um callback quando ele retornar, e quando esse RPC retornar, o dhaga em espera será adicionado à fila do executável e então captado pela thread pai. (Nota Banq: semelhante ao princípio node.js)
Podemos usar Dhaga em vez de threads para operações de alta latência, e usamos 512 dhagas em uma única thread quando o número de threads aumenta além do limite razoável de throughput.
|