Codemountain, Paulo Suzart's Blog

Twitter Finagle, primeiras impressões

leave a comment »

Quanto tempo sem postar sobre Scala! Mas isso não significa que fiquei sem estudar a linguagem e muito do que gira ao seu redor. Neste Post, vou colocar alguma scoisas sobre o Twitter Finagle, auto entitulado como uma biblioteca para construção de servidores e clientes RPC assíncronos em Java, Scala ou qualquer coisa que execute na JVM.

O finagle é uma camada muuuuuito fina em cima do JBoss Netty, que é um framework Java NIO realmente surpreendente e sabidamente veloz. O Netty é a base para o Aleph (clojure), framework que postei outro dia.

Aqui vai um hello world tosco com o Finagle:

class SimpleService extends Service[HttpRequest, HttpResponse] {
  def apply(request: HttpRequest): Future[HttpResponse] = {
    val response = new DefaultHttpResponse(HTTP_1_1, Ok)
    Future.value(response)
  }
}

A coisa é simples. Service é uma classe do finagle que extende uma função que recebe um HttpRequest do netty e retorna um Future[HttpResponse]. Future é uma estrutura para programação assíncrona fantástica, mas talvez seja assunto para outro post. No exemplo acima, a única coisa que o servidor faz é responder um 200 para o cliente.

E sim, se eu quiser saber os parâmetros de uma query string ou mesmo de um formulário submetido, teria que fazer tudo isso manualmente. Para isso o finagle traz uma pequena abstração em cima do HttpRequest do Netty, é o Request. Nela é possível encontrar funções mais civilizadas e algumas outras facilidades como gerar o response a partir da requisição, aproveitando então seu encoding, versão http, etc. Uma versão mais interessante usando algumas classes providas no finagle é a seguinte:

class AdvancedService extends Service[Request, Response] {
  def apply(request: Request): Future[Response] = {
    (Path(request.path)) match {
      case Root / "user" / Integer(id) =>
        val response = request.response
        response.setContentString("The user id is %d\n\n" format id)
        Future.value(response)
    }
  }
}

Aqui aparecem algumas coisas interessantes. A classe Path permite extrair os conhecidos Path Parameters em um pattern match, que no caso pega o caminho imediatamente após /user. Se requisitarmos /user/21, a resposta do servidor apresentará o identificador repreentado no path. O pacote http.path do finagle possui outras facilidades para parâmetros em query string e métodos Http. Acabei usando em um pequeno trabalho pessoal para facilitar as coisas.

Mas espera! Tem muito código aqui. Então por que não reduzir essa coisa toda? Eu queria chegar ao ponto de apenas entregar uma função parcialmente aplicada que fizesse um match na requisição para saber seu HTTP VERB. Se for o que desejo tratar, então faço um pattern mach nos seus parâmetros para seguir com o processamento. Pode ficar assim:


val superAd = ~~ {
  case (Get, request) =>
    (Path(request.path)) match {
       case Root / "user" / Integer(id) =>
            val response = request.response
            response.setContentString("The user id is %d\n\n" format id)
            Future.value(response)
    }
}

Wow! Bem melhor.E fiz isso com uma minuscula abstração que é a seguinte:

  val methNotAllwd: PartialFunction[(HttpMethod, Request), Future[Response]] = {
    case (_, request) =>
      val response = request.response
      response.status = MethodNotAllowed
      Future.value(response)
  }

  def ~~(body: PartialFunction[(HttpMethod, Request), Future[Response]]) = {
    new Service[Request, Response] {
      def apply(request: Request): Future[Response] = {

        val pf: PartialFunction[(HttpMethod, Request), Future[Response]] = {
          body orElse methNotAllwd
        }
        pf(request.method, request)
      }
    }

  }

Além de poder usar a função ~~ para tratar minhas requisições, caso o método da requisição não tenha um pattern definido, será retornado um 405 para o cliente. Prefiro abstrações pequenas à mega abstrações que custumávamos fazer há uns 8 anos atrás. Tinhamos quase que um framework caseiro em cima de qualquer outro framework que usávamos.

Para executar um servidor http com o superAd atendendo as requisições, use o codec RitchHttp ao invés do Http puro.

Aqui falei muito rapidamente da parte Http para o lado servidor do Finagle, mas existe uma gama de features muito legais. Uma delas é a possibilidade de encadear filtros na requisição, semelhante aos filtros Servlet. No código de exemplo deste post, você pode ver que usei o ExceptionFilter que já vem no finagle. Ele vai tratar qualquer exceção no seu código.

Como estou usando o Finagle?

Bem, construí um proxy SQS para um projeto de um amigo. Ele precisa receber mensagens mas não pode tornar as suas filas públicas. Além disso, é preciso validar a mensagem e autenticar o usuário antes de permitir que a mensagem vá para a fila.

Por que o Finagle?

Eu realmente acredito que precisamos reaproveitar coisas boas. O finagle oferece uma camada fina sobre o netty, e por isto não precisamos de um container WEB ou Servido de aplicação JEE, etc. Todo o seu código vai rodar com o mínimo de camadas e num nível baixo de abstração, o que tende a ser mais performático. Além de tornar o netty com uma cara de Scala.

Outro ponto importante é que a construção de clients é tão simples quanto a criação de services. Além de poder em alguns casos usar outros protocolos (Thrift, TCP puro com codecs) e manter a mesma simplicidade.

O finagle – e quase todos os projetos open source do Twitter – pode fazer sua vida ficar mais fácil. Outro projeto que uso neste mesmo caso é Logging. Simples, direto e com cara de Scala.

O código do post pode ser baixado aqui no meu github.

Written by paulosuzart

outubro 14, 2011 às 5:04 pm

Publicado em scala

Tagged with ,

Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

%d blogueiros gostam disto: