Travailler avec Apache Thrift

Travailler avec Apache Thrift

1. Vue d'ensemble

Dans cet article, nous découvrirons comment développer des applications client-serveur multiplateformes à l'aide du framework RPC appeléApache Thrift.

Nous couvrirons:

  • Définition des types de données et des interfaces de service avec IDL

  • Installer la bibliothèque et générer les sources pour différentes langues

  • Implémentation des interfaces définies dans un langage particulier

  • Implémentation du logiciel client / serveur

Si vous voulez aller directement aux exemples, passez directement à la section 5.

2. Apache Thrift

Apache Thrift a été développé à l'origine par l'équipe de développement de Facebook et est actuellement maintenu par Apache.

Par rapport àProtocol Buffers, qui gèrent les processus de sérialisation / désérialisation d'objets multiplateformes,Thrift mainly focuses on the communication layer between components of your system.

Thrift utilise un langage de description d'interface (IDL) spécial pour définir les types de données et les interfaces de service qui sont stockés en tant que fichiers.thrift et utilisés plus tard comme entrée par le compilateur pour générer le code source des logiciels client et serveur qui communiquent sur différentes programmations langues.

Pour utiliser Apache Thrift dans votre projet, ajoutez cette dépendance Maven:


    org.apache.thrift
    libthrift
    0.10.0

Vous pouvez trouver la dernière version dans lesMaven repository.

3. Langage de description d'interface

Comme déjà décrit,IDL permet de définir des interfaces de communication dans un langage neutre. Vous trouverez ci-dessous les types actuellement pris en charge.

3.1. Types de base

  • bool - une valeur booléenne (vrai ou faux)

  • byte - un entier signé de 8 bits

  • i16 - un entier signé 16 bits

  • i32 - un entier signé de 32 bits

  • i64 - un entier signé 64 bits

  • double - un nombre à virgule flottante de 64 bits

  • string - une chaîne de texte encodée à l'aide de l'encodage UTF-8

3.2. Types spéciaux

  • binary - une séquence d'octets non codés

  • optional - un typeOptional de Java 8

3.3. Structs

Les Thriftstructs sont l'équivalent des classes en langage POO mais sans héritage. Unstruct a un ensemble de champs fortement typés, chacun avec un nom unique comme identifiant. Les champs peuvent avoir diverses annotations (ID de champs numériques, valeurs par défaut facultatives, etc.).

3.4. Les conteneurs

Les conteneurs d'épargne sont des conteneurs fortement typés:

  • list - une liste ordonnée d'éléments

  • set - un ensemble non ordonné d'éléments uniques

  • map<type1,type2> - une carte de clés strictement uniques aux valeurs

Les éléments de conteneur peuvent être de n'importe quel type Thrift valide.

3.5. Exceptions

Les exceptions sont fonctionnellement équivalentes àstructs, sauf qu'elles héritent des exceptions natives.

3.6. Prestations de service

Les services sont en réalité des interfaces de communication définies à l'aide de types Thrift. Ils consistent en un ensemble de fonctions nommées, chacune avec une liste de paramètres et un type de retour.

4. Génération de code source

4.1. Support linguistique

Il existe une longue liste de langues actuellement prises en charge:

  • C++

  • C#

  • Go

  • Haskell

  • Java

  • Javascript

  • Node.js

  • Perl

  • PHP

  • Python

  • Ruby

Vous pouvez consulter la liste complètehere.

4.2. Utilisation du fichier exécutable de la bibliothèque

Téléchargez simplement leslatest version, compilez-les et installez-les si nécessaire, et utilisez la syntaxe suivante:

cd path/to/thrift
thrift -r --gen [LANGUAGE] [FILENAME]

Dans les commandes définies ci-dessus,[LANGUAGE] est l'une des langues prises en charge et[FILENAME] est un fichier avec une définition IDL.

Notez l'indicateur-r. Il dit à Thrift de générer du code de manière récursive une fois qu'il a remarqué des inclus dans un fichier.thrift donné.

4.3. Utilisation du plugin Maven

Ajoutez le plugin dans votre fichierpom.xml:


   org.apache.thrift.tools
   maven-thrift-plugin
   0.1.11
   
      path/to/thrift
   
   
      
         thrift-sources
         generate-sources
         
            compile
         
      
   

Après cela, exécutez la commande suivante:

mvn clean install

Notez que ce plugin n'aura plus de maintenance. Veuillez visiterthis page pour plus d'informations.

5. Exemple d'application client-serveur

5.1. Définition d'un fichier d'épargne

Écrivons un service simple avec des exceptions et des structures:

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)
}

Comme vous pouvez le constater, la syntaxe est assez simple et s’explique d'elle-même. Nous définissons un ensemble d'espaces de nom (par langage d'implémentation), un type d'exception, une structure et enfin une interface de service qui seront partagés entre différents composants.

Ensuite, stockez-le simplement sous forme de fichierservice.thrift.

5.2. Compiler et générer un code

Il est maintenant temps d’exécuter un compilateur qui générera le code pour nous:

thrift -r -out generated --gen java /path/to/service.thrift

Comme vous pouvez le voir, nous avons ajouté un indicateur spécial-out pour spécifier le répertoire de sortie des fichiers générés. Si vous n’obtenez aucune erreur, le répertoiregenerated contiendra 3 fichiers:

  • CrossPlatformResource.java

  • CrossPlatformService.java

  • InvalidOperationException.java

Générons une version C ++ du service en exécutant:

thrift -r -out generated --gen cpp /path/to/service.thrift

Nous avons maintenant 2 implémentations valides différentes (Java et C ++) de la même interface de service.

5.3. Ajout d'une implémentation de service

Bien que Thrift ait fait l'essentiel du travail pour nous, nous devons encore écrire nos propres implémentations desCrossPlatformService. Pour ce faire, il suffit d'implémenter une 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. Ecrire un serveur

Comme nous l’avons dit, nous souhaitons créer une application client-serveur multiplate-forme. Nous avons donc besoin d’un serveur. Le grand avantage d'Apache Thrift est qu'il dispose de son propre cadre de communication client-serveur, qui rend la communication un jeu d'enfant:

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.");
        }
    }
}

La première chose à faire est de définir une couche de transport avec l'implémentation de l'interfaceTServerTransport (ou classe abstraite, pour être plus précis). Puisque nous parlons de serveur, nous devons fournir un port pour écouter. Ensuite, nous devons définir une instance deTServer et choisir l'une des implémentations disponibles:

  • TSimpleServer - pour un serveur simple

  • TThreadPoolServer - pour serveur multi-thread

  • TNonblockingServer - pour un serveur multi-thread non bloquant

Et enfin, fournissez une implémentation de processeur pour le serveur choisi qui a déjà été générée pour nous par Thrift, c.-à-d. ClasseCrossPlatofformService.Processor.

5.5. Ecrire un client

Et voici la mise en œuvre du client:

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();

Du point de vue du client, les actions sont assez similaires.

Tout d’abord, définissez le transport et pointez-le sur notre instance de serveur, puis choisissez le protocole approprié. La seule différence est que nous initialisons ici l’instance client qui, une fois encore, était déjà générée par Thrift, c’est-à-dire ClasseCrossPlatformService.Client.

Comme il est basé sur les définitions de fichier.thrift, nous pouvons appeler directement les méthodes qui y sont décrites. Dans cet exemple particulier,client.ping() fera un appel distant au serveur qui répondra avectrue.

6. Conclusion

Dans cet article, nous vous avons montré les concepts de base et les étapes d'utilisation d'Apache Thrift, et nous avons montré comment créer un exemple fonctionnel utilisant la bibliothèque Thrift.

Comme d'habitude, tous les exemples se trouvent toujours dansthe GitHub repository.