«1. Обзор

В этой статье мы узнаем, как разрабатывать кроссплатформенные клиент-серверные приложения с помощью фреймворка RPC под названием Apache Thrift.

Мы рассмотрим:

    Определение типов данных и интерфейсов служб с помощью IDL. Установка библиотеки и создание исходных кодов для разных языков. Реализация определенных интерфейсов на конкретном языке. Реализация программного обеспечения клиент/сервер. примеры, переходите сразу к разделу 5.

2. Apache Thrift

Apache Thrift изначально был разработан командой разработчиков Facebook и в настоящее время поддерживается Apache.

По сравнению с протокольными буферами, которые управляют кроссплатформенными процессами сериализации/десериализации объектов, Thrift в основном фокусируется на коммуникационном уровне между компонентами вашей системы.

Thrift использует специальный язык описания интерфейса (IDL) для определения типов данных и сервисных интерфейсов, которые хранятся в виде файлов .thrift и используются позже в качестве входных данных компилятором для создания исходного кода клиентского и серверного программного обеспечения, которые взаимодействуют с помощью различных программ. языки.

Чтобы использовать Apache Thrift в своем проекте, добавьте эту зависимость Maven:

Вы можете найти последнюю версию в репозитории Maven.

<dependency>
    <groupId>org.apache.thrift</groupId>
    <artifactId>libthrift</artifactId>
    <version>0.10.0</version>
</dependency>

3. Язык описания интерфейсов

Как уже было сказано, IDL позволяет определять коммуникационные интерфейсы на нейтральном языке. Ниже вы найдете поддерживаемые в настоящее время типы.

3.1. Базовые типы

bool — логическое значение (истина или ложь) byte — 8-разрядное целое число со знаком i16 — 16-разрядное целое число со знаком i32 — 32-разрядное целое число со знаком i64 — « 64-битное целое число со знаком double — 64-битная строка чисел с плавающей запятой — текстовая строка, закодированная с использованием кодировки UTF-8

    3.2. Специальные типы

двоичный — последовательность незакодированных байтов необязательный — необязательный тип Java 8

    3.3. Структуры

Структуры Thrift эквивалентны классам в языках ООП, но без наследования. Структура имеет набор строго типизированных полей, каждое из которых имеет уникальное имя в качестве идентификатора. Поля могут иметь различные аннотации (числовые идентификаторы полей, необязательные значения по умолчанию и т. д.).

3.4. Контейнеры

Контейнеры Thrift — это строго типизированные контейнеры:

list — упорядоченный список набора элементов — неупорядоченный набор уникальных элементов map\u003ctype1,type2\u003e — карта строго уникальных ключей к значениям

    Элементы контейнера могут быть любого допустимого типа Thrift.

3.5. Исключения

Исключения функционально эквивалентны структурам, за исключением того, что они наследуют собственные исключения.

3.6. Службы

Службы на самом деле являются коммуникационными интерфейсами, определенными с использованием типов Thrift. Они состоят из набора именованных функций, каждая со списком параметров и типом возвращаемого значения.

4. Генерация исходного кода

4.1. Поддержка языков

Существует длинный список поддерживаемых в настоящее время языков:

C++ C# Go Haskell Java Javascript Node.js Perl PHP Python Ruby

    Полный список можно посмотреть здесь.

4.2. Использование исполняемого файла библиотеки

Просто загрузите последнюю версию, соберите и установите ее, если необходимо, и используйте следующий синтаксис:

В приведенных выше командах [LANGUAGE] является одним из поддерживаемых языков, а [FILENAME ] — это файл с определением IDL.

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

Обратите внимание на флаг -r. Он указывает Thrift рекурсивно генерировать код, как только он замечает включения в данный файл .thrift.

4.3. Использование плагина Maven

Добавьте плагин в ваш файл pom.xml:

После этого просто выполните следующую команду:

<plugin>
   <groupId>org.apache.thrift.tools</groupId>
   <artifactId>maven-thrift-plugin</artifactId>
   <version>0.1.11</version>
   <configuration>
      <thriftExecutable>path/to/thrift</thriftExecutable>
   </configuration>
   <executions>
      <execution>
         <id>thrift-sources</id>
         <phase>generate-sources</phase>
         <goals>
            <goal>compile</goal>
         </goals>
      </execution>
   </executions>
</plugin>

Обратите внимание, что этот плагин больше не будет поддерживаться. Пожалуйста, посетите эту страницу для получения дополнительной информации.

mvn clean install

5. Пример клиент-серверного приложения

5.1. Определение файла Thrift

Давайте напишем простой сервис с исключениями и структурами:

Как видите, синтаксис довольно прост и не требует пояснений. Мы определяем набор пространств имен (для каждого языка реализации), тип исключения, структуру и, наконец, интерфейс службы, который будет общим для разных компонентов.

namespace cpp com.baeldung.thrift.impl
namespace java com.baeldung.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 <CrossPlatformResource> getList() throws (1:InvalidOperationException e),

    bool ping() throws (1:InvalidOperationException e)
}

Затем просто сохраните его как файл service.thrift.

«5.2. Компиляция и генерация кода

Теперь пришло время запустить компилятор, который сгенерирует для нас код:

Как вы могли видеть, мы добавили специальный флаг -out, чтобы указать выходной каталог для сгенерированных файлов. Если вы не получили никаких ошибок, сгенерированный каталог будет содержать 3 файла:

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

CrossPlatformResource.java CrossPlatformService.java InvalidOperationException.java

    Давайте сгенерируем C++ версию сервиса, выполнив:

Теперь мы получить 2 разные действительные реализации (Java и C++) одного и того же интерфейса службы.

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

5.3. Добавление реализации службы

Хотя Thrift сделал большую часть работы за нас, нам все еще нужно написать собственные реализации CrossPlatformService. Для этого нам просто нужно реализовать интерфейс CrossPlatformService.Iface:

5.4. Написание сервера

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<CrossPlatformResource> getList() 
      throws InvalidOperationException, TException {
        return Collections.emptyList();
    }

    @Override
    public boolean ping() throws InvalidOperationException, TException {
        return true;
    }
}

Как мы уже говорили, мы хотим создать кроссплатформенное клиент-серверное приложение, поэтому для него нам нужен сервер. Отличительной особенностью Apache Thrift является то, что он имеет собственную структуру взаимодействия клиент-сервер, которая упрощает взаимодействие:

Прежде всего необходимо определить транспортный уровень с реализацией интерфейса TServerTransport (или абстрактного класса, если быть точнее). Поскольку мы говорим о сервере, нам нужно предоставить порт для прослушивания. Затем нам нужно определить экземпляр TServer и выбрать одну из доступных реализаций:

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

TSimpleServer — для простого сервера TThreadPoolServer — для многопоточного сервера TNonblockingServer — для неблокирующего многопоточного сервера

    И, наконец, предоставьте реализацию процессора для выбранного сервера, которая уже была сгенерирована для нас Thrift, т.е. класс CrossPlatofformService.Processor.

5.5. Написание клиента

А вот реализация клиента:

С точки зрения клиента действия очень похожи.

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

Прежде всего, определите транспорт и укажите его на экземпляр нашего сервера, затем выберите подходящий протокол. Единственное отличие состоит в том, что здесь мы инициализируем экземпляр клиента, который, опять же, уже был сгенерирован Thrift, то есть класс CrossPlatformService.Client.

Поскольку он основан на определениях файла .thrift, мы можем напрямую вызывать описанные там методы. В этом конкретном примере client.ping() сделает удаленный вызов серверу, который ответит true.

6. Заключение

В этой статье мы показали вам основные концепции и этапы работы с Apache Thrift, а также показали, как создать рабочий пример, использующий библиотеку Thrift.

Как обычно, все примеры всегда можно найти в репозитории GitHub.

«