Quantos tópicos devo usar no meu servidor NIO?

5

Eu construí um servidor Java NIO TCP, ele atualmente usa quatro threads. Um thread ServerRunnable que usa um seletor e três threads de trabalho.

Eu tenho procurado por algumas informações sobre isso, como eu li no passado, que você deve ter apenas um thread por núcleo do processador. Então é isso.

Mas isso me fez pensar recentemente, e depois de um pouco mais de pesquisa me deparei com este tópico .

Onde, nos comentários sobre o usuário de resposta aceita, os Donal Fellows apontam o seguinte:

Have at most one CPU-bound thread per processor allocated to the application. IO-bound threads aren't a big problem (other than the memory they consume) and it's important to remember that apps can be restricted to only use a subset of the system's CPUs; after all, it's (usually) the user's/admin's computer and not the programmer's.

Com isso em mente, estou correto em pensar que posso aumentar com segurança o número de threads de seletores e threads de trabalho em meu pool de threads.

Meu thread do servidor lê entrada, processa os dados em JSONObjects e, em seguida, os envia para uma fila. Os threads de trabalho, em seguida, pegam os JSONObjects da fila, verificam que tipo de objetos eles são e, em seguida, os empurram para o banco de dados. Então, há muito pouco trabalho computacional acontecendo lá. É seguro o suficiente para eu aumentar o número de threads aqui, como em usar mais threads ServerRunnable e mais threads de trabalho? Diga para dobrar a quantidade de cada por exemplo?

O que preciso pensar quando considero algo assim?

    
por bot_bot 19.03.2015 / 10:34
fonte

3 respostas

5

Você não tem motivos para assumir quantos núcleos de CPU o sistema de usuários possui. Sua máquina de desenvolvimento pode ser executada em uma CPU de quatro núcleos sem mais nada para fazer, mas também pode ser movida para uma máquina virtual de um único núcleo ou para um servidor high-end de 32 núcleos.

Por esse motivo, você não deve codificar o número de CPUs.

Em Java, você pode usar ThreadPoolExecutor para delegar o gerenciamento de encadeamentos à JVM. Geralmente, você passa pacotes de trabalho menores para o executor na forma de objetos que implementam Runnable e deixam a decisão de qual encadeamento processa cada executável no conjunto de encadeamentos. No seu caso, os JSONObjects individuais seriam tais pacotes de trabalho.

O ThreadPoolExecutor permite que você defina o número mínimo e máximo de CPUs que ele pode usar. Eu recomendo que você faça isso configurável e padrão para Runtime.getRuntime().availableProcessors() quando nenhuma opção de configuração é fornecida.

O fato de seu encadeamento IO ser ou não um encadeamento real em relação à carga da sua CPU depende do que ele está realmente fazendo. Quando ele recebe uma alta largura de banda de E / S e tem considerável trabalho de análise, isso pode acontecer. Mas não podemos dizer isso sem criar um perfil em seu aplicativo em condições do mundo real.

    
por 19.03.2015 / 13:46
fonte
1

O poder de processamento é o recurso mais escasso em um computador. Enquanto escrevo isto, o processador mais avançado que eu conheço tem 18 núcleos e custa cerca de 6 grands . Isso limita o seu número de threads para 18, se você quiser ter um verdadeiro paralelismo. Qualquer coisa além disso é um exagero, a menos que você esteja escrevendo GUIs ou outros aplicativos insensíveis à latência.

Um único segmento é capaz de lidar com pelo menos dez mil conexões. Agora imagine se você quisesse ter 10 mil threads, um para cada conexão?

Dê uma olhada no este artigo sobre servidores de alta disponibilidade com CoralReactor para entender como um único thread pode lidar com milhares de conexões por meio de um demultiplexador e multiplexador.

Aviso: Eu sou um dos desenvolvedores do CoralReactor.

    
por 02.05.2015 / 18:40
fonte
1

Uma das coisas que é crítico no projeto de um sistema multithread é descobrir como dividir o trabalho de uma maneira que mantenha tantos núcleos quanto possível. Seu design proposto usa dois tipos de encadeamentos que são principalmente vinculados a E / S e colocam parte do trabalho computacional em cada um deles.

Sugiro que você separe os encadeamentos vinculados a E / S e coloque o material de computação intensiva no meio usando três tipos de encadeamentos:

  • Entrada - Lê a entrada, coloca-a em uma fila de entrada e volta a esperar por mais entradas. Eu estou assumindo que a entrada é um fluxo único, onde não é prático ter vários leitores. Se múltiplos funcionarem, é seguro adicionar mais desses encadeamentos, até o número de canais que seu ambiente de entrada fornece. O ponto importante aqui é que descarregar a entrada em uma fila o mais rápido possível faz com que o encadeamento volte a ler mais entradas ou seja limitado por E / S sem adicionar nenhum atraso enquanto processa, maximizando a taxa de ingestão.

  • Processando - Captura itens da fila de entrada, converte-os em objetos JSON, decide de que tipo eles são, cria a ação correta do banco de dados e coloca isso em uma fila de saída. Em outras palavras, isso é todo o trabalho de paralelização e uso intensivo de CPU.

  • Output - Retira itens da fila de saída e grava seu banco de dados. Esses encadeamentos quase não processam e, como os encadeamentos de entrada, passam a maior parte do tempo aguardando a conclusão da E / S.

O benefício neste modelo é que você pode usar o estado das filas para tomar decisões sobre como ajustar o sistema:

  • O crescimento contínuo no comprimento médio da fila de entrada significa que não há threads de processamento suficientes para manipular a carga de entrada. A solução é adicionar mais threads de processamento ou, se você estiver sem núcleos físicos, obter uma máquina com mais. (Melhor pode ser outra máquina para pegar um pouco da carga, mas isso é outra discussão.)

  • Crescimento na fila de saída significa que você tem um afunilamento em fazer as gravações do banco de dados. Normalmente, você desejará quantos encadeamentos de saída o banco de dados puder manipular em paralelo. Mais e você está apenas descarregando o problema de overqueueing para o banco de dados. (Alguns bancos de dados são melhores que outros para lidar com isso, portanto, se você optar por mais threads de saída, é uma decisão que só você pode tomar.)

por 02.05.2015 / 20:17
fonte