Grails de alta performance - parte 2

Este post faz parte de uma série que estou escrevendo sobre técnicas para fazer uma aplicação grails de alta performance. Estou usando uma aplicação de exemplo, para demonstrar estas técnicas. Esta aplicação se chama GPerform, e está disponível no GitHub, e é uma espécie de Twitter + Instagram (em menor escala, naturalmente), onde usuários enviam posts de texto curto + foto.

Na parte 1 desta série, eu mostrei a primeira prática para se desenvolver uma aplicação grails de alta performance: Não usar Collections para as relações hasMany.

Neste post vou mostrar os plugins que usei.

1) Spring-events plugin:

Este plugin dá a possibilidade de publicar eventos via Spring, de forma assíncrona.

Para instalar o plugin, basta executar "grails install-plugin spring-events".

Eu usei este plugin para lançar um evento indicando que alguém enviou um novo post no sistema, e que, portanto, o sistema precisa processar a foto (criando uma foto com tamanho (kb) menor, e outras fotos thumbnail).

Mas, para não deixar o usuário esperando enquanto o sistema processa as fotos, e assim dar uma impressão de muita velocidade de resposta para o usuário, eu quis fazer este processamento de forma assíncrona, dando uma resposta para o usuário quase que imediata após o envio do seu post.

A action que recebe o envio do post do usuário, com texto + foto, apenas grava a foto num diretório, salva a entidade Foto no banco de dados, e lança este evento assíncrono no Spring, retornando a resposta imediatamente para o usuário (sem processar a foto neste momento). Veja:

    def compartilhar = {
        
        String msgErro = ""
        
        if(!session.usuario){
            redirect(controller:'home')
            return
        }
        
        // Pega a foto do formulario e salva no diretorio para ser processada
        MultipartFile fotoOriginal = request.getFile('fotoOriginal')
        String extensao = identificaExtensao(fotoOriginal)
        
        String nomeArquivo = RandomStringUtils.random(10).encodeAsSHA256() + extensao
        
        try {
            File destino = new File(ConfigurationHolder.config.gp.fotos.originais.folder + File.separator + nomeArquivo)
            fotoOriginal.transferTo(destino)
            
            // Salva uma isntancia do objeto de metadados Foto
            Foto foto = new Foto(params)
            foto.autor = Usuario.get(session.usuario.id)
            foto.nomeArquivo = nomeArquivo
            if(foto.save(flush:true)){
                
                // metodo adicionado pelo spring-events Plugin
                publishEvent(new FotoSubidaEvent(new Expando(id:foto.id)))
                flash.message = "Foto enviada com sucesso. Aguarde processamento."
                redirect(action:index)
            }
            else{
                if(destino.exists()) destino.delete()
                msgErro = "Erro ao salvar os dados da foto."
                render(view:'index', model:[msgErro:msgErro, foto:foto])
            }
        } catch (IOException ioe) {
            msgErro = "Erro ao fazer upload do arquivo."
        } catch (IllegalStateException ie) {
            msgErro = "Erro ao fazer upload do arquivo."
        }
    }

Veja que o método importante aqui é o "publishEvent()", que publica o evento FotoSubidaEvent (eu sei que o nome ficou feio, mas ...).  Pronto, o evento foi publicado.

Agora, precisamos criar uma classe que vai ser o Listener deste evento. A maneira mais fácil que encontrei foi criar uma classe de serviço que implementa "ApplicationListener", ou seja, ela será um Listener para os eventos do tipo FotoSubidaEvent. Ao implemetar esta interface, sua classe de Service terá que ter um método "void onApplicationEvent(FotoSubidaEvent event)". Este método será chamado pelo Spring automaticamente quando um evento do tipo FotoSubidaEvent for lançado, de forma assíncrona.

2) Springcache plugin:

Este plugin faz uso do projeto Spring Cache,  e permite que se faça:
- Cache de métodos dos beans do Spring; por exemplo, para fazer cache do retorno de métodos das classes de serviços. 
- Fragmentos das páginas, ou páginas inteiras, geradas pelos controllers; por exemplo, para fazer o cache de uma determinada action de um controller; 

No caso da aplicação de exemplo, GPerform, usei o Springcache para fazer cache da action "HomeController.recentes()" , ou seja, eu coloquei em cache os posts de fotos mais recentes. Assim, não é preciso acessar o banco de dados o tempo todo para pegar os posts mais recentes, pois eles estão na memória do cache.

Para usar este plugin, basta instalar usando o comando "grails install-plugin springcache". Em seguida, coloquei uma anotação na action do meu controller (na action que quero que a resposta fique em cache):

    @Cacheable("recentesCache")
    def recentes = {
        log.info "Executando action 'recentes' e por isso nao ta usando o cache agora."
        def recentes = buscaFotosMaisRecentes(20)
        [fotosMaisRecentes:recentes]
    }

Veja que eu coloquei um nome neste cache ("recentesCache"), pois eu posso criar vários caches distintos, e controlar cada um da forma que eu quiser. Esta anotação @Cacheable("recentesCache") é que indica para o plugin que o retorno desta action precisa ser guardado em memória no cache. Só isso.

Agora o problema passa a ser como renovar o cache quando um novo post for enviado por um usuário do site. Para isso, basta limpar o cache (matar o cache) quando um novo post for inserido. No meu caso, o que fiz foi o seguinte: quando um usuário envia um novo post (que é um texto + foto), o controller recebe esta requisição, faz algumas verificações, salva a foto num diretório para ser processada posteriormente (de forma assíncrona -- falo sobre isso mais tarde), e grava os dados da entidade Foto no banco de dados (que são os metadados sobre a foto e o texto do post do usuário). Quando o sistema for processar a foto de forma assíncrona, aí sim a foto está pronta para ser mostrada no site, e é neste momento que eu preciso limpar o cache. O método que processa a foto e limpa o cache é um método de uma classe de Serviço (FotoService). Veja abaixo:


    @CacheFlush("recentesCache")
    void onApplicationEvent(FotoSubidaEvent event) {
        if(log.isInfoEnabled()) log.info "Executando evento FotoSubidaEvent..."
        
        Foto foto = Foto.get(event.source.id)
        if(foto){
            // processa a imagem da foto
        }
        
    }

Assim que este método for executado pela aplicação, o cache será limpado (jogado fora), pois este método tem a anotação @CacheFlush("recentesCache"). Veja que usei o mesmo nome "recentesCache" para indicar qual cache quero limpar.

Perceba também que este é aquele método "onApplicationEvent()", citado no item 1 acima, usado pelo Spring quando o framework detecta o lançamento do evento do tipo FotoSubidaEvent.

Pronto. Da próxima vez que a action HomeController.recentes() for executada, o Springcache vai ver que o "recentesCache" não existe mais, aí vai executar a action, pegar seu resultado e colocar em cache novamente. E enquanto este cache existir, a action não é executada, e ao invés disso, o conteúdo do cache é enviado para o usuário.

Um pequeno detalhe deste plugin é que ele tem uma configuração padrão que indica que o cache deve existir por apenas 120 segundos. Como era muito pouco para o meu caso, então configurei para que o cache vivesse por 3600 segundos. Veja como:

springcache {
    defaults {
        eternal = false
        diskPersistent = false
        timeToLive= 86400
    }
    caches {
        recentesCache {
            timeToLive= 3600
        }
    }
}



Ficamos por aqui com este artigo que já tá longo. Ainda falta coisa pra falar, como resources plugin, cache-headers plugin, cached-resources plugin e zipped-resources plugin. Até a próxima.

Lembre-se que você é bem vindo para contribuir com esta aplicação de exemplo GPerform via o GitHub. Abcs a todos.

GPerform: uma aplicação de exemplo com PERFORMANCE na alma

Venho pensando num tema legal para escrever um post. Aí resolvi dar uma atualizada, ler um pouco, ver umas apresentações, ..... E uma coisa que tem me interessado muito ultimamente é o assunto performance. Como uso muito Grails, então o assunto seria Grails e Performance. Então vamos lá.

Depois de algumas leituras, acho que juntei diversas dicas, plugins, e técnicas que vão fazer uma aplicação grails voar em alta velocidade. Mas não queria apenas ficar na leitura, pois sabemos bem que a teoria é uma coisa, na prática.....

Então botei a mão na massa e resolvi criar uma nova aplicação que chamei de GPerform (Grails Performance). Criei uma aplicação (na verdade está sendo criada ainda) que é uma espécie de Twitter + Instagram (em menor escala, naturalmente). Você manda uma foto, com comentário. Você pode seguir pessoas, e pessoas podem te seguir. Você pode avaliar fotos como boa ou ruim. Tudo é público.

A aplicação é 0.1, ok? Mas está disponível no GitHub. Seja bem vindo para contribuir se quiser (estou dando meus primeiros passos com o Git, então paciência comigo....).

A primeira coisa a fazer foi criar o modelo de dados, e aí já começam as dicas para uma melhor performance da sua aplicação: Não usar Collections para as relações hasMany. E mais que isso: acabar com as relações diretas entre duas classes para os casos many-to-many e one-to-many. Pois é, parece estranho.  Mas tem fundamento.

Para esclarecer o fundamento, vou dar um pequeno exemplo. Imagina:

class ContaCorrente{
    
    String numero
    String agencia
    
    static hasMany = [transacoes: Transacao]
}


class Transacao{
    
    BigDecimal valor
    
    static belongsTo = [conta: ContaCorrente]
}


Imagine que você queira usar as facildades que o Grails dá para gerenciar esta relação one-to-many. Ou seja, você vai querer usar o método conta.addToTransacoes(t) que está disponível para você, claro.

Mas imagine quantas transações tem numa conta corrente al longo de um ano. Se você tiver 30 transações por mês, no ano serão 360. Ou seja, no final do ano, quando o seu sistema for adicionar mais uma transação na conta do usuário, ele irá buscar as 360 transações para montar a collection "conta.transacoes" e só depois irá adicionar a nova transação nesta collection, para aí gravar esta alteração na collection no banco de dados. Se inicialmente você queria fazer apenas um insert, você acaba fazendo um select que retorna 360 itens pra memória (que você não vai usar nenhum deles), um insert da nova transação, e um update da ContaCorrente (pois o GORM atualiza o campo version deste objeto pai).

É ou não é coisa demais, e é muito provável que a performance da aplicação será seriamente afetada?

Para resolver isso, Burt Beckwith sugere, em uma apresentação na infoQ, que não se use a relação one-to-many com o hasMany. Que se faça apenas o outro lado da relação, que é o many-to-one. Qual é a perda disso? Você não vai ter o método addToTransacoes(). Mas isso é fácil de resolver. E também não vai ter um atributo "transacoes" que te retorna todas as transacoes da sua conta (você provavelmente não iria usar nunca - nem deveria). (veja um post interessante sobre isso) Por exemplo:

class ContaCorrente{    

    String numero
    String agencia
    
    def buscaTransacoesRecentes(int qtde){
        Transacao.executeQuery('from Transacao t where t.conta = :c order by t.dateCreated desc', [c:this], [max:qtde])
    }

    def adicionaTransacao(BigDecimal valor){
        new Transacao(valor:valor, conta:this).save()
    }

}


class Transacao{
    
    BigDecimal valor
    Date dateCreated

    ContaCorrente conta

}

Pronto. Simples assim. Você agora conseguiu ter apenas o seu único insert que você queria. Sem selects desnecessários, e sem update desnecessário (não estou contando com o select da ContaCorrente, mas tbem não contei com ele ali em cima). Veja que eu criei métodos na classe ContaCorrente para adicionar uma nova transação e para buscar as mais recentes.

Acho que tá bom por hoje né? Terei que escrever outros posts para contar tudo. Tem ainda o uso de diversos plugins, como por exemplo: springcache, spring-events, resources, cache-headers, cached-resources, zipped-resources, e por aí vai. Já já eu volto.

Dicas e passo a passo para iniciar com o Amazon Web Services - AWS (em inglês)

Estou começando a fazer uns testes com os servidores da Amazon, o Amazon Web Services. Aí descobri umas dicas e passo a passo com boas informações:

http://bristle.com/Tips/CloudComputing.htm#aws

Abcs a todos.

Grails: problema com uso de herança nos Controllers

Ao fazer uma revisão na performance de uma aplicação Grails, acabei me deparando com um problema muito esquisito.

Nesta aplicação, há alguns controllers que possuíam alguns métodos comuns. Então, nada mais natural do que criar um controller pai, abstrato, do qual os demais controllers herdavam para poderem utilizar os métodos comuns. Não eram actions, eram métodos simples.

Percebi, então, que os alguns dos métodos do controller pai eram executados mesmo sem serem chamados. É isso mesmo. Não me perguntem como, mas estava acontecendo. Será que havia algum erro da minha aplicação. Pode ser que sim, mas....

Então criei uma action de teste simples no meu controller filho:

class FilhoController extends PaiController {
    def teste = {
     render "ola"
    }
}

Ou seja, nada deveria ser executado além de aparecer um "ola" na tela. Mas o problema é que ao configurar o Grails para fazer o log das queries SQL (no DataSource.groovy coloquei loggingSql = true), quando eu acessava esta action http://localhost:8080/app/filho/teste , um monte de SQL aparecia no console. Eram as queries que existiam nos métodos do PaiController. E aí? Fiquei horas, literalmente, tentando entender. Achei que fosse algo em algum Filter, algo no BootStrap, no resources.groovy, sei lá aonde. Mas não imaginei que era a herança que estava causando isso.

Bom, resolvi arriscar e acabei com a herança. Matei o "extends PaiController". Aí criei uma classe groovy comum contendo todos os métodos da antiga classe PaiController. Problema resolvido. Acredita nisso? Nem eu. Mas........

Só mais um detalhe: nesta classe groovy, que chamei de WebHelper, eu precisava de session, de um service, do request, da taglib para poder usar o método message(), e por aí vai. Então, criei esta classe com.minhaapp.web.WebHelper na pasta src/groovy, e configurei o resources.groovy para poder injetar nestas minha classe tudo que eu precisava, da seguinte forma:

beans = {
    
  webHelper(com.minhaapp.web.WebHelper) { bean ->
    bean.scope = 'prototype' // aqui pode ser singleton se vc preferir
    bean.autowire = 'byName'
    applicationTagLib = ref('org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib')
  }

}


E a classe WebHelper então ficou assim:

package com.minhaapp.web


import org.codehaus.groovy.grails.plugins.web.taglib.ApplicationTagLib
import org.springframework.web.servlet.support.RequestContextUtils
import org.springframework.web.context.request.RequestContextHolder

public class WebHelper {
    def grailsApplication
    def meuService
    def applicationTagLib
    
    def teste(){
        println grailsApplication.class
        println RequestContextHolder.currentRequestAttributes().session.class
        println applicationTagLib.class
        println meuService.class
    }
    
}


Abcs a todos.

Reportagens sobre Grails na revista Wide

Pessoal

a revista Wide deste mês tem duas reportagens boas sobre o Grails. Uma delas é comigo mesmo, onde falo a respeito do uso do Grails no caso do NoiteUniversitaria.com.br ( e comento um pouco sobre o caso do Manubia ).
A outra é uma entrevista com o Graeme Rocher, desenvolvedor líder do projeto Grails.

Interessante a revista. (não estou ganhando $$ com a revista não hein)....

Abcs a todos.

Grails e Roo: screencast de comparação ao gerar uma mesma aplicação

Olá pessoal,

venho dando uma olhada no Spring Roo. Esta ferramenta é bastante útil para quem desenvolve com Java. Ou seja, não apenas na plataforma Java, como o Grails, mas apenas com a linguagem Java. Se você tiver esta condição de ter que desenvolver com Java, vale a pena conhecer o Roo.

Quero fazer uma comparação entre o desenvolvimento com Grails e Roo. Não é uma comparação justa, pois o Grails é um framework é em Groovy, e outro é uma ferramenta de geração que produz uma aplicação 99% Java (usando frameworks conhecidos com Spring, Hibernate, Log4j, etc). Porque então estou comparando? No fundo quero fazer um benchmark de performance entre as duas aplicações. Mas até chegar lá, vou mostrar desde o início, apresentando a geração da aplicação através dos comandos de geração das duas ferramentas.

Para isso, então, escolhi uma aplicação bem simples, e o passo a passo a ser seguido tanto com Grails quanto com o Roo é:

- Criar projeto
- Configurar persistência com Hibernate e Hypersonic DB in Memory (só Roo precisa deste passo)
- Criar entidade Project
- Criar atributos com validação: name, beginDate, active
- Criar 2 finders com os atributos active e beginDate (só Roo precisa deste passo)
- Configurar Log (só Roo precisa deste passo)
- Configurar arquivos Eclipse (só Roo precisa deste passo)
- Gerar Controller e views
- Executar a aplicação.


Aí vão os vídeos.  Perdoem-me a pouca empolgação do vídeo 1, mas é que já era 1h da manhã e estava todo mundo dormindo lá em casa..... não podia fazer muito barulho pra não acordar minha filha   :-)
Abraços a todos.

Vídeo 1: Gerando com Roo



Vídeo 2: Gerando com Grails

Tudo sobre Grails GORM e as relações One-To-Many, Many-To-One e Many-To-Many

Esta é mais uma dica rápida. Você já se pegou com dúvidas de como funcionam as relações entre entidades no Grails? Como fazer o cascade corretamente? como se livrar daquela exception que não insiste em ser lançada?
Pois é. O grails possui o mapeamento Objeto Relacional baseado no Hibernate, mas cria uma série de facilidades nas entidades para podermos lidar de forma fácil com as relações entre objetos.
GORM significa Grails' Object Relational Mappging. E as relações a que me refiro são One-To-Many, Many-To-One, e Many-To-Many.
Há um excelente resumo escrito por Peter Ledbrook no blog da SpringSource.

http://blog.springsource.com/2010/07/02/gorm-gotchas-part-2/

Vale MUITO a pena a leitura. Este link acima é a Parte 2 de uma série sobre o GORM. Caso queira (vale a pena tbem) leia a Parte 1.

Abcs
Felipe