Como projetar serviços da Web altamente escaláveis ​​em Java?

15

Estou criando alguns serviços da Web que teriam 2000 usuários simultâneos. Os serviços são oferecidos gratuitamente e, portanto, espera-se obter uma grande base de usuários. No futuro, pode ser necessário escalar até 50.000 usuários.

Já existem algumas outras perguntas que abordam o problema, como - /programming/2567254/building-highly-scalable-web-services

No entanto, meus requisitos diferem da pergunta acima.

Por exemplo - Meu aplicativo não possui uma interface de usuário, portanto, imagens, CSS e javascript não são um problema. É em Java, portanto, sugestões como usar o HipHop para traduzir PHP para código nativo são inúteis.

Por isso, decidi fazer minha pergunta separadamente.

Esta é a minha configuração do projeto -

  1. Serviços da Web baseados em descanso usando o Apache CXF
  2. Hibernate 3.0 (com otimizações relevantes, como carregamento lento e HQL personalizado para ajuste)
  3. Tomcat 6.0
  4. MySql 5.5

Quais são as melhores práticas a serem seguidas para tornar um aplicativo baseado em Java escalável?

Kshitiz Sharma
fonte
Se você estiver expondo um serviço REST, usar um proxy reverso como o Varnish ajudaria bastante. Qual a atualização dos dados? Você tem certeza de que precisa de um banco de dados relacional? Você poderia particionar os dados? Com a pilha de tecnologia que você está descrevendo, eu me concentraria em garantir que o menor número possível de solicitações realmente atingisse seu endpoint. Você já pensou em fazer isso na memória com soluções como Hazel cast / Gigaspaces etc?
ebaxt
@ baxt obrigado por suas sugestões. Gigaspaces parece ser de código aberto. Mas o elenco de Hazel parece interessante.
Kshitiz Sharma
1
@ebaxt "Tem certeza de que precisa de um banco de dados relacional?" A adoção do nosql teria mudanças drásticas na arquitetura do aplicativo. Estamos tentando manter a complexidade no mínimo. Custo, porém, não é um fator para nós. Então, vamos nos ater à abordagem relacional.
Kshitiz Sharma
1
Você pode usar o Postgres, MySQL ou o que quer que seja. O que há com sua infraestrutura? Você pode usar matrizes de disco? Os servidores estão hospedados no mesmo local? Você pode conectar seu cluster com batimentos cardíacos, etc.? Você pode colocá-los na mesma sub-rede?
edze
1
Também sou programador. Mas se o seu banco de dados relacional for o gargalo, você tenderá a terminar com essas perguntas. Existem bancos de dados no mercado, alguns com desempenho melhor que outros em algumas situações. Mas eles estão usando diferentes níveis de isolamento de transação padrão e concorrência otimista vs Pessimista Concurrency etc.
edze

Respostas:

8

Eu lidei com a questão no passado, mas ainda sinto que tenho muito o que aprender em campo. Acho que esse é um dos campos mais interessantes que existem no desenvolvimento de software hoje em dia, aqui estão algumas reflexões sobre isso: O
MySQL é um banco de dados justo o suficiente, a menos que você esteja trabalhando com uma quantidade enorme de dados e, nesse caso, você pode considerar o NoSQL banco de dados, mas você deve examinar cuidadosamente qual é o melhor banco de dados NoSQL para suas necessidades.

Você deve implementar o armazenamento em cache no seu sistema - tentar armazenar em cache o máximo de dados somente leitura possível ou definir algumas estratégias de armazenamento em cache - por exemplo, tivemos um cenário em que era válido para um usuário ver "dados antigos" como desde que a atualização recente tenha ocorrido na última hora.
Eu consideraria o JBoss Cache, ou talvez o Infinispan (que é mais como uma estrutura de dados distribuídos) ou outra estrutura de cache popular para isso.
Além disso, como você mencionou o tomcat, presumo que você trabalhe em algum módulo de solicitação-resposta. Tente considerar o uso de um cache existente no escopo de uma determinada solicitação; pode ser até um HashMap simples associado ao armazenamento local do encadeamento .
Minha idéia aqui se assemelha bastante ao cache de primeiro nível no Hibernate .

Lembre-se de que arquivos, transações e outros recursos são caros em termos de mantê-los abertos. Certifique-se de fechar arquivos e transações o mais rápido possível, ou você terá bugs que serão reproduzidos em configurações de larga escala

Além disso, você deve entender quais 2.000 usuários simultâneos - isso significa que 2.000 usuários estão acessando seu servidor de uma só vez ou estão usando seu sistema? Distinga entre casos em que 2000 usuários tentam abrir um soquete para o servidor e um caso em que apenas 500 e 1500 estão atualmente observando resultados, de preenchimento de entrada no lado do cliente.

Você deve considerar o uso de cluster - você terá que lidar com problemas como balanceamento de carga , sessão persistente (o que significa que o balanceador de carga redirecionará uma solicitação para o mesmo servidor para a mesma sessão) e muito mais.

Se você precisa ter um código de sincronização - escolha a estratégia de sincronização com cuidado. Vi alguns sistemas em que um bloqueio simples era usado, mas um ReaderWriterLockpoderia ter melhorado as coisas, pois a maioria dos acessos era somente leitura.

Considere ter cache e validação do lado do cliente, se possível, tente salvar chamadas para o servidor e enviar apenas diferenças de dados, caso a maioria de sua resposta para uma solicitação com o mesmo parâmetro não seja alterada.
Por exemplo, no projeto de código aberto oVirt , solicitamos obter estatísticas de uma determinada máquina virtual. alguns dados da VM raramente mudam; portanto, enviamos apenas MD5, se os dados alteram também o valor MD5, executamos uma solicitação para obter os dados completos, e não apenas o MD5.

Eu mencionei o hibernate antes - eu recomendaria que você o considerasse cuidadosamente - se você precisar executar muitas gravações e menos leituras, o Hibernate pode não ser o ideal para você e você deve considerar talvez trabalhar com o Spring-JDBC como wrapper sobre JDBC.

Indexe seu banco de dados com sabedoria e use um esquema de banco de dados correto. Considere o uso de uma camada de procedimentos armazenados, pois eles são pré-compilados e otimizados

. Gostaria de declarar que, no passado, lidei com um sistema (nó único) no mysql (principalmente acesso somente leitura) com o jboss 4.2.1 e consegui atingir 2000 concorrentes. Comercial
(não acessando de uma só vez em termos de abertura de soquetes 2000 contra nosso servidor), mas usando / navegando em nosso sistema, usando o JBoss Cache e pré-carregando no cache alguns dos dados mais acessados, ou dados que percebemos que serão "populares e populares" "mas nossa solução foi boa para nossa arquitetura e nossos fluxos,
como eu digo nesses casos -
há mais dicas e truques, mas isso realmente depende da sua arquitetura e dos fluxos que você precisa ter em seu sistema. Boa sorte!


fonte
Concordo, exceto para procs armazenados, não use procs armazenados. E você pode usar um hashmap concorrente e valores atômicos, para fazer threadsafe
Nim Chimpsky
3

Boa pergunta. Provavelmente difícil dizer qual é a melhor abordagem, mas tentarei com a minha experiência.

A melhor maneira de escalar o aplicativo da Web baseado em Java é escrevê-lo o mais sem estado possível (se possível). Isso permite escalar horizontalmente o aplicativo, onde você pode adicionar servidores tomcat se houver mais usuários simultâneos.

No entanto, como você observou, pode haver um problema nas conexões com o banco de dados. Mas a pergunta que tenho é: como você está obtendo os dados? É gerado pelo usuário ou você obtém os dados de terceiros? Isso é muito importante porque, se você estiver fornecendo um serviço ao usuário com os dados agregados de aplicativos de terceiros (por exemplo, FB, Twitter etc.), o que você pode seguir é gravar no banco de dados mestre e replicar os dados nos bancos de dados escravos. que são alocados para cada instância do tomcat. Cada servidor tomcat pode obter seu próprio banco de dados escravo.

 Are there faster alternatives to Mysql?

Você pode optar pelo cluster MySQL que possui armazenamento de dados na memória. Mas tome cuidado com o fato de que o aplicativo pode precisar de algumas alterações. Eles sql joinsnão são bem suportados no cluster MySQL, embora na versão mais recente haja melhorias para o mesmo. Se o custo não for um fator, você pode tentar o Oracle.

A solução de cache definitivamente melhorará o desempenho. Mas, então, tudo depende da arquitetura de todo o aplicativo. Você deve estar ciente de quando enviar dados para o cache, quando torná-los sujos (remover do cache).

Em relação à distribuição da carga no ambiente multi-servidor, sugiro que você use o balanceador de carga do que o Apache para balanceamento de carga.

Chandra
fonte
"Eu sugiro que você use o balanceador de carga do que o Apache para balanceamento de carga" Qual abordagem / software você sugeriria se não fosse o Apache?
Kshitiz Sharma
Basicamente, eu estava recomendando o hardware do balanceador de carga, que o administrador da rede deve poder configurar. Esse curso tem um custo adicional ao projeto. Esse balanceador de carga terá seu próprio IP (também chamado de IP virtual) e, basicamente, você atribuirá esse IP ao seu domínio. Quando a solicitação chega, isso o encaminhará para todos os servidores conectados da maneira round robin (também outros algoritmos disponíveis). Você pode usar o apache para esse fim se o hardware não for uma opção, mas eu preferiria o hardware, pois você não precisa ajustar o apache apenas para esse fim.
Estamos usando um servidor dedicado com httpd para fazer a mesma coisa. O hardware não é um problema.
Kshitiz Sharma
Você pode usar httpd e mod_cluster, se bem me lembro. Eu iria considerar cuidadosamente antes de ir para o "Overkill" solução de hardware LB, antes de verificar httpd e mod_cluster
@zaske - Você provavelmente está certo de que o balanceador de carga de hardware talvez seja um exagero. Mas, caso você precise aumentar, é fácil, adicionando mais servidores.
2

Atualmente, estou configurando um sistema semelhante (em nível profissional) e esse é o design que escolhi:

  • Dois balanceadores de carga Nginx (ambos ativos, ambos com failover para o outro, equilibrados com rodízio de DNS)
  • Dois bancos de dados MySQL no modo de replicação mestre principal
  • Duas instâncias do Tomcat como um cluster do tomcat
  • Duas instâncias Memcached para armazenamento em cache e compartilhamento de estado da sessão para o cluster Tomcat

Isso alcançará uma solução redundante, de alta disponibilidade e escalável.

Os balanceadores de carga (em hardware decente) facilmente balancearão uma linha saturada de 1gbit cada. Este também é um ótimo local para descarregamento de SSL.

Você pode salvar suas informações da sessão no memcached. Caso uma instância do tomcat falhe, outra instância do tomcat poderá recuperar informações relevantes da sessão e os clientes não perceberão nada. Não se esqueça de combinar isso com sessões complicadas também. (Para manter o tráfego de rede baixo)

O cluster do Tomcat também tem uma opção para compartilhar informações da sessão entre o cluster em tempo real, sem usar o memcached. Embora eu pense em desempenho, usar o Memcached será melhor.

Se você precisar de mais energia em qualquer um desses aplicativos:

  • Nginx: Adicione mais balanceadores de carga, embora eu não ache que esse seja o gargalo muito em breve.
  • Tomcat: você pode aumentar facilmente o tamanho do cluster do Tomcat ou adicionar mais clusters
  • Mysql: adicione alguns escravos somente leitura ou aumente o tamanho do cluster (dependendo do seu aplicativo, mas desde que você escreveu um aplicativo baseado em REST, isso não deve ser um problema)
  • Memcached: adicione mais nós, o Memcached dimensiona muito bem, acredito.

Não sei como seu aplicativo é construído e quais são os grandes recursos, mas se você vir uma alta carga de banco de dados (durante seus testes de carga!), Adicionar um cache entre o aplicativo e o banco de dados certamente pode melhorar muito o desempenho. Mas não se esqueça que nem tudo é fácil de alterar, se suas consultas sempre forem diferentes, o cache não ajudará (muito)

Meu conselho seria baixar o VMware Workbench (ou software de virtualização similair) e tentar criar uma configuração simples. Sem balanceamento de carga ou cluster, apenas o básico e o trabalho a partir daí. Adicione um por um mais recursos (balanceamento, armazenamento em cache, clustering etc.) e faça uma pesquisa sobre cada tópico, para que você saiba que fez a escolha certa.

Se você continuar executando os mesmos testes de desempenho durante esse processo, poderá ver por si mesmo se usar X é melhor que usar Y na sua instalação ou qual o impacto do cache, etc.

No final, uma configuração como essa realmente depende dos requisitos do seu aplicativo e de seus clientes, tudo pode ser feito de várias maneiras, cada uma com suas próprias forças e fraquezas.

Mais alguma pergunta?

Boa sorte!

Wesley

Wesley
fonte
avelã? (resolvido
(
Você usa uma estrutura para a camada de armazenamento em cache ou apenas um monte de hashes manuais em consultas SQL?
djechlin