Caelum | Ensino e Inovação - Cursos de Java, Scrum, Ruby on Rails


Um produto para muitos clientes: implementando multitenancy

Por Guilherme Silveira em 23/08/10

São diversos as aplicações web disponíveis, como quadros eletrônicos, sistemas de tracking, email e aplicações para empresas, ou até mesmo controle de clientes e vendas. Eles até ganharam um pomposo nome dentro do cloud computing: Software as a Service (SAAS).

O que essas aplicações possuem em comum? Todas elas atendem diversos clientes sem que um tenha conhecimento da existência dos outros.

Em um post recente no Tectura.com.br foram discutidas vantagens e desvantagens de diversas abordagens para produtos com a necessidade de suportar mais de um tenant.

A Microsoft categoriza três tipos de abordagens dependendo do nível de compartilhamento de recursos entre os clientes e apresente um relatório onde analisa os custos contra a segurança.

Em um extremo, nada é compartilhado entre cada cliente: para cada nova conta criada dentro de seu serviço, é criada uma nova máquina na cloud e uma instalação limpa é executada, com seu próprio banco.

Dois servidores, dois bancos

Nessa abordagem p processo de autorização é automático, um cliente é criado através da instalação automática de uma nova máquina virtual e os recursos são compartilhados se o ambiente for uma cloud.

Na outra ponta, tudo é compartilhado: novos clientes são inseridos no mesmo conjunto de máquinas e a instalação é feita através de um simples insert no banco, adicionando um novo cliente. Qual abordagem escolher?

Um servidor, um banco

A autorização é feita programaticamente na aplicação, os recursos são compartilhados entre todas elas, escala-se tipicamente através do uso de load balancers e replicação master/slave e a cada novo cliente basta executar um insert no banco.

Em qualquer abordagem onde o banco seja compartilhado por diversas empresas, precisamos garantir a segurança dos dados de cada uma, para que nenhum acesso indevido ocorra, trazendo preocupações de autorização para dentro do código de nossa aplicação. Caso seja criado para cada cliente um banco em uma máquina no cloud, essa questão fica concentrada em um único ponto da arquitetura, a segurança está implícita.

Dois servidores, um banco

Diversos servidores e um banco implica em clientes não afetarem uns aos outros, um controle programático de autorização e novos clientes são criados a partir da instalação de um novo contexto web.

Ao mesmo tempo, o processo de customização de seu serviço por cliente também é afetada de acordo com o nível de compartilhamento de dados entre eles. Na abordagem onde os clientes compartilham o mesmo servidor web, a customização é uma preocupação de nossa aplicação, enquanto ao utilizarmos aplicações web distintas para cada cliente, podemos facilmente separar customizações por instância, por deploy efetuado.

A escalabilidade é afetada pois compartilhar recursos em memória na camada web, entre eles dados cacheados do banco ou filas, permitem diminuir o tempo de processamento ou de latência, aumentando o número de requisições suportados.

O processo de escalar também está diretamente ligado: quando uma máquina não aguentar mais as requisições, será levantada uma outra máquina que funcionará em cluster para um, n ou todos os clientes?

Outro fator importante é como limitar o uso do serviço de acordo com as regras contratadas pelo cliente e controlar o dano que um pode causar a outros. Em um pico de uso por parte dos usuários de um cliente, ativamos o chargeback, dependendo do tipo de serviço que é prestado. Implementações comuns de chargeback em cloud (baseados em virtualização) permitem um pico de consumo temporário ou até mesmo mover a aplicação de uma máquina para outra sem que cliente algum perçeba o que está acontecendo.

Dois servidores, um banco

Diversos servidores e um banco implica em clientes não afetarem uns aos outros, um controle programático de autorização e novos clientes são criados a partir da instalação de um novo contexto web.

Ao invés daqueles usuários atrapalharem a performance de outros clientes, é alocado memória e processador distintos dos atuais para ele.

A simplicidade do código é um dos fatores mais afetados pela escolha feita: um código que sabe da existência de múltiplos clientes está diretamente ligado a uma chave que identifica o cliente atual. E essa ligação se reflete por todos os lados do código, uma vez que o comportamento da aplicação em geral depende dele: uma chave estrangeira, um relacionamento, que vai permear toda a aplicação.

Caso a aplicação implemente o suporte a multi tenant através de instalções web distintas, com configurações para cada cliente apontando para um bancos distintos, o código fica mais simples e fácil de manter, uma vez que não é necessário se preocupar com a existência de outros clientes.

Por fim, uma preocupação que surge para a empresa provedora dos serviços é de como agrupar os dados existentes em todos os clientes e gerar relatórios de administração e estatísticas que permitam a melhora do serviço prestado. Quando possuimos um único banco, escalado através de master slave ou múltiplos nós, basta executarmos queries longas (sql ou não), mas se o sistema estiver distribuido entre diversos bancos sem ligação entre si, um processo de batch deve rodar para agrupar os dados e permitir o consumo posterior pelas ferramentas de BI.

É importante analisar todos esses pontos antes de tomar a decisão se o controle de espaço de aplicação por cliente será feito no nosso código, na camada web, no banco ou em algum outro ponto. Como discutimos bastante no curso de arquitetura, toda e qualquer decisão implica em um tradeoff: o importante é saber o que está sendo trocado e qual é o impacto disso.

  • Share/Bookmark

Então você quer ser um arquiteto Java?

Por Paulo Silveira em 21/07/10

Durante o atual processo de revisão do livro de Arquitetura e Design de Software, discussões apareceram sobre o termo arquiteto. Antes de definir o que faz um arquiteto, há o termo arquitetura.

Quem é o arquiteto? Aquele que senta sozinho e toma todas as grandes decisões?

O que é a arquitetura de uma aplicação?
Uma pergunta difícil de responder. Entre as definições mais antigas, Roy Fielding possui um bom texto no primeiro capítulo de sua dissertação de doutorado. O Instituto de Engenharia de Software da Universidade de Carnegie Mellon apresenta diferentes definições, algumas clássicas e bastante conhecidas, como “arquitetura é a estrutura do sistema, composta de componentes, as propriedades que são visíveis externamente desses componentes e o relacionamento entre eles“.

Nas palavras de Martin Fowler, “o termo arquitetura envolve a noção dos principais elementos do sistema, as peças que são difíceis de mudar. Uma fundação na qual o resto precisa ser construído“. Fowler reformula sua definição de arquitetura e a define como “as peças que as pessoas acham que é difícil de mudar“. No mesmo artigo Ralph Johnson, do GoF, diz que arquitetura “é o conjunto de decisões de design que gostaríamos de ter feito no começo do projeto” e termina com uma definição mais abrangente: “arquitetura é tudo aquilo que importa“. Com tantas definições, talvez seja mais fácil diferenciarmos design de arquitetura.

Qual é a diferença de design e arquitetura de software?
Aqui também temos uma resposta clássica na literatura: a arquitetura é responsável pelos requisitos não-funcionais, e o design pelos funcionais. Mas parece que essa distinção não é tão clara assim para muitos outros autores.

Neal Ford apresenta uma distinção simples, realçando que o design é feito em cima do que foi decidido pela arquitetura, e por isso o que faz parte da arquitetura é mais difícil de mudar. Devemos minimizar as peças que dificultam mudanças do nosso design, mas é impossível eliminar todas, além de que flexibilidade sempre vem a um custo de complexidade.

É difícil criar um distinção maior entre os dois. No livro Patterns of Enterprise Application Architecture, Fowler diz que “alguns dos padrões nesse livro podem ser chamados arquiteturais, já que representam decisões importantes sobre essas partes; outros são mais sobre design e te ajudam a implementar essa arquitetura. Eu não faço nenhuma tentativa forte de separar esses dois, já que é o que é arquitetural ou não é subjetivo“.

O arquiteto deve saber programar na plataforma em questão?
Sem dúvida. Cada vez mais vemos que o design e a implementação devem ser trabalhados juntos. A imagem de um arquiteto distante sem profundo conhecimento técnico que apenas toma as grandes decisões ficou pra trás: conhecimento técnico e a capacidade de liderança são as características fundamentais.

Mais do que querer ser o poderoso arquiteto que apenas despacha ordens e toma todas as grandes decisões, cada vez mais enxergamos que o caminho é ser o líder que incentiva essa tomada de decisão, além de ser um exímio programador. Parafraseando mais uma vez Martin Fowler, “…o arquiteto deve ser como um guia… que é um experiente e capacitado membro da equipe que ensina aos outros a melhor se virarem, ainda assim ele está sempre lá para as partes mais complicadas“.

Vale lembrar que precisamos de mais de 10 mil horas, ou 10 anos, para dominar uma linguagem.

  • Share/Bookmark

Escalando sistemas com soluções NoSQL

Por douglas.campos em 07/06/10

Um dos grandes desafios enfrentados no dia a dia do desenvolvedor eficaz é o de cumprir requisitos não-funcionais de uma aplicação, principalmente os relacionados a performance e escalabilidade. Uma das alternativas mais conhecidas para escalar horizontalmente é a de dividir as tarefas que não necessitam de retorno imediato ao cliente em processos batch. Para tanto, podemos usar diversas ferramentas, desde soluções caseiras até diversos frameworks, tanto em ruby como em java.

Quando se trata de processos batch, é comum buscar um controle mais fino sobre o resultado da execução (sucesso, falha, pendente, etc). Normalmente, usaríamos bancos relacionais para isso, tanto pela sua popularidade como pela performance. Supondo que eu precise gerenciar 1000 pequenas tarefas simultâneas, pagaríamos um preço bem alto pela concorrência, visto que o banco começaria a demorar para obter o lock, fazer o insert, refazer o lock e atualizar o status da tarefa. Muitos consideram o uso de banco de dados relacionais um erro para casos como esse. Buscando eficiência, poderíamos usar uma solução NoSQL para persistência e controle das tarefas, onde evitaríamos os locks, graças ao modelo de concorrência simplificado.

Uma opção em ruby é o resque, um framework de processamento batch criado pelo github que utiliza o redis, um banco de dados não-relacional, tanto para persistir as tarefas, como para coordenar os diversos processos executores de tarefas, mais conhecidos como workers.

Agendar um processo é bem simples – basta que eu tenha uma classe ruby com um método perform, com o processamento a ser executado e um atributo de instância @queue define em qual fila a tarefa será colocada:

# uma classe qualquer, poderia ser um model
class Relatorio
    @queue = :relatorio_anual

    def self.perform(ano)
        gera_relatorio_anual(ano)
    end
end

e finalmente, faço o agendamento:

#método recebe a classe da tarefa, e os argumentos
Resque.enqueue(Relatorio, 2009)

Essa chamada faz uso bem eficiente de metaprogramação, de maneira que um worker posteriormente executará um código semelhante a esse:

classe, argumentos = Resque.reserve(:relatorio_anual)
classe.perform(*argumentos) if class.respond_to? :perform

posteriormente iniciamos um worker usando as tasks do rake que vem no próprio resque, indicando qual fila o worker que estamos criando vai atender

$ QUEUE=relatorio_anual rake resque:work

assim que o worker subir ele já irá executar Resque.reserve, que o colocará em espera por novos trabalhos. opcionalmente, poderíamos executar um worker para atender todas as filas:

$ QUEUE=* rake resque:work

O principal fator que contribui para que resque e redis funcionem como um relógio é o fato de ambos estarem focados em alta performance e, ao mesmo tempo, extrema simplicidade sem ser muito invasivo, já que permitem que você mantenha sua estrutura de persistência relacional inalterada.

Cada vez mais sistemas vem utilizando solucões NoSQL para algumas funcionalidades específicas, como foi o caso recente do Large Hadron Collider com o Mongo DB. Parece que a combinação de bancos relacionais e não-relacionais no mesmo sistema, cada um com seu propósito, tem sido a direção tomada pelo mercado.

  • Share/Bookmark

Cloud Computing na Casa Branca e o PaaS

Por Paulo Silveira em 14/05/10

Não é novidade empresas economizarem milhares de dólares ao adotarem o cloud, como foi o caso do New York Times. Agora, foi a vez do governo norte americano, que deve economizar mais de um milhão de dólares até 2011 através do Amazon EC2.

Governo americano começa a adotar o cloud.

O Amazon EC2 permite inúmeras configurações diferentes e você pode tomar diversas decisões: quantos servidores precisa, como fazer o balancing, quando colocar ou retirar um novo servidor, etc. Ele te serve com a infraestrutura que você definir, abstraindo o hardware, daí o termo Infrastructure as a Service (IaaS).

A outra opção é a Platform as a Service (PaaS), que restringe suas opções: essa modalidade costuma definir frameworks, uso de threads, conexões, sistema de arquivos e até mesmo qual banco de dados você vai utilizar.

O Google App Engine (GAE) é hoje o líder em plataforma como serviço. Você tem uma série de restrições para utilizá-lo, e o único banco de dados que pode utilizar é um não relacional, o BigTable do Google. Nesse mesmo grupo, encaixam-se o SalesForce (Force.com), o Heroku e o recentemente anunciado VMForce (junto com a SpringSource).

Por que então alguém utilizaria o PaaS em vez de IaaS, se teremos restrições?

Como Krishnan Subramanian descreve em três artigos, o PaaS abstrai todo o ambiente e middleware para você, até mesmo as decisões operacionais e de configuração. Ele vai além, e diz que o PaaS é quem vai ganhar grande parte do mercado, dado que cada vez mais as aplicações, mesmo as que não necessitam (ao menos por enquanto) de grande escalabilidade, vão migrando para o Cloud. Colocar sua aplicação no PaaS é muito mais simples que no IaaS, mas você tem de pagar o preço: suas restrições.

A Caelum usa o Cloud do Google App Engine há mais de 6 meses, e isso já nos salvou de picos de acesso ao sistema de reserva e acesso ao site, como quando a Info Online noticiou nossas apostilas ou quando enviamos nossa newsletter para os milhares de usuários cadastrados. E, mesmo para um site pequeno como o da Caelum, sem grandes requisitos de escalabilidade, a tranquilidade de saber que ele não ficará indisponível por causa de picos momentâneos é o grande atrativo. Além, claro, da enorme redução de trabalho com infraestrutura e custo.

E você, planeja migrar sua aplicação para o Cloud e diminuir a dor de cabeça com escalabilidade? Ela está apta para enfrentar bancos não relacionais ou restrição a threads e file system no PaaS, ou você vai direto para o IaaS, tendo total controle porém mais trabalho operacional?

  • Share/Bookmark

Hipermídia e contratos dinâmicos: menor acoplamento

Por Guilherme Silveira em 17/12/09

Nos últimos anos você vem comprando livros em um website: você acessa o site inicial www.amazon.com, procura pelo livro que deseja comprar, adiciona-o ao seu carrinho, escolhe o método de pagamento e finaliza a compra.

Na época do Natal, o site muda: existe agora uma promoção de fim de ano e você se depara com um conteúdo inesperado: existem funcionalidades e informações novas (como um programa de desconto através de cupons). Como reage um humano ao encontrar a mudança com novas possibilidades de iteração em um site?

  • Gritar: “contrato violado! não comprarei mais nada!
  • Ignorar as novas informações e executar o processo
  • Usar o intelecto humano e se aproveitar das novas informações

Como humanos sabemos o quão natural é agir de maneira a ignorar as informações – caso elas não contribuam com meu objetivo – ou tirar proveito delas.

A opção 1 só se concretiza caso existisse um comprometimento total a maneira que o site disponibilizava suas informações e ao processo: se meu acoplamento for alto e o que eu espero seja fixo, imutável. Infelizmente robôs não são ainda capazes de raciocionar como nós e executar a última opção.

O conteúdo hipermídia permite evoluir o servidor com funcionalidades e dados sem quebrar os clientes consumidores por padrão. Ninguém deixaria de comprar pois fornecemos funcionalidades e dados novos em relação aos recursos disponibilizados.

Isso permitiu a evolução de sites por diversos anos sem que usuários enviassem emails para o responsável reclamando da nova função que foi adicionada, dizendo que não utilizarão o sistema pois existe conteúdo extra.

Hipermídia permite um baixo acoplamento entre o cliente e o servidor e pode ser levado para o mundo da automatização: a web dos sistemas. Na web humana, validamos nossos contratos com o usuário final através do uso de testes end-to-end, verificando a existência de funcionalidades como o usuário o faria.

Diversas opções de ferramentas como selenium-rc e webdriver fornecem funcionalidades para garantir que o comportamento esperado não será quebrado com novos releases.

Eles não validam tudo retornado pela requisição, dando espaço para a ::forward-compatibility::, a capacidade de evoluir nosso sistema no servidor sem quebrar o comportamento esperado. Por exemplo, adicionar novas funcionalidades ou campos não relativos ao teste não deve quebrar o mesmo.

Na web para sistemas integrados, a representação mais comum é o xml, que não suporta conteúdo hipermídia, uma vez que uris devem ser tratadas como texto (de acordo com a especificação) então acabamos criando nossos próprios media-types, como vnd/caelum+xml, onde há a definição de como elas devem ser tratadas: o nosso próprio micro formato.

Existem diversas alternativas para criar esquemas forward e backward compatíveis mas infelizmente esse não é o comportamento padrão de arquivos como o formato ::xsd:: e arquitetos não se lembram disso ao definir seus esquemas, o suporte é opcional.

Dentre essas opções, a mais fácil e possivelmente perigosa envolve permitir qualquer tipo de conteúdo em qualquer campo, enquanto outra solução envolve o uso de tipos polimórficos: um perigoso início de schema-hell controlando diversas versões para uma mesma funcionalidade.

Micro formatos como os que podemos criar permitem a definição de uma estrutura fixa e uma dinâmica: um contrato parcialmente fixo, com garantias para validação e compatibilidade, além de parcialmente dinâmico, com liberdade para evolução, diminuindo o acoplamento que seu sistema possuia ao utilizar um esquema totalmente fixo.

Mas a responsabilidade de não quebrar o contrato original fixo ainda é do servidor.

Na web humana, xhtml permite validar a estrutura (o contrato) enquanto é responsabilidade sua (seus testes) não remover o campo de busca de livro, caso contrário o processo não se completa.

Enquanto esquemas permitem a validação de dados, os testes permitem a validação dos processos. Ambos devem ser escritos de maneira a permitir a evolução desacoplada do servidor e do cliente. E quais seriam então as partes dinâmicas do meu contrato?

Os possíveis estados de seu recurso podem variar com o tempo: uma aplicação para empréstimo pode ser só aprovada ou recusada, mas com o passar do tempo a empresa pode decidir a existência de um novo estado: “prolongado”.

As relações entre seu recurso e outros recursos também variam: um cliente pode ter uma lista de serviços contratados atualmente, acessando a sua representação via links. É natural imaginar que surjam novos serviços e que o cliente mude suas contratações.

As transições e operações disponíveis para seus recursos também são dinâmicas: suportando um método HTTP novo ou um novo link não quebra a existência de clientes que consomem as transições e operações existentes até então.

Todo esse dinamismo é guiado através de hiperlinks e conteúdo hipermídia. Como os clientes terão certeza que não quebramos o contrato dinâmico?

Da mesma maneira que implementamos testes para garantir o comportamento esperado, precisamos deles para garantir que o processo não é alterado no servidor.

Os testes end-to-end são a única garantia de que não quebramos os processos junto ao cliente, seja ele humano ou outro serviço.

Esquemas xml podem ser usados de maneira a garantir flexibilidade e compatibilidade, mas não é o comportamento padrão de tal ferramenta: depende muito mais do usuário conhecer e fazer o uso adequado dela.

ATOM é um exemplo que suporta por padrão contratos dinâmicos: ao seguir o Must Ignore, ganhamos forward e backward compatibility. Contratos dinâmicos fornecem dicas para os frameworks, permitindo ao servidor guiar o cliente naquilo que pode executar ou acessar.

A consequência principal de contratos dinâmicos é o baixo acoplamento.

O Restfulie foca no poder do hipermídia como facilitador na evolução a médio e longo prazo: não são URIs elegantes ou a adoção do protocolo HTTP sozinhos que criam sistemas de baixo acoplamento.

  • Share/Bookmark



Caelum | Ensino e Inovação
São Paulo: Rua Vergueiro, 3185, cj. 87, próximo ao Metrô Vila Mariana   |   Tel. (11) 5571-2751
Rio de Janeiro: Rua Senador Dantas, 80, cj. 307/308 - Centro   |   Tel. (21) 2220-4156 ou 2297-0033
Brasília: SCS Qd. 8 Bl. B-50, Sala 521 - Ed. Venâncio 2000   |   Tel. (61) 3039-4222