Перейти к основному содержимому

Работа с Apache Thrift

· 6 мин. чтения

1. Обзор

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

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

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

Если вы хотите сразу перейти к примерам, переходите сразу к разделу 5.

2. Бережливость Апача

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

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

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

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

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

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

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

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

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

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

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

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

3.3. Структуры

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

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

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

  • list – упорядоченный список элементов
  • set — неупорядоченный набор уникальных элементов
  • map<type1,type2> — карта строго уникальных ключей к значениям

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

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

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

3.6. Услуги

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

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

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

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

  • С++
  • С#
  • Идти
  • Хаскелл
  • Ява
  • Javascript
  • Node.js
  • Перл
  • PHP
  • питон
  • Рубин

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

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

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

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

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

Обратите внимание на флаг -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.foreach.thrift.impl
namespace java com.foreach.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. Компиляция и генерация кода

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

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

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

  • Кроссплатформенный ресурс.java
  • Кроссплатформенная служба.java
  • Инвалидоператионексцептион.java

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

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

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

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

Хотя Thrift сделал за нас большую часть работы, нам все еще нужно написать собственные реализации CrossPlatformService . Для этого нам просто нужно реализовать интерфейс CrossPlatformService.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<CrossPlatformResource> getList()
throws InvalidOperationException, TException {
return Collections.emptyList();
}

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

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

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

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

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

  • 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 .