Estou na fase de design de escrever um novo aplicativo de serviço do Windows que aceite conexões TCP / IP para conexões de longa execução (isto é, não é como HTTP, onde há muitas conexões curtas, mas um cliente se conecta e permanece conectado por horas ou dias ou semanas).
Estou procurando idéias para a melhor maneira de projetar a arquitetura de rede. Vou precisar iniciar pelo menos um thread para o serviço. Estou pensando em usar a API assíncrona (BeginRecieve, etc.), pois não sei quantos clientes terei conectado a qualquer momento (possivelmente centenas). Definitivamente, não quero iniciar um thread para cada conexão.
Os dados fluirão principalmente para os clientes do meu servidor, mas haverá alguns comandos enviados pelos clientes ocasionalmente. Esse é principalmente um aplicativo de monitoramento no qual meu servidor envia dados de status periodicamente para os clientes.
Alguma sugestão sobre a melhor maneira de tornar isso o mais escalável possível? Fluxo de trabalho básico? Obrigado.
EDIT: Para ficar claro, estou procurando soluções baseadas em .net (c #, se possível, mas qualquer idioma .net funcionará)
NOTA DE RECONHECIMENTO: Para receber a recompensa, espero mais do que uma resposta simples. Eu precisaria de um exemplo prático de solução, como um ponteiro para algo que eu poderia baixar ou um pequeno exemplo em linha. E deve ser baseado em .net e Windows (qualquer idioma .net é aceitável)
EDIT: Quero agradecer a todos que deram boas respostas. Infelizmente, eu só podia aceitar um e optei por aceitar o método Begin / End mais conhecido. A solução da Esac pode muito bem ser melhor, mas ainda é nova o suficiente para que eu não saiba ao certo como vai funcionar.
Eu votei todas as respostas que achei boas, gostaria de poder fazer mais por vocês. Obrigado novamente.
fonte
Respostas:
Eu escrevi algo semelhante a isso no passado. De minha pesquisa, anos atrás, mostrei que escrever sua própria implementação de soquete era a melhor aposta, usando os soquetes assíncronos. Isso significava que os clientes que realmente não estavam fazendo nada realmente exigiam relativamente poucos recursos. Tudo o que ocorre é tratado pelo pool de threads .net.
Eu escrevi como uma classe que gerencia todas as conexões para os servidores.
Simplesmente usei uma lista para armazenar todas as conexões do cliente, mas se você precisar de pesquisas mais rápidas para listas maiores, poderá escrevê-la como quiser.
Além disso, você precisa que o soquete esteja realmente ouvindo as conexões recebidas.
O método start realmente inicia o soquete do servidor e começa a escutar as conexões recebidas.
Gostaria apenas de observar que o código de tratamento de exceções parece ruim, mas o motivo é que eu tinha um código de supressão de exceção lá para que quaisquer exceções fossem suprimidas e retornassem
false
se uma opção de configuração fosse definida, mas eu queria removê-lo para amor de brevidade.O _serverSocket.BeginAccept (novo AsyncCallback (acceptCallback)), _serverSocket) acima define essencialmente o soquete do servidor para chamar o método acceptCallback sempre que um usuário se conectar. Esse método é executado no pool de threads .Net, que manipula automaticamente a criação de threads de trabalho adicionais se você tiver muitas operações de bloqueio. Isso deve lidar de maneira ideal com qualquer carga no servidor.
O código acima basicamente acabou de aceitar a conexão que entra, enfileira
BeginReceive
que é um retorno de chamada que será executado quando o cliente envia dados e, em seguida, enfileira o próximoacceptCallback
que aceitará a próxima conexão do cliente que entrar.A
BeginReceive
chamada de método é o que diz ao soquete o que fazer quando recebe dados do cliente. ParaBeginReceive
, você precisa fornecer uma matriz de bytes, que é onde ele copiará os dados quando o cliente enviar dados. OReceiveCallback
método será chamado, e é assim que lidamos com o recebimento de dados.Edição: Neste padrão eu esqueci de mencionar que nesta área de código:
O que eu geralmente faria é no código que você quiser, é remontar pacotes em mensagens e, em seguida, criá-los como trabalhos no pool de threads. Dessa forma, o BeginReceive do próximo bloco do cliente não é atrasado enquanto qualquer código de processamento de mensagens estiver em execução.
O retorno de chamada de aceitação termina de ler o soquete de dados chamando o recebimento final. Isso preenche o buffer fornecido na função de recebimento inicial. Depois que você fizer o que quiser, onde deixei o comentário, chamamos o próximo
BeginReceive
método que executará o retorno de chamada novamente se o cliente enviar mais dados. Agora, aqui está a parte realmente complicada: quando o cliente envia dados, seu retorno de chamada de recebimento pode ser chamado apenas com parte da mensagem. A remontagem pode se tornar muito, muito complicada. Eu usei meu próprio método e criei uma espécie de protocolo proprietário para fazer isso. Eu o deixei de fora, mas se você solicitar, eu posso adicioná-lo. Esse manipulador foi, na verdade, o código mais complicado que eu já escrevi.O método de envio acima, na verdade, usa uma
Send
chamada síncrona , para mim, devido aos tamanhos das mensagens e à natureza multithread do meu aplicativo. Se você deseja enviar para todos os clientes, basta percorrer a lista _sockets.A classe xConnection que você vê acima referenciada é basicamente um invólucro simples para um soquete incluir o buffer de bytes e, na minha implementação, alguns extras.
Também para referência, aqui estão os que
using
eu incluo, pois sempre fico irritado quando eles não estão incluídos.Espero que seja útil, pode não ser o código mais limpo, mas funciona. Existem também algumas nuances no código que você deve estar cansado de mudar. Por um lado, tenha apenas uma
BeginAccept
chamada por vez. Costumava haver um bug .net muito irritante em torno disso, que foi anos atrás, então não me lembro dos detalhes.Além disso, no
ReceiveCallback
código, processamos tudo o que é recebido do soquete antes de enfileirar o próximo recebimento. Isso significa que, para um soquete único, na verdade, estamos apenasReceiveCallback
uma vez a qualquer momento e não precisamos usar a sincronização de threads. No entanto, se você reordenar isso para chamar a próxima recepção imediatamente após extrair os dados, o que pode ser um pouco mais rápido, será necessário sincronizar corretamente os encadeamentos.Além disso, cortei muito do meu código, mas deixei a essência do que está acontecendo no lugar. Este deve ser um bom começo para o seu design. Deixe um comentário se tiver mais alguma dúvida sobre isso.
fonte
Send
métodos serão bloqueados indefinidamente nos dois lados, porque não há ninguém lendo os dados de entrada.Existem várias maneiras de executar operações de rede em C #. Todos eles usam mecanismos diferentes sob o capô e, portanto, sofrem grandes problemas de desempenho com alta simultaneidade. As operações Begin * são uma delas que muitas pessoas confundem por ser a maneira mais rápida / rápida de fazer networking.
Para resolver esses problemas, eles introduziram o conjunto de métodos * Async: No MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
A classe SocketAsyncEventArgs faz parte de um conjunto de aprimoramentos da classe System.Net.Sockets .. ::. Socket que fornece um padrão assíncrono alternativo que pode ser usado por aplicativos de soquete de alto desempenho especializados. Esta classe foi projetada especificamente para aplicativos de servidor de rede que exigem alto desempenho. Um aplicativo pode usar o padrão assíncrono aprimorado exclusiva ou apenas em áreas quentes de destino (por exemplo, ao receber grandes quantidades de dados).
O principal recurso desses aprimoramentos é evitar a alocação e sincronização repetidas de objetos durante E / S de soquete assíncrono de alto volume. O padrão de design Begin / End implementado atualmente pela classe System.Net.Sockets .. ::. Socket requer que um objeto System .. ::. IAsyncResult seja alocado para cada operação de soquete assíncrona.
Nos bastidores, a * API Async usa portas de conclusão de E / S, que é a maneira mais rápida de executar operações de rede, consulte http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
E apenas para ajudá-lo, estou incluindo o código fonte de um servidor de telnet que escrevi usando a API * Async. Estou incluindo apenas as partes relevantes. Observe também que, em vez de processar os dados em linha, optei por enviá-los para uma fila sem bloqueio (sem espera) que é processada em um encadeamento separado. Observe que eu não estou incluindo a classe Pool correspondente, que é apenas um pool simples, que criará um novo objeto se estiver vazio, e a classe Buffer, que é apenas um buffer auto-expansível que não é realmente necessário, a menos que você esteja recebendo um indeterminado quantidade de dados. Se você quiser mais informações, sinta-se à vontade para me enviar uma MP.
fonte
Costumava haver uma discussão muito boa sobre TCP / IP escalável usando .NET, escrita por Chris Mullins, da Coversant, infelizmente parece que seu blog desapareceu de sua localização anterior, então tentarei reunir seus conselhos de memória (alguns comentários úteis dele aparecem neste segmento: C ++ vs. C #: desenvolvendo um servidor IOCP altamente escalável )
Em primeiro lugar, observe que o uso
Begin/End
e osAsync
métodos naSocket
classe utilizam as portas de conclusão de E / S (IOCP) para fornecer escalabilidade. Isso faz uma diferença muito maior (quando usada corretamente; veja abaixo) na escalabilidade do que qual dos dois métodos você escolhe para implementar sua solução.As postagens de Chris Mullins foram baseadas no uso
Begin/End
, que é o que eu pessoalmente tenho experiência. Observe que Chris montou uma solução com base nisso, que escalou até 10.000 milhões de conexões simultâneas de clientes em uma máquina de 32 bits com 2 GB de memória e até 100.000 em uma plataforma de 64 bits com memória suficiente. De minha própria experiência com essa técnica (embora nem de longe esse tipo de carga) não tenho motivos para duvidar desses números indicativos.IOCP versus encadeamento por conexão ou primitivas 'select'
O motivo pelo qual você deseja usar um mecanismo que usa o IOCP sob o capô é que ele usa um pool de encadeamentos de nível muito baixo do Windows que não ativa nenhum encadeamento até que haja dados reais no canal de E / S que você está tentando ler ( observe que o IOCP também pode ser usado para E / S de arquivo). A vantagem disso é que o Windows não precisa alternar para um encadeamento apenas para descobrir que ainda não existem dados. Isso reduz o número de alternâncias de contexto que seu servidor precisará fazer ao mínimo necessário.
Switches de contexto é o que definitivamente matará o mecanismo 'thread por conexão', embora essa seja uma solução viável se você estiver lidando apenas com algumas dezenas de conexões. Esse mecanismo, no entanto, não é extensivo à imaginação 'escalável'.
Considerações importantes ao usar o IOCP
Memória
Em primeiro lugar, é essencial entender que o IOCP pode facilmente resultar em problemas de memória no .NET se a sua implementação for muito ingênua. Toda
BeginReceive
chamada IOCP resultará na "fixação" do buffer que você está lendo. Para uma boa explicação de por que isso é um problema, consulte: Weblog de Yun Jin: OutOfMemoryException and Pinning .Felizmente, esse problema pode ser evitado, mas exige um pouco de troca. A solução sugerida é alocar um grande
byte[]
buffer na inicialização do aplicativo (ou próximo a ele), de pelo menos 90 KB ou mais (no .NET 2, o tamanho necessário pode ser maior nas versões posteriores). O motivo para fazer isso é que grandes alocações de memória terminam automaticamente em um segmento de memória não compactável (The Large Object Heap) efetivamente fixado automaticamente. Ao alocar um buffer grande na inicialização, você garante que esse bloco de memória imóvel esteja em um "endereço relativamente baixo", onde não atrapalhará e causará fragmentação.Você pode usar compensações para segmentar esse grande buffer em áreas separadas para cada conexão que precisa ler alguns dados. É aqui que um trade-off entra em cena; como esse buffer precisa ser pré-alocado, você terá que decidir quanto espaço de buffer você precisa por conexão e qual limite superior você deseja definir no número de conexões para as quais deseja dimensionar (ou implementar uma abstração que podem alocar buffers fixados adicionais quando você precisar).
A solução mais simples seria atribuir a cada conexão um único byte em um deslocamento exclusivo dentro desse buffer. Em seguida, você pode fazer uma
BeginReceive
chamada para que um único byte seja lido e executar o restante da leitura como resultado do retorno de chamada recebido.Em processamento
Quando você recebe o retorno de chamada da
Begin
chamada feita, é muito importante perceber que o código no retorno de chamada será executado no encadeamento IOCP de baixo nível. É absolutamente essencial que você evite operações demoradas nesse retorno de chamada. O uso desses encadeamentos para processamento complexo reduzirá sua escalabilidade com a mesma eficácia que o uso de 'encadeamento por conexão'.A solução sugerida é usar o retorno de chamada apenas para enfileirar um item de trabalho para processar os dados recebidos, que serão executados em algum outro encadeamento. Evite operações potencialmente bloqueadas dentro do retorno de chamada, para que o encadeamento IOCP possa retornar ao seu pool o mais rápido possível. No .NET 4.0, sugiro que a solução mais fácil seja gerar um
Task
, fornecendo uma referência ao soquete do cliente e uma cópia do primeiro byte que já foi lido pelaBeginReceive
chamada. Essa tarefa é responsável por ler todos os dados do soquete que representam a solicitação que você está processando, executando-os e fazendo uma novaBeginReceive
chamada para enfileirar o soquete para IOCP mais uma vez. Antes do .NET 4.0, você pode usar o ThreadPool ou criar sua própria implementação de fila de trabalho encadeada.Resumo
Basicamente, sugiro usar o código de exemplo de Kevin para esta solução, com os seguintes avisos adicionados:
BeginReceive
já está 'fixado'BeginReceive
não faça nada além de enfileirar uma tarefa para lidar com o processamento real dos dados recebidosQuando você faz isso, não tenho dúvida de que você poderia replicar os resultados de Chris ao aumentar potencialmente centenas de milhares de clientes simultâneos (dado o hardware certo e uma implementação eficiente do seu próprio código de processamento;)
fonte
Você já obteve a maior parte da resposta através dos exemplos de código acima. Usar a operação de E / S assíncrona é absolutamente o caminho a percorrer aqui. Async IO é a maneira como o Win32 é projetado internamente para escalar. O melhor desempenho possível que você pode obter é alcançado usando portas de conclusão, vinculando seus soquetes às portas de conclusão e tendo um conjunto de encadeamentos aguardando a conclusão da porta de conclusão. O senso comum é ter 2-4 threads por CPU (núcleo) aguardando a conclusão. Eu recomendo revisar esses três artigos de Rick Vicik da equipe de Desempenho do Windows:
Os artigos mencionados abrangem principalmente a API nativa do Windows, mas são uma leitura obrigatória para quem tenta ter uma noção da escalabilidade e desempenho. Eles também têm alguns resumos do lado gerenciado.
A segunda coisa que você precisa fazer é verificar o livro Melhorando o Desempenho e Escalabilidade de Aplicativos .NET , disponível on-line. Você encontrará conselhos pertinentes e válidos sobre o uso de encadeamentos, chamadas assíncronas e bloqueios no Capítulo 5. Mas as verdadeiras jóias estão no Capítulo 17, onde você encontrará itens como orientações práticas para ajustar seu pool de encadeamentos. Meus aplicativos tiveram alguns problemas sérios até ajustar os maxIothreads / maxWorkerThreads conforme as recomendações deste capítulo.
Você diz que deseja criar um servidor TCP puro, então meu próximo ponto é falso. No entanto , se você se encontrar encurralado e usar a classe WebRequest e seus derivados, esteja avisado de que existe um dragão vigiando essa porta: o ServicePointManager . Esta é uma classe de configuração que tem um propósito na vida: arruinar seu desempenho. Certifique-se de liberar seu servidor do ServicePoint.ConnectionLimit imposto artificialmente ou seu aplicativo nunca será dimensionado (eu deixo você descobrir qual é o valor padrão ...). Você também pode reconsiderar a política padrão de enviar um cabeçalho Expect100Continue nas solicitações http.
Agora, sobre a API gerenciada por soquete principal, as coisas são bastante fáceis no lado de envio, mas são significativamente mais complexas no lado de recebimento. Para obter alta taxa de transferência e escala, você deve garantir que o soquete não seja controlado por fluxo, porque você não possui um buffer publicado para recebimento. Idealmente, para obter alto desempenho, você deve colocar à frente de 3 a 4 buffers e colocar novos buffers assim que receber um ( antes de processar o que recebeu) para garantir que o soquete sempre tenha um local para depositar os dados provenientes da rede. Você verá por que provavelmente não conseguirá isso em breve.
Depois de terminar de jogar com a API BeginRead / BeginWrite e iniciar o trabalho sério, você perceberá que precisa de segurança no seu tráfego, por exemplo. Autenticação NTLM / Kerberos e criptografia de tráfego, ou pelo menos proteção contra adulteração de tráfego. A maneira como você faz isso é usar o System.Net.Security.NegotiateStream (ou SslStream, se você precisar passar por domínios diferentes). Isso significa que, em vez de depender de operações assíncronas de soquete direto, você confiará nas operações assíncronas de AuthenticatedStream. Assim que você obtém um soquete (da conexão no cliente ou da aceitação no servidor), você cria um fluxo no soquete e o envia para autenticação, chamando BeginAuthenticateAsClient ou BeginAuthenticateAsServer. Depois que a autenticação for concluída (pelo menos, você estará seguro da loucura nativa InitiateSecurityContext / AcceptSecurityContext ...), você fará sua autorização verificando a propriedade RemoteIdentity do seu fluxo autenticado e fazendo a verificação da ACL que seu produto deve suportar. Depois disso, você enviará mensagens usando o BeginWrite e as receberá com o BeginRead. Esse é o problema que eu estava falando antes e que você não poderá postar vários buffers de recebimento, porque as classes AuthenticateStream não suportam isso. A operação BeginRead gerencia internamente todo o IO até que você receba um quadro inteiro; caso contrário, ele não pode manipular a autenticação da mensagem (descriptografar o quadro e validar a assinatura no quadro). Embora, na minha experiência, o trabalho realizado pelas classes AuthenticatedStream seja bastante bom e não deva ter nenhum problema. Ou seja. você poderá saturar a rede de GB com apenas 4-5% da CPU. As classes AuthenticatedStream também impõem a você as limitações de tamanho de quadro específico do protocolo (16k para SSL, 12k para Kerberos).
Isso deve começar no caminho certo. Não vou postar código aqui, há um exemplo perfeitamente bom no MSDN . Já fiz muitos projetos como esse e pude escalar para cerca de 1000 usuários conectados sem problemas. Acima disso, você precisará modificar as chaves do Registro para permitir ao kernel mais identificadores de soquete. e certifique-se de implantar em um sistema operacional de servidor , ou seja, W2K3, não XP ou Vista (ou seja, sistema operacional cliente), isso faz uma grande diferença.
BTW, verifique se você possui operações de bancos de dados no servidor ou no pedido de veiculação de arquivo. Você também usa o sabor assíncrono para eles ou drena o pool de threads rapidamente. Para conexões do SQL Server, adicione o 'Asyncronous Processing = true' à seqüência de conexão.
fonte
Eu tenho esse servidor em execução em algumas das minhas soluções. Aqui está uma explicação muito detalhada das diferentes maneiras de fazer isso no .net: Aproxime-se do fio com soquetes de alto desempenho no .NET
Ultimamente, tenho procurado maneiras de melhorar nosso código e analisarei isso: " Aprimoramentos de desempenho do soquete na versão 3.5 ", que foram incluídos especificamente "para uso em aplicativos que usam E / S de rede assíncrona para obter o melhor desempenho".
"O principal recurso desses aprimoramentos é evitar a alocação e sincronização repetidas de objetos durante E / S de soquete assíncrono de alto volume. O padrão de design Begin / End atualmente implementado pela classe Socket para E / S de soquete assíncrono requer um sistema. O objeto IAsyncResult seja alocado para cada operação de soquete assíncrona. "
Você pode continuar lendo se seguir o link. Pessoalmente, testarei seu código de amostra amanhã para compará-lo com o que tenho.
Edit: Aqui você pode encontrar o código de trabalho para o cliente e o servidor usando o novo 3.5 SocketAsyncEventArgs, para que você possa testá-lo em alguns minutos e percorrer o código. É uma abordagem simples, mas é a base para iniciar uma implementação muito maior. Também este artigo de quase dois anos atrás na MSDN Magazine foi uma leitura interessante.
fonte
Você já pensou em usar apenas uma ligação TCP da rede WCF e um padrão de publicação / assinatura? O WCF permitiria que você se concentrasse principalmente no seu domínio, em vez de no encanamento.
Existem muitas amostras do WCF e até uma estrutura de publicação / assinatura disponível na seção de download do IDesign que pode ser útil: http://www.idesign.net
fonte
Estou pensando em uma coisa:
Por que é que? O Windows pode lidar com centenas de threads em um aplicativo desde pelo menos o Windows 2000. Eu já fiz isso, é realmente fácil trabalhar com eles se os threads não precisarem ser sincronizados. Especialmente considerando que você está realizando muitas E / S (para que você não seja vinculado à CPU e que muitos encadeamentos sejam bloqueados na comunicação de disco ou de rede), não entendo essa restrição.
Você já testou a maneira multiencadeada e achou que falta alguma coisa? Você pretende também ter uma conexão com o banco de dados para cada encadeamento (isso mataria o servidor do banco de dados, por isso é uma má idéia, mas é facilmente resolvido com um design de três camadas). Você está preocupado com o fato de ter milhares de clientes em vez de centenas e realmente ter problemas? (Embora eu tentasse mil threads ou até dez mil se tivesse mais de 32 GB de RAM - novamente, considerando que você não está vinculado à CPU, o tempo de troca de threads deve ser absolutamente irrelevante.)
Aqui está o código - para ver como isso funciona, acesse http://mdpopescu.blogspot.com/2009/05/multi-threaded-server.html e clique na imagem.
Classe de servidor:
Programa principal do servidor:
Classe de cliente:
Programa principal do cliente:
fonte
Usar o Async IO integrado do .NET (
BeginRead
etc) é uma boa idéia, se você conseguir todos os detalhes corretamente. Quando você configura adequadamente os manipuladores de soquete / arquivo, ele usará a implementação IOCP subjacente do SO, permitindo que suas operações sejam concluídas sem o uso de threads (ou, na pior das hipóteses, usando um thread que eu acredito que seja proveniente do pool de threads de E / S do kernel. do pool de threads do .NET, que ajuda a aliviar o congestionamento do pool de threads.)A principal dica é garantir que você abra seus soquetes / arquivos no modo sem bloqueio. A maioria das funções de conveniência padrão (como
File.OpenRead
) não faz isso, então você precisará escrever suas próprias.Uma das outras preocupações principais é o tratamento de erros - o tratamento adequado de erros ao escrever código de E / S assíncrono é muito, muito mais difícil do que fazê-lo no código síncrono. Também é muito fácil acabar com as condições de corrida e os impasses, mesmo que você não esteja usando os threads diretamente, portanto, você precisa estar ciente disso.
Se possível, você deve tentar usar uma biblioteca de conveniência para facilitar o processo de execução de E / S assíncrona e escalável.
O tempo de execução de coordenação de concorrência da Microsoft é um exemplo de uma biblioteca .NET projetada para diminuir a dificuldade de executar esse tipo de programação. Parece ótimo, mas como eu não o usei, não posso comentar sobre o tamanho da escala.
Para meus projetos pessoais que precisam realizar E / S de rede ou disco assíncrona, eu uso um conjunto de ferramentas de simultaneidade / IO do .NET que eu criei no ano passado, chamado Squared.Task . É inspirado em bibliotecas como imvu.task e twisted , e incluí alguns exemplos de trabalho no repositório que fazem E / S de rede. Também o usei em alguns aplicativos que escrevi - o maior lançado publicamente sendo o NDexer (que o utiliza para E / S de disco sem rosca). A biblioteca foi escrita com base na minha experiência com o imvu.task e possui um conjunto de testes de unidade bastante abrangentes, por isso, recomendo fortemente que você experimente. Se você tiver algum problema, ficarei feliz em oferecer-lhe alguma ajuda.
Na minha opinião, com base na minha experiência usando E / S assíncronas / sem thread em vez de threads, é um esforço interessante na plataforma .NET, desde que você esteja pronto para lidar com a curva de aprendizado. Ele permite evitar os aborrecimentos de escalabilidade impostos pelo custo dos objetos Thread e, em muitos casos, você pode evitar completamente o uso de bloqueios e mutexes, fazendo uso cuidadoso de primitivas de simultaneidade como Futuros / Promessas.
fonte
Eu usei a solução de Kevin, mas ele diz que essa solução não possui código para remontagem de mensagens. Os desenvolvedores podem usar este código para remontagem de mensagens:
fonte
Você pode encontrar uma boa visão geral das técnicas na página de problemas do C10k .
fonte
Você pode tentar usar uma estrutura chamada ACE (Adaptive Communications Environment), que é uma estrutura C ++ genérica para servidores de rede. É um produto maduro, muito sólido e foi projetado para suportar aplicativos de alta confiabilidade e grande volume até a classe de telecomunicações.
A estrutura lida com uma ampla variedade de modelos de simultaneidade e provavelmente possui um adequado para sua aplicação pronta para uso. Isso deve facilitar a depuração do sistema, pois a maioria dos problemas desagradáveis de simultaneidade já foram resolvidos. A desvantagem aqui é que a estrutura é escrita em C ++ e não é a mais quente e fofa das bases de código. Por outro lado, você recebe uma infraestrutura de rede de nível industrial testada e uma arquitetura altamente escalável pronta para uso.
fonte
Eu usaria o SEDA ou uma biblioteca de encadeamento leve (erlang ou linux mais recente, consulte escalabilidade NTPL no lado do servidor ). A codificação assíncrona é muito complicada se a sua comunicação não for :)
fonte
Bem, os soquetes .NET parecem fornecer select () - o melhor para lidar com entradas. Para saída, eu teria um pool de threads de gravador de soquete ouvindo em uma fila de trabalho, aceitando o descritor / objeto de soquete como parte do item de trabalho, para que você não precise de um encadeamento por soquete.
fonte
Eu usaria os métodos AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync adicionados no .NET 3.5. Fiz uma referência e eles são aproximadamente 35% mais rápidos (tempo de resposta e taxa de bits) com 100 usuários constantemente enviando e recebendo dados.
fonte
para que as pessoas copiem colando a resposta aceita, é possível reescrever o método acceptCallback, removendo todas as chamadas de _serverSocket.BeginAccept (novo AsyncCallback (acceptCallback), _serverSocket); e coloque-o em uma cláusula finalmente {}, desta maneira:
você pode até remover a primeira captura, já que o conteúdo é o mesmo, mas é um método de modelo, e você deve usar a exceção digitada para lidar melhor com as exceções e entender o que causou o erro, então implemente essas capturas com algum código útil
fonte
Eu recomendaria ler esses livros sobre o ACE
para obter idéias sobre padrões, permitindo criar um servidor eficiente.
Embora o ACE seja implementado em C ++, os livros cobrem muitos padrões úteis que podem ser usados em qualquer linguagem de programação.
fonte
Você não obterá o nível mais alto de escalabilidade se usar exclusivamente o .NET. Pausas no GC podem prejudicar a latência.
A E / S sobreposta é geralmente considerada a API mais rápida do Windows para comunicação em rede. Não sei se é o mesmo que sua API assíncrona. Não use select, pois cada chamada precisa verificar todos os soquetes abertos em vez de ter retornos de chamada nos soquetes ativos.
fonte
Você pode usar a estrutura de código-fonte aberto do Push Framework para o desenvolvimento de servidores de alto desempenho. Ele é construído no IOCP e é adequado para cenários push e transmissão de mensagens.
http://www.pushframework.com
fonte