Quais padrões de design são mais aproveitados na criação de aplicativos de alta disponibilidade?[fechado]

StackOverflow https://stackoverflow.com/questions/814742

Pergunta

Da mesma forma, existem padrões de design que devem ser evitados?

Foi útil?

Solução

Presumo que você esteja escrevendo um aplicativo do tipo servidor (vamos deixar os aplicativos da Web por um tempo - existem algumas boas soluções prontas para uso que podem ajudar nisso, então vamos dar uma olhada no "eu tenho esse novo tipo de servidor excelente que escrevi ", mas quero que seja um problema de HA).

Em uma implementação de servidor, as solicitações dos clientes são geralmente (de uma forma ou de outra) convertidas em algum evento ou padrão de tipo de comando e são então executadas em uma ou mais filas.

Então, primeiro problema - é necessário armazenar eventos/comandos de uma maneira que sobreviva no cluster (ou seja,quando um novo nó assume o controle de master , ele analisa o próximo comando que precisa ser executado e começa).

Vamos começar com uma implementação de servidor com thread único (o mais fácil - e os conceitos ainda se aplicam ao multithread, mas ele tem seu próprio conjunto de problemas0.Quando um comando está sendo processado, é necessário algum tipo de processamento de transação.

Outra preocupação é gerenciar os efeitos colaterais e como você lida com a falha do comando atual?Sempre que possível, trate os efeitos colaterais de maneira transacional, de modo que sejam tudo ou nada.ou seja.se o comando alterar as variáveis ​​de estado, mas travar no meio da execução, ser capaz de retornar ao estado "anterior" é ótimo.Isso permite que o novo nó mestre retome o comando travado e apenas execute o comando novamente.Uma boa maneira novamente é dividir os efeitos colaterais em tarefas menores que podem ser executadas novamente em qualquer nó.ou seja.armazene as principais tarefas de início e término da solicitação, com muitas pequenas tarefas que lidam com, digamos, apenas um efeito colateral por tarefa.

Isso também introduz outras questões que afetarão seu design.Essas variáveis ​​de estado não são necessariamente atualizações de bancos de dados.Eles poderiam ser um estado compartilhado (digamos, uma máquina de estado finito para um componente interno) que também precisa ser distribuído no cluster.Portanto, o padrão de gerenciamento muda de forma que o código mestre veja uma versão consistente do estado necessário e, em seguida, confirme esse estado em todo o cluster.Usar alguma forma de armazenamento de dados imutável (pelo menos do thread mestre que faz a atualização) é útil.ou seja.todas as atualizações são efetivamente feitas em novas cópias que devem passar por algum tipo de mediador ou fachada que atualiza apenas as cópias locais na memória com as atualizações após a atualização no cluster (ou o número mínimo de membros no cluster para consistência dos dados).

Algumas dessas questões também estão presentes em sistemas de trabalho mestre.

Também é necessário um bom gerenciamento de erros, pois aumenta o número de coisas que podem dar errado na atualização do estado (já que a rede agora está envolvida).

Eu uso muito o padrão de estado.Em vez de atualizações de uma linha, para efeitos colaterais, você deseja enviar solicitações/respostas e usar fsms específicos de conversa para rastrear o progresso.

Outra questão é a representação dos pontos finais.ou seja.o cliente conectado ao nó mestre precisa ser capaz de se reconectar ao novo mestre e, em seguida, ouvir os resultados?Ou você simplesmente cancela todos os resultados pendentes e deixa os clientes reenviarem?Se você permitir que solicitações pendentes sejam processadas, será necessária uma boa maneira de identificar endpoints (clientes) (ou seja,algum tipo de ID do cliente em uma pesquisa).

Também precisa de código de limpeza, etc.não quero que os dados que aguardam a reconexão de um cliente esperem para sempre).

Muitas filas são usadas.Muitas pessoas usarão, portanto, algum barramento de mensagens (jms, digamos, para java) para enviar eventos de maneira transacional.

Terracota (novamente para java) resolve muito disso para você - basta atualizar a memória - terracota é sua fachada/mediador aqui.Eles acabaram de injetar os aspectos para você.

Terracota (eu não trabalho para eles) - introduz o conceito de "superestático", então você obtém esses singletons em todo o cluster que são legais, mas você só precisa estar ciente de como isso afetará o fluxo de trabalho de teste e desenvolvimento - ou seja.use muita composição, em vez de herança de implementações concretas para uma boa reutilização.

Para aplicativos da web - um bom servidor de aplicativos pode ajudar na replicação de variáveis ​​de sessão e um bom balanceador de carga funciona.De certa forma, usar isso por meio de um REST (ou do método de serviço da web de sua escolha) é uma maneira fácil de escrever um serviço multithread.Mas isso terá implicações de desempenho.Novamente depende do domínio do seu problema.

Os serviços de mensagens (digamos, jms) são frequentemente usados ​​para introduzir um acoplamento fraco entre diferentes serviços.Com um servidor de mensagens decente, você pode fazer muito roteamento de mensagens (novamente, o Apache Camel ou similar faz um ótimo trabalho), ou seja.digamos, um consumidor persistente contra um grupo de produtores de JMS, etc.isso também pode permitir um bom failover.As filas Jms etc. podem fornecer uma maneira simples de distribuir cmds no cluster, independentemente do mestre/escravo.(novamente, depende se você está fazendo LOB ou escrevendo um servidor/produto do zero).

(se eu tiver tempo mais tarde, vou arrumar, talvez colocar mais alguns detalhes para corrigir a ortografia, gramática, etc.)

Outras dicas

Uma abordagem para criar software confiável é Software somente de falha:

O software somente em fias é software que trava com segurança e se recupera rapidamente. A única maneira de parar é travá -lo, e a única maneira de iniciá -lo é se recuperar. Um sistema somente em colisão é composto por componentes somente de colisão que se comunicam com solicitações de repetíveis; As falhas são tratadas travando e reiniciando o componente com defeito e novamente novamente as solicitações que tenham tempo para o tempo. O sistema resultante geralmente é mais robusto e confiável, porque a recuperação de falhas é um cidadão de primeira classe no processo de desenvolvimento, em vez de uma reflexão tardia, e você não precisa mais do código extra (e interfaces e bugs associados) para desligamento explícito. Todo o software deve ser capaz de travar com segurança e se recuperar rapidamente, mas o software apenas com falha deve ter essas qualidades, ou sua falta se torna rapidamente evidente.

Eu recomendo que você leia Solte! por Michael Nygard.Ele descreve uma série de antipadrões que impactam os sistemas de produção e padrões para ajudar a evitar que um componente errôneo derrube todo o sistema.O livro cobre três áreas principais;Estabilidade, Capacidade e Design Geral (abrangendo Rede, Segurança, Disponibilidade e Administração).

Meu local de trabalho anterior foi afetado (uma vez ou outra) por praticamente todos os cenários de falha descritos por Nygard (com perda de receita para cada interrupção resultante).A implementação de algumas das técnicas e padrões que ele sugere resultou em sistemas significativamente mais estáveis ​​e previsíveis (e sim, o livro é um pouco centrado em Java, mas os princípios são aplicáveis ​​em muitos contextos).

Errado:

... e haverá um servidor de armazenamento

Bom:

... e haverá uma fazenda de (múltiplos) servidores de armazenamento com (múltiplos) balanceadores de carga na frente deles

  • Coloque os balanceadores de carga na frente de tudo. Por enquanto, você pode ter 4 back -end, mas no futuro você pode ter 400 deles, por isso é aconselhável gerenciá -lo apenas no LB, nem todos os aplicativos que usam o back -end.

  • Use vários níveis de cache.

  • Procure soluções populares para acelerar os thigs (Memcached, por exemplo).

  • Se você for renovar um sistema, faça-o parte por parte, em várias etapas pequenas. Se você fizer isso em um grande passo (desligue o antigo, ligue o novo e ore que funcionará), provavelmente falhará.

  • Use nomes DNS para coisas, Fe storage-lb.servicename Resolva para endereços de todos os balanceadores de carga de armazenamento. Se você deseja adicionar um, basta modificar o DNS, todos os serviços começarão a usá -lo automaticamente.

  • Mantenha simples. Quanto mais sistemas você depende, mais seu serviço sofrerá com ele.

Projetar sistemas de alta disponibilidade (HA) é uma área ativa de pesquisa e desenvolvimento.Se você olhar para ACM ou IEEE, há muitos artigos de pesquisa sobre qualidades de serviço (disponibilidade, confiabilidade, escalabilidade, etc.) e como alcançá-las (acoplamento fraco, adaptação, etc.).Se você está procurando mais aplicações práticas, dê uma olhada em sistemas tolerantes a falhas e middleware que são construídos para permitir funcionalidades semelhantes a clustering, grade ou nuvem.

Replicação e balanceamento de carga (também conhecido comoproxy reverso) são alguns dos padrões mais comuns para alcançar sistemas de alta disponibilidade e muitas vezes podem ser feitos sem fazer alterações no código do software subjacente, presumindo que ele não esteja muito acoplado.Mesmo muitas das ofertas recentes de nuvem são alcançadas essencialmente por meio de replicação e balanceamento de carga, embora tendam a criar elasticidade para lidar com amplas faixas de demanda do sistema.

Tornar os componentes de software sem estado alivia o fardo da replicação, pois o estado em si não precisa ser replicado junto com os componentes de software.A apatridia é uma das principais razões pelas quais o HTTP é tão bem dimensionado, mas muitas vezes exige que os aplicativos adicionem seu próprio estado (por exemplo,sessões) que então precisa ser replicado.

Portanto, é mais fácil tornar altamente disponíveis sistemas fracamente acoplados do que sistemas fortemente acoplados.Como a confiabilidade dos componentes do sistema determina a confiabilidade geral do sistema, os componentes que não são confiáveis ​​podem precisar ser substituídos (falhas de hardware, bugs de software, etc.).Permitir a adaptação dinâmica em tempo de execução permite que esses componentes com falha sejam substituídos sem afetar a disponibilidade de todo o sistema.O fraco acoplamento é outra razão para o uso de sistemas de mensagens confiáveis, onde o remetente e o destinatário não precisam estar disponíveis ao mesmo tempo, mas o próprio sistema ainda está disponível.

Pelo que entendi, você está procurando padrões específicos para usar em aplicativos Java parte de uma arquitetura de HA. É claro que há um número numeroso de padrões e práticas recomendadas que podem ser usadas, mas esses não são realmente "padrões de HA". Em vez disso, são boas idéias que podem ser utilizadas nos contextos das manys.

Acho que o que estou tentando dizer é o seguinte: uma arquitetura de alta disponibilidade é composta por inúmeras peças pequenas. Se escolhermos uma dessas pequenas partes e examiná -las, provavelmente descobriremos que não há atributos mágicos de HA para esse pequeno componente. Se examinarmos todos os outros componentes, encontraremos a mesma coisa. É quando eles são combinados de maneira inteligente que se torne um aplicativo HA.

Um aplicativo HA é um aplicativo em que você planeja o pior desde o início. Se você pensa em termos de "esse componente é tão estável que não precisamos de redundância adicional para isso", provavelmente não é uma arquitetura de HA. Afinal, é fácil lidar com os cenários de problemas que você prevê. É o que o surpreende que derruba o sistema.

Apesar de tudo isso, existem padrões que são especialmente úteis nos contextos de HA. Muitos deles estão documentados no livro clássico "Padrões de arquitetura de aplicativos corporativos" Por Martin Fowler.

Estou interpretando "Alta disponibilidade" Como "Zero tempo de inatividade"`, que pode ser implementado de acordo com outra pergunta de SE:

Implantação zero de tempo de inatividade para aplicativos Java

  1. Switch A/B: (Rolling Upgrade + Fallback Mecanism)
  2. Implantação paralela - apache tomcat: (apenas para aplicativos da web)
  3. Encadernação de porta atrasada
  4. Encadernação avançada da porta

Usarei alguns desses conceitos para criar padrões de design para um sistema de alta disponibilidade da perspectiva do software, o que complementa as abordagens acima.

Padrões a serem usados:

Proxy/Fábrica :

Tenha um objeto de proxy e proxy decidirá onde redirecionar as solicitações. Suponha que você tenha versão 1 e versão 2 do software. Se os clientes estiverem se conectando ao protocolo antigo, redirecre -os para o software versão 1. Novos clientes podem se conectar diretamente à versão 2. O proxy pode ter método de fábrica ou abstratofactory para renderizar uma nova versão do software.

Estratégia

Você pode alterar o algoritmo no tempo de execução selecionando um algoritmo de uma família de algoritmos. Se você recebe o exemplo das companhias aéreas, poderá alternar entre os algoritmos de desconto e normal durante os meses de tráfego sem pico e pico.

Decorador:

Você pode alterar o comportamento do objeto no tempo de execução. Adicione uma nova classe e decore responsabilidade adicional.

Adaptador:

Útil quando você altera a interface ou contrato entre a versão 1 e a versão 2. o adaptador responderá às solicitações antigas e novas do cliente adequadamente.

Diretrizes gerais:

  1. Acoplamento frouxo entre objetos
  2. Seguir SÓLIDO Princípios em seu aplicativo

Referir-se SOURCEMA Artigos do site para padrões acima para melhor compreensão.

O que não usar:

Além dos padrões de design, você deve tomar algumas precauções para obter tempo de inatividade zero para o seu aplicativo.

  1. Não introduza o ponto único de falhas em seu sistema.
  2. Use caches distribuídos (por exemplo, terracota) /bloqueios com moderação.
  3. Remova o acoplamento duro entre os serviços. Remova o acoplamento apertado entre os serviços usando o barramento de mensagens/ estruturas (JMS, ActiveMQ etc.)

A alta disponibilidade é mais sobre disponibilidade e redundância de hardware do que sobre convenções de codificação. Existem alguns padrões que eu usaria em quase todos os casos de ha: eu escolheria o Padrão Singleton para o meu objeto de banco de dados e use o padrão de fábrica Para criar o Singleton. A fábrica pode então ter a lógica para lidar com problemas de disponibilidade com o banco de dados (que é onde a maioria dos problemas de disponibilidade acontece). Por exemplo, se o mestre estiver inativo, conecte -se a um segundo mestre para leituras e gravações até que o mestre esteja de volta. Não sei se esses são os padrões mais alavancados, mas são os mais alavancados no meu código.

Obviamente, essa lógica pode ser tratada em um método __construct, mas um padrão de fábrica permitirá que você controla melhor seu código e a lógica de tomada de decisão de como lidar com problemas de conectividade do banco de dados. Uma fábrica também permitirá que você lide melhor com o padrão de singleton.

Eu absolutamente evitaria o Padrão de decorador, e as Padrão de observador. Ambos criam complexidade em seu código que dificulta a manutenção. Seus são casos em que essas são a melhor escolha para suas necessidades, mas na maioria das vezes não são.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top