Integrando Apache Camel e Apache Thrift

Veja neste artigo um framework que tem sido amplamente utilizado para construção de serviços de alta performance por grandes empresas como Twitter e Facebook. Também vamos entender como utilizá-lo com o Apache Camel.

Nesse artigo vamos examinar um framework bem interessante, simples e de alta performance para a construção de serviços. Estamos falando do Apache Thrift, que é amplamente utilizado hoje no mundo apesar de muitos nem saberem que ele existe. Também faremos um casamento do Apache Thrift com o Apache Camel.

Em 2007, o Facebook publicou um trabalho descrevendo em detalhes o funcionamento de um mecanismo de troca de mensagens e construção de serviços ágil. Esse documento dissertava sobre uma espécie de RPC que tempos depois foi doado para a fundação Apache e hoje é conhecido por Apache Thrift.

O Apache Thrift é extremamente simples, mas sua arquitetura interna é bem organizada e ao mesmo tempo facilmente adaptável e isso tem chamado a atenção das grandes corporações tais como Twitter, Linkedin e Facebook já há alguns anos. Se você fizer uma pesquisa rápida no Google associando Thrift ao Twitter, por exemplo, verá que eles já investiram muito tempo e conhecimento na construção de muitas ferramentas extremamente interessantes.



Figura 1: Camadas do Thrift

Antes de começarmos, provavelmente você não tem as ferramentas necessárias para trabalhar com esse framework. Vá até o site http://thrift.apache.org e faça o download de acordo com a sua plataforma. Detalhes completos de como fazer a instalação está disponível em http://thrift.apache.org/docs/install/.

Antes de continuarmos é preciso entender que ele não é de forma alguma um padrão internacional mantido por alguma entidade regulamentadora tal qual Web Services, mas é bem útil e aplicável dentro de alguns contextos mais específicos. Tanto cliente quanto servidor trocam mensagens bem definidas através de um contrato razoavelmente rígido (Figura 1). Esse contrato é descrito em uma forma de IDL (Interface Definition Language) que é utilizado para gerar tanto código do cliente quanto do servidor.

O Thrift oferece um compilador/gerador para essa IDL e suporta as seguintes linguagens de programação: C++, Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, OCaml and Delphi e muito mais. Ora, isso quer dizer que podemos ter facilmente uma aplicação IPHONE (Cocoa/ Objective C) “conversando” com um servidor em Erlang ou Java, utilizando exatamente o mesmo contrato.

Veremos na Listagem 1 um trecho de exemplo dessa IDL e como ela se parece ao mesmo tempo com Java, C++ ou C.

Listagem 1: Exemplo de descritor Thrift

struct Work { 1: i32 num1 = 0, 2: i32 num2, 3: Operation op, 4: optional string comment, } exception InvalidOperation { 1: i32 what, 2: string why } service Calculator extends shared.SharedService { void ping(), i32 add(1:i32 num1, 2:i32 num2), i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch), oneway void zip() }

Na Listagem 1 temos estruturas que são agrupamentos de informação (struct), exceções (exception) representando erros, declaração de serviços (service) que inclusive podem estender operações de outros serviços. O Thrift suporta vários tipos pré-definidos como inteiros, string, longs, booleans etc. Mas o que fazer com esse arquivo? É preciso que ele seja interpretado, como vemos na Listagem 2.

Listagem 2: Linha de comando para chamada do Thrift

thrift –gen cocoa meuexemplo.thrift

Se a definição estiver correta, uma série de arquivos serão gerados. No nosso artigo, como trabalharemos com cliente e serviço em Java, utilizaremos o maven para gerenciar isso para nós (Listagem 3).

Listagem 3: trecho de projeto Maven.

<plugin> <groupId>org.apache.thrift.tools</groupId> <artifactId>maven-thrift-plugin</artifactId> <version>0.1.11</version> <configuration> <thriftExecutable>/usr/local/bin/thrift</thriftExecutable> </configuration> <executions> <execution> <id>thrift-sources</id> <phase>generate-sources</phase> <goals> <goal>compile</goal> </goals> </execution> <execution> <id>thrift-test-sources</id> <phase>generate-test-sources</phase> <goals> <goal>testCompile</goal> </goals> </execution> </executions> </plugin>

Reparem que estamos referenciando o caminho onde o executável do compilador do Thrift está instalado no ambiente, nesse caso “/usr/local/bin/thrift”. Isso pode variar de acordo com a sua plataforma. O nosso projeto maven se encarregará de gerar o código e adicioná-lo ao caminho de build do nosso projeto. Mas o que foi gerado? Vejamos a figura 2.


Figura 1: Camadas da arquitetura do Thrift

Para cada struct, service, exception e enum que declaramos na IDL é gerada uma classe Java correspondente. A mais importante é o Processor criado para cada serviço. Ele faz a ligação entre o protocolo e a nossa implementação de negócio que fica no “Server”. É o processor que sabe identificar, por exemplo, que método está sendo chamado e direcioná-lo corretamente para a implementação que está no Handler.

Implementar um serviço no Thrift significa implementar um Handler que é a realização da interface que está descrita por um service. Não entendeu? Vamos ver a listagem 4 com um exemplo banal de “start” do servidor e chamada do cliente.

Listagem 4: Exemplo de chamada do thrift

Fácil não é? Reparem que para criar um Processor, precisamos de um Handler e para iniciarmos um servidor, precisamos de um processor. O cliente faz uso dos componentes da API do thrift (transport, protocol) e do código gerado (Calculator.Cllient). E a implementação do Handler? Vejamos um trecho na Listagem 5.

Listagem 5: Handler para serviço

public class CalculatorHandler implements Calculator.Iface { ... public void ping() { System.out.println("ping()"); } public int add(int n1, int n2) { System.out.println("add(" + n1 + "," + n2 + ")"); return n1 + n2; } ... }

A essa altura você deve estar pensando se é possível utilizar o Thrift dentro de um servidor HTTP, por exemplo, como um um Tomcat e Jboss. A resposta é CLARO!. Como foi dito no início, o Thrift é facilmente adaptável a qualquer ambiente ou framework, graças à sua arquitetura. Nesse artigo, vamos explorar como seria utilizá-lo dentro do Apache Camel.

Já sabemos que o Camel funciona com um Contexto que controla uma ou mais rotas. Essas rotas são configuradas por uma instância de RouteBuilder e podem direcionar requisições para instâncias de Processor (se você viajou agora, pare de ler e veja mais sobre o Camel no site da DevMedia ou em http://camel.apache.org antes de continuar).

Para acoplarmos o Thrift ao Camel. Utilizamos uma rota que inicia um servidor HTTP em uma porta específica e para cada requisição recebida, direciona o exchange a um processor que utiliza a API do Thrift. Vamos ver a rota na Listagem 6.

Listagem 6: Rota do Camel para o Thrift

public class ThriftRouteBuilder extends RouteBuilder { @Autowired ThriftProcessor processor; @Override public void configure() throws Exception { from("jetty://http://localhost:8182/thrift").process(processor).end(); } }

Como sempre o Apache Camel nos surpreende por ser extremamente enxuto e direto ao ponto. O Processador do thrift está na Listagem 7.

Listagem 7: Trecho do Processador Camel para Thrift

private tutorial.Calculator.Processor<CalculatorHandler> processor; @Override public void process(Exchange exchange) throws Exception { HttpMessage httpMessage = exchange.getIn(HttpMessage.class); InputStream inputBody = (InputStream) exchange.getIn().getBody(); HttpServletRequest request = httpMessage.getRequest(); HttpServletResponse response = httpMessage.getResponse(); doPost(request, response, inputBody); } protected void doPost(HttpServletRequest request, HttpServletResponse response, InputStream is) throws ServletException, IOException { TTransport inTransport = null; TTransport outTransport = null; try { response.setContentType("application/x-thrift"); OutputStream out = response.getOutputStream(); TTransport transport = new TIOStreamTransport(is, out); inTransport = transport; outTransport = transport; TProtocol inProtocol = inProtocolFactory.getProtocol(inTransport); TProtocol outProtocol = outProtocolFactory .getProtocol(outTransport); processor.process(inProtocol, outProtocol); out.flush(); } catch (TException te) { throw new ServletException(te); } }

O que esse processador do Camel faz é simplesmente receber o Stream de bytes e repassá-lo utilizando através de um transporte e protocolo ao nosso processor Calculator.Processor. A resposta é simplesmente adicionada ao ServletResponse que é então controlado pelo Apache Camel. Esse código não está pronto para produção, mas demonstra facilmente como é fácil adicionar funcionalidades baseadas no Thrift.

Bom, é isso pessoal! Conseguimos passar rapidamente pelo Apache Thrift. Não se esqueçam de visitar o código fonte do projeto disponível em https://github.com/leogsilva/CamelThrift.git. Boa programação a todos e até a próxima.


Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados