Muito interessante o post do Digg que fala sobre as tecnologias, estratégias e problemas que o Digg (assim como outras aplicações gigantes na Web) vive ao redor do tema escalabilidade.
Quando se trata de aplicações web populares como Digg, Twitter e Facebook, escalabilidade se torna um desafio e tanto, e manter a aplicação com redundância, performance e consistência gera um custo grande de manutenção e monitoramento.
Até muito recentemente, ou até hoje mesmo, muitos desses sites usam a solução de Mysql + memcached para poderem suportar o nível de escalabilidade necessário. Por exemplo, até o fim de 2008, o Facebook possuía mais de 800 servidores de memcached para poder servir mais de 28 terabytes de cache. É mole? 28 Tera de cache! E isso, só para poder servir a leitura de dados em cache. Na hora de escrever os dados, aí usa-se uma solução de Mysql configurado com Master-Slave, e muitas vezes com particionamento vertical.
O fato atualmente é: esta solução não está mais dando conta do recado. Alguns desses sites estão saindo da solução com Mysql+memcached e estão indo para o mundo não SQL. E o que muitos deles estão adotando é o Cassandra.
Cassandra é um banco de dados distribuído, altamente escalável, baseado no conceito de BigTable.
É isso. E vem por aí uma nova era para as soluções de aplicações web de alto volume.
Vale a leitura do post do Digg.
Abcs
Escalabilidade de aplicações Web: futuro vs presente = Cassandra vs Mysql+memcached
GWT + Grails = Rock n Roll: Parte 2 - Separados e falando com RequestBuiler e JSON
Este post é a Parte 2 da sequência de posts sobre o tema GWT + Grails = Rock n Roll. Você pode ver a Parte 1 aqui. Nesta primeira mostrei como criar uma aplicação baseada em GWT e Grails. Para juntar os dois, usei o plugin Grails-Gwt.
Neste post de hoje, depois de alguns dias lendo sobre GWT, fazendo testes e brincando com a aplicação criada no post da Parte 1, resolvi tentar também desenvolver outra aplicação com GWT + Grails mas desta vez sem usar o plugin. Desta forma, poderia eu mesmo decidir se prefiro com ou sem o Plugin. Este post, então, mostrará como criar uma solução composta de duas aplicações isoladas, uma GWT e outra Grails, que se falam via troca de dados usando JSON.
Separando Grails e GWT em duas aplicações distintas
Como descrevi na Parte 1, não usar o plugin e ter duas aplicações distintas (uma GWT e outra Grails) significa ter que lidar com a Same Origin Policy (SOP) em tempo de desenvolvimento. Em produção simplesmente pode-se dar um jeito de levar os arquivos Html, Css e Javascript gerados pelo GWT para o propeto grails (em último caso, basta copiar e colar de um lado para o outro). Mas em desenvolvimento é preciso lidar com SOP de uma maneira mais produtiva do que ter que ficar copiando e colando de um lado para outro. A maneira mais simples que encontrei foi usando uma ProxyServlet, originalmente criada por Jason Edward, e depois evoluída por Matt Raible. O código fonte desta classe eu peguei no projeto Grails OAuth Plugin criado pelo M Raible. É simples de usar, e resolve o problema facilmente. O que ela faz neste nosso contexto é:
- a aplicações GWT no cliente faz uma requisição para o servidor;
- a aplicações GWT no servidor recebe esta requisição e repassa para a aplicação Grails
- a aplicação Grails recebe esta requisição e responde JSON
- a aplicação GWT recebe esta resposta JSON e repassa para o cliente GWT
Desta forma não precisamos nos preocupar com SOP.
O que se ganha em fazer isso? Para que, afinal, quero ter duas aplicações separadas (uma GWT e outra Grails). Bom, isso acaba sendo uma decisão pessoal de cada um, ok? Mas para mim, comecei a achar mais interessante seguir os padrões de cada projeto. Um projeto GWT, segundo as recomendações, deve ser organizado de uma maneira, com determinada árvore de diretórios, etc. Pode não ser seguido, mas é um padrão recomendado. Então, porque não seguir e falar a mesma lingua em chats, fóruns de discussão, novos desenvolvedores na equipe, etc?
Além disso, separar os dois projetos provoca uma reflexão conceitual interessante sobre arquitetura de aplicações ricas para a web (RIA - Rich Internet Applications). Ao separar, você está literalmente, fisicamente, implementando a arquitetura SOFEA (Service Oriented Front-End Architecture). SOFEA foi muito bem apresentado no artigo Life Above the Service Tier (vale a leitura. Este pessoa hoje em dia criou um grupo sobre este assunto e tem um site em: ThinServerArchitecture.com ).
Numa arquiteturea SOFEA, toda a lógica de interface com usuário está sendo rodada no cliente, no nosso caso, no browser. Aqui então, as camadas View e Contoller da aplicação terão um projeto só pra elas (o projeto GWT). Ou seja, o desacoplamento é total. Você simplesmente desacopla o máximo que pode o tal do Front-End. Isso é interessante na minha opinião, pois aí a sua aplicação servidora, que roda no servidor, ela acaba por ter a responsabilidade apenas de prover serviços para a aplicação de Front-End (User Interface - UI). Parece até que estamos voltando para o conceito Cliente x Servidor, e de certa forma, estamos sim, mas um pouco diferente, pois é um Thin Client, onde apenas as regras de interface e controle de fluxo de navegação estão no cliente. Todas as demais regras de negócio, estão e devem estar no servidor.
Outras duas justificativas para esta separação em duas aplicações são:
- Pode ficar mais simples equipes diferentes trabalharem cada uma em um projeto diferente
- Cada projeto pode ser versionado independentemente
Comunicação entre GWT e Grails com JSON
Para a comunicação entre o Front-End e o Back-End, escolhi usar JSON. A aplicação grails pode facilmente gerar uma resposta JSON, enquanto que a aplicação GWT pode facilmente consumir JSON. Então, sendo o JSON um formato leve para troca de dados, e sendo ele facilmente servido e consumido pelo Grails e GWT respectivamente, acho que JSON é uma boa escolha. Mas, se você preferir trabalhar com XML, ou outro formato proprietário, a decisão é sua.
Portanto, para criarmos esta nova aplicação, basta criar um novo projeto GWT, e outro Grails.
Aplicação Front-End (GWT)
Eu estou usando o Eclipse com o plugin do GWT, portanto, para criar um novo projeto basta clicar na opção de criar novo projeto GWT do Eclipse. Criei este projeto GWT chamado rockgwtfront, com o pacote com.rockgwt.front
O wizard do Eclipse já cria uma classe EntryPoint, o arquivo XML de configuração do Module, e também cria um Serviço RPC de exemplo. Mas não precisaremos deste serviço para este exemplo, portanto, pode apagar o pacote com.rockgwt.front.server e o pacote com.rockgwt.front.shared, e também pode apagar os arquivos GreetingService.java e GreetingServiceAsync.java do pacote com.rockgwt.front.client. Depois altere a classe Rockgwtfront.java para não usar mais este Serviço RPC. No final das contas, fiquei com a classe de EntryPoint abaixo:
package com.rockgwt.front.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.Response;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONException;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RootPanel;
public class Rockgwtfront implements EntryPoint {
public void onModuleLoad() {
final Button sendButton = new Button("Get Hendrix songs");
final FlexTable songsTable = new FlexTable();
// We can add style names to widgets
sendButton.addStyleName("sendButton");
sendButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
String hendrixSongsServiceUrl = GWT.getHostPageBaseURL();
if(!GWT.getHostPageBaseURL().contains("rockgwt"))
hendrixSongsServiceUrl += "rockgwt/";
hendrixSongsServiceUrl += "hendrix/songs";
RequestBuilder builder = new RequestBuilder(RequestBuilder.GET, hendrixSongsServiceUrl);
builder.setTimeoutMillis(10000);
builder.setCallback(new RequestCallback() {
@Override
public void onResponseReceived(Request request, Response response) {
if (response.getStatusCode() == 200) {
JSONValue v = JSONParser.parse(response.getText());
JSONArray songs = v.isArray();
if(songs != null){
updateBacklogItemsTable(songsTable, songs);
}
else{
throw new JSONException("O retorno nao veio como um objeto de Projeto");
}
} else {
onError(request, new RequestException(response.getText()));
}
}
@Override
public void onError(Request request, Throwable exception) {
Window.alert(exception.getLocalizedMessage());
}
});
try {
builder.send();
} catch (RequestException e) {
Window.alert(e.getLocalizedMessage());
}
}
});
RootPanel.get("sendButtonContainer").add(sendButton);
RootPanel.get("songsTableContainer").add(songsTable);
}
private void updateBacklogItemsTable(FlexTable songsTable, JSONArray songs) {
songsTable.clear();
for (int i=0; i<songs.size(); i++) {
JSONObject item = songs.get(i).isObject();
songsTable.setWidget(i, 0, new Label(item.get("name").isString().stringValue()));
songsTable.setWidget(i, 1, new Label(item.get("album").isString().stringValue()));
}
}
}
Veja que a classe acima utiliza um RequestBuilder para realizar a requisição no servidor. Este RequestBuilder é uma classe do GWT que pode ser usada para fazer requisições remotas. Com ela, as requisições são sempre assíncronas, SEMPRE. Veja que passei um objeto Callback que será chamado quando esta requisição voltar do servidor. Assim, neste callback posso fazer o que for preciso com a resposta recebida. Neste caso, devo fazer o parse do JSON para poder trabalhar com os dados retornados. No fim de tudo, pego esses dados e os apresento numa tabela, só isso.
Veja que a aplicação GWT espera um retorno JSON no seguinte formato:
[{"name":"Hey Joe", "album":"Are You Experienced"},
{"name":"Bold as Love", "album":"Axis: Bold as Love"}]
Portanto, é preciso que a aplicação Grails retorne um JSON exatamente neste formato acima.
É preciso adicionar ao ~/rockgwtfront/war/WEB-INF/lib do projeto os JARs necessários para a ProxyServlet citada acima. Portanto, adicione commons-fileupload-1.2.1.jar, httpclient-4.0.1.jar, e httpmime-4.0.1.jar ao lib. Crie a classe ProxyServlet. Você pode baixar o OAuth 1.3 e pegar o ProxyServlet de lá. Em seguida, é preciso configurar o web.xml para que ele saiba que o ProxyServlet existe. Vou mapear este proxyServlet para ele ser chamado para toda requisição que tenha o padrão /rockgwtback/*. Veja abaixo:
<web-app> <servlet> <servlet-name>RockServletservlet-name> <servlet-class>com.rockgwt.front.servlet.ProxyServletservlet-class> <init-param> <param-name>proxyHostparam-name> <param-value>localhostparam-value> init-param> <init-param> <param-name>proxyPortparam-name> <param-value>8080param-value> init-param> <init-param> <param-name>secureparam-name> <param-value>falseparam-value> init-param> servlet> <servlet-mapping> <servlet-name>RockServletservlet-name> <url-pattern>/rockgwt/*url-pattern> servlet-mapping> <welcome-file-list> <welcome-file>Rockgwtfront.htmlwelcome-file> welcome-file-list>
web-app>
Veja que o servlet-mapping mostra como deverá ser a url a ser chamada pelo cliente GWT para o servidor Grails. ( /rockgwtback/* ).
Aplicação Back-End (Grails)
Para o projeto Grails, ou você cria normalmente por linha de comando, ou pode até criar pelo Eclipse mesmo se estiver usando o STS (SpringSource Tool Suite). Criei este projeto Grails chamando-o de rockgwtback. Para este projeto, criei um controller chamado HendrixController que possui a action "musics". Esta action retorna uma lista de músicas do Hendrix. Veja abaixo:
package com.rockgwt.back.controller
class HendrixController {
def index = { }
def songs = {
println "chegou"
render(builder: 'json') {
songs = array {
song name: '1) Hey Joe', album: 'Are You Experienced'
song name: '2) Bold as Love', album: 'Axis: Bold as Love'
}
}
}
}
Alterei também o nome da aplicação dentro do application.properties, colocando app.name=rockgwt, pois foi assim que eu coloquei a URL da requisição lá em cima, na aplicação GWT, e também no servlet-mappging do web.xml
É isso!
Assim, temos um sistema rodando com Grails e GWT, independentes um do outro, mas juntos formando a nossa aplicação. Faltaria aqui neste exemplo uma forma de juntar as duas aplicações para produção. Provavelmente faria isso com o Maven. Em produção, então, seria uma única aplicação mesmo.
Grails + GWT = Rock n Roll: Parte 1 - Desbravando um novo mundo
Meus amigos leitores (quantos devem ser estes leitores? deixe uma mensagem dizendo que vc é meu leitor! rs...) ... Bem vindos ao maravilhoso mundo do Rock n Roll. As bandas de rock muitas vezes são mal vistas por pessoas mais tradicionais. Outras vezes são veneradas por jovens rebeldes. Não importa. As boas bandas no final das contas só fazem uma coisa: dão um show de Rock n Roll. Mas o que é isso afinal? Sem dúvida a música é o principal produto desta fábrica rebelde. Por trás de pessoas tatuadas, cheias de brincos e roupa rasgada, por vezes alcoólatras e drogadas, bem no fundo há músicos que juntos ( e aqui está o mais importante: JUNTOS) conseguem tocar almas sedentas de Rock puro e simples. Já ouviu um bom Lynyrd Skynyrd com suas 3 guitarras, piano, batera, etc? É brincadeira o que esses caras tinham de musicalidade. Juntos eles deixam os fãs de rock embasbacados até hoje em dia.
- - a DAO está bem resolvida,
- - os Serviços estão lá e cumprem seu papel (de controle transacional, por exemplo),
- - os Controllers também tem seu papel de fronteira entre o mundo HTTP e nossas regras de negócio,
- - mas aí chega na View e facilmente a gente começa a se perder e fazer uma bagunça com HTMLs, CSSs, Styles, Javascript em arquivos .js, Javascript inline, e por aí vai.
- - Instalar o SDK do GWT
- - Criar a variável de ambiente GWT_HOME apontando para a raiz do SDK do GWT
- - Instalar o Plugin de Eclipse do GWT
- - Criar uma aplicação pura e simples GWT usando o plugin do eclipse.
- - Abrir a aplicação no browser usando a URL apresentada pelo Eclipse (com o parâmetro gwt.codesrv na URL - este parâmetro é que permite você alterar o código Java e imediatamente dar um refresh no browser e ver sua mudança lá).
- - Garantir que o browser instalou o plugin de execução do GWT.
- - Executar esta aplicação e brincar um pouco com os diferentes Layouts de GWT.
- - Criar uma nova aplicação Grails com o comando grails create-app rockgwt
- - Vai pra dentro dela ( cd rockgwt )
- - Instalar o plugin GWT do Grails (usando o comando grails install-plugin gwt ). Eu estou usando o grails 1.2.x e o plugin 0.5
- - Criar um GWT Module com o comando do plugin grails create-gwt-module com.rockgwt.RockNRoll
- - Criar a página principal do GWT para o seu Module. Eu fiz isso criando um controller do grails e uma Page index.gsp para este controller. Esta page é que irá hospedar o GWT:
- 1) grails create-controller com.rockgwt.controller.Principal
- 2) grails create-gwt-page principal/index.gsp com.rockgwt.RockNRoll (quando este comando te perguntar se você quer criar o Controller, diga que não, pois você acabou de criá-lo no passo anterior --- acho que o plugin não reconhece o Controller criado com package, sei lá).
- - Compilar o seu módulo com o comando grails compile-gwt-modules (demora um pouquinho mesmo)
- - Executar o Development Mode com o comando grails run-gwt-client Perceba que vai se abrir a janela da aplicação Development Mode do GWT
- - Abra outro Terminal , vá para o seu projeto rockgwt e execute a aplicação grails (é isso mesmo, você terá duas coisas rodando: o grails run-app da aplicação, e o grails run-gwt-client anterior, do GWT).
- - Abra o browser e vá para a Home da sua aplicação GWT + Grails em: http://localhost:8080/rockgwt?gwt.codesvr=127.0.0.1:9997
- - Na verdade, pode ser que na sua máquina a porta final seja outra, não sei. Para ganrantir sucesso, basta clicar no botão Launch Default Browser existente na janela do Development Mode.
- - O endereço que você quer acessar no final de tudo será o seu controler Principal, ou seja:
http://localhost:8080/rockgwt/principal/index?gwt.codesvr=127.0.0.1:9997 - - Não deixe de colocar este parâmetro maluco. Ele é o responsável por permitir que você altere o código Java e dê um refresh no browser para ver as modificações imediatamente.
package com.rockgwt.client;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.RootPanel;
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class RockNRoll implements EntryPoint {
/**
* This is the entry point method.
*/
public void onModuleLoad() {
final Button sendButton = new Button("Rock n Roll");
RootPanel.get().add(sendButton);
}
}
UPDATE (8/3/2010):
- Apenas para completar, para poder usra o Eclipse com tudo redondo, clique no botao direito do mouse em cima de src/gwt e selecione a opção Build Path > Use As Source Folder
- E por último clique com o direito do mouse em cima do projeto rockgwt e selecione a opção Google > Webtoolkit Settings, lá dentro do wizard que se abre, selecione o primeiro checkbox que aprece lá em cima "Use Google Web Toolkit". Assim você terá tudo que precisa no classpath da aplicação no Eclipse.
Executando códigos diferentes em Desenvolvimento e Produção
Há vezes em que queremos executar códigos diferentes dependendo do ambiente que a aplicação está sendo executada. Por exemplo, integrações com sistemas externos algumas vezes são simuladas em Desenvolvimento, e são executadas pra valer em Produção.
import grails.util.Environment;
class MeuController{
Environment.executeForCurrentEnvironment {
production {
// AQUI vai o código que deve ser executado em produção
}
development {
// AQUI vai o código a ser executado em desenvolvimento.
}
}
}
Boas informações sobre grails em português
Pessoal
Grails e datas: Bind automático de atributo do tipo Date com formato customizado
Segue uma pequena receita de bolo para quem não está usando o datePicker padrão do Grails.
Pessoa tx = new Pessoa(params)
package com.meusistema.editor;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.context.i18n.LocaleContextHolder;
public class CustomPropertyEditorRegistrar implements PropertyEditorRegistrar {
def messageSource;
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat(messageSource.getMessage("dateFormat4y",null,'dd/MM/yyyy',LocaleContextHolder.locale )),true));
}
}
dateFormat4y=dd/MM/yyyy
beans = {
customPropertyEditorRegistrar(com.manubia.propertyeditor.CustomPropertyEditorRegistrar) {
messageSource = ref('messageSource')
}
}