Trabalhando com o Apache Thrift
1. Visão geral
Neste artigo, descobriremos como desenvolver aplicativos cliente-servidor de plataforma cruzada com a ajuda da estrutura RPC chamadaApache Thrift.
Nós cobriremos:
-
Definindo tipos de dados e interfaces de serviço com IDL
-
Instalando a Biblioteca e Gerando as Fontes para Diferentes Idiomas
-
Implementando as interfaces definidas em um idioma específico
-
Implementando o software cliente / servidor
Se você quiser ir direto aos exemplos, vá direto para a seção 5.
2. Apache Thrift
O Apache Thrift foi desenvolvido originalmente pela equipe de desenvolvimento do Facebook e atualmente é mantido pelo Apache.
Em comparação comProtocol Buffers, que gerencia processos de serialização / desserialização de objeto de plataforma cruzada,Thrift mainly focuses on the communication layer between components of your system.
Thrift usa uma linguagem de descrição de interface (IDL) especial para definir tipos de dados e interfaces de serviço que são armazenados como arquivos.thrift e usados posteriormente como entrada pelo compilador para gerar o código-fonte do software cliente e servidor que se comunicam por meio de programação diferente línguas.
Para usar o Apache Thrift em seu projeto, adicione esta dependência do Maven:
org.apache.thrift
libthrift
0.10.0
Você pode encontrar a versão mais recente emMaven repository.
3. Linguagem de descrição da interface
Como já descrito,IDL permite definir interfaces de comunicação em linguagem neutra. Abaixo, você encontrará os tipos atualmente suportados.
3.1. Tipos de base
-
bool - um valor booleano (verdadeiro ou falso)
-
byte - um inteiro assinado de 8 bits
-
i16 - um inteiro assinado de 16 bits
-
i32 - um inteiro assinado de 32 bits
-
i64 - um inteiro assinado de 64 bits
-
double - um número de ponto flutuante de 64 bits
-
string - uma string de texto codificada usando a codificação UTF-8
3.2. Tipos Especiais
-
binary - uma sequência de bytes não codificados
-
optional - um tipoOptional de Java 8
3.3. Estruturas
Thriftstructs são equivalentes a classes em linguagens OOP, mas sem herança. Umstruct tem um conjunto de campos fortemente tipados, cada um com um nome único como identificador. Os campos podem ter várias anotações (IDs numéricos do campo, valores padrão opcionais, etc.).
3.4. Containers
Os contêineres de economia são contêineres fortemente tipados:
-
list - uma lista ordenada de elementos
-
set - um conjunto não ordenado de elementos únicos
-
map<type1,type2> - um mapa de chaves estritamente exclusivas para valores
Os elementos do contêiner podem ser de qualquer tipo Thrift válido.
3.5. Exceções
As exceções são funcionalmente equivalentes astructs, exceto pelo fato de serem herdadas das exceções nativas.
3.6. Serviços
Os serviços são, na verdade, interfaces de comunicação definidas usando os tipos Thrift. Eles consistem em um conjunto de funções nomeadas, cada uma com uma lista de parâmetros e um tipo de retorno.
4. Geração de código fonte
4.1. Suporte de linguas
Há uma longa lista de idiomas suportados atualmente:
-
C++
-
C#
-
Go
-
Haskell
-
Java
-
Javascript
-
Node.js
-
Perl
-
PHP
-
Pitão
-
Ruby
Você pode verificar a lista completahere.
4.2. Usando o arquivo executável da biblioteca
Basta baixar olatest version, compilar e instalar se necessário e usar a seguinte sintaxe:
cd path/to/thrift
thrift -r --gen [LANGUAGE] [FILENAME]
No conjunto de comandos acima,[LANGUAGE] é um dos idiomas suportados e[FILENAME] é um arquivo com definição IDL.
Observe o sinalizador-r. Ele diz ao Thrift para gerar código recursivamente assim que perceber a inclusão em um determinado arquivo.thrift.
4.3. Usando o plugin Maven
Adicione o plugin em seu arquivopom.xml:
org.apache.thrift.tools
maven-thrift-plugin
0.1.11
path/to/thrift
thrift-sources
generate-sources
compile
Depois disso, basta executar o seguinte comando:
mvn clean install
Observe que este plugin não terá mais manutenção. Visitethis page para mais informações.
5. Exemplo de um aplicativo cliente-servidor
5.1. Definindo Arquivo Thrift
Vamos escrever alguns serviços simples com exceções e estruturas:
namespace cpp com.example.thrift.impl
namespace java com.example.thrift.impl
exception InvalidOperationException {
1: i32 code,
2: string description
}
struct CrossPlatformResource {
1: i32 id,
2: string name,
3: optional string salutation
}
service CrossPlatformService {
CrossPlatformResource get(1:i32 id) throws (1:InvalidOperationException e),
void save(1:CrossPlatformResource resource) throws (1:InvalidOperationException e),
list getList() throws (1:InvalidOperationException e),
bool ping() throws (1:InvalidOperationException e)
}
Como você pode ver, a sintaxe é bastante simples e auto-explicativa. Definimos um conjunto de namespaces (por idioma de implementação), um tipo de exceção, uma estrutura e, finalmente, uma interface de serviço que será compartilhada entre diferentes componentes.
Em seguida, basta armazená-lo como um arquivoservice.thrift.
5.2. Compilando e gerando um código
Agora é hora de executar um compilador que irá gerar o código para nós:
thrift -r -out generated --gen java /path/to/service.thrift
Como você pode ver, adicionamos um sinalizador especial-out para especificar o diretório de saída dos arquivos gerados. Se você não obteve nenhum erro, o diretóriogenerated conterá 3 arquivos:
-
CrossPlatformResource.java
-
CrossPlatformService.java
-
InvalidOperationException.java
Vamos gerar uma versão C ++ do serviço executando:
thrift -r -out generated --gen cpp /path/to/service.thrift
Agora temos duas implementações válidas diferentes (Java e C ++) da mesma interface de serviço.
5.3. Adicionar uma implementação de serviço
Embora Thrift tenha feito a maior parte do trabalho para nós, ainda precisamos escrever nossas próprias implementações deCrossPlatformService. Para fazer isso, precisamos implementar uma interfaceCrossPlatformService.Iface:
public class CrossPlatformServiceImpl implements CrossPlatformService.Iface {
@Override
public CrossPlatformResource get(int id)
throws InvalidOperationException, TException {
return new CrossPlatformResource();
}
@Override
public void save(CrossPlatformResource resource)
throws InvalidOperationException, TException {
saveResource();
}
@Override
public List getList()
throws InvalidOperationException, TException {
return Collections.emptyList();
}
@Override
public boolean ping() throws InvalidOperationException, TException {
return true;
}
}
5.4. Escrevendo um servidor
Como dissemos, queremos criar um aplicativo cliente-servidor de plataforma cruzada, por isso precisamos de um servidor para ele. O melhor do Apache Thrift é que ele possui sua própria estrutura de comunicação cliente-servidor, o que torna a comunicação um pedaço de bolo:
public class CrossPlatformServiceServer {
public void start() throws TTransportException {
TServerTransport serverTransport = new TServerSocket(9090);
server = new TSimpleServer(new TServer.Args(serverTransport)
.processor(new CrossPlatformService.Processor<>(new CrossPlatformServiceImpl())));
System.out.print("Starting the server... ");
server.serve();
System.out.println("done.");
}
public void stop() {
if (server != null && server.isServing()) {
System.out.print("Stopping the server... ");
server.stop();
System.out.println("done.");
}
}
}
A primeira coisa é definir uma camada de transporte com a implementação da interfaceTServerTransport (ou classe abstrata, para ser mais preciso). Como estamos falando de servidor, precisamos fornecer uma porta para ouvir. Em seguida, precisamos definir uma instânciaTServer e escolher uma das implementações disponíveis:
-
TSimpleServer - para servidor simples
-
TThreadPoolServer - para servidor multi-thread
-
TNonblockingServer - para servidor multithread sem bloqueio
Por fim, forneça uma implementação de processador para o servidor escolhido que já foi gerado para nós pela Thrift, ou seja, CrossPlatofformService.Processor classe.
5.5. Escrevendo um cliente
E aqui está a implementação do cliente:
TTransport transport = new TSocket("localhost", 9090);
transport.open();
TProtocol protocol = new TBinaryProtocol(transport);
CrossPlatformService.Client client = new CrossPlatformService.Client(protocol);
boolean result = client.ping();
transport.close();
Da perspectiva do cliente, as ações são bastante semelhantes.
Primeiro, defina o transporte e aponte-o para a instância do servidor, depois escolha o protocolo adequado. A única diferença é que aqui inicializamos a instância do cliente que, mais uma vez, já foi gerada pelo Thrift, ou seja, CrossPlatformService.Client classe.
Como é baseado nas definições de arquivo.thrift, podemos chamar diretamente os métodos descritos lá. Neste exemplo particular,client.ping() fará uma chamada remota para o servidor que responderá comtrue.
6. Conclusão
Neste artigo, mostramos os conceitos básicos e as etapas para trabalhar com o Apache Thrift, e mostramos como criar um exemplo funcional que utiliza a biblioteca Thrift.
Como normalmente, todos os exemplos podem ser sempre encontrados emthe GitHub repository.