Статьи

Краткое руководство по Apache Thrift

Thrift — это межъязыковая RPC-инфраструктура, изначально разработанная на Facebook, теперь открытая как проект Apache. В этом посте будет описано, как написать сервис и клиент в различных режимах, таких как блокирование, неблокирование и асинхронность.

(Я чувствовал, что последние два режима менее документированы и нуждаются в некотором введении типа учебника, отсюда и мотивация этого сообщения). Чтобы легко следовать руководству, полезно иметь базовое представление об архитектуре Thrift, состоящей из транспортов, протоколов и процессоров. (Хорошую статью можно найти в [1]). Здесь я буду использовать версию 0 Thrift и привязку Java Thrift.

Комиссионная установка
Инструкции по установке можно найти по адресу http://wiki.apache.org/thrift/ThriftInstallation .

Подводя итог этапам установки Ubuntu.

1. Установите необходимые зависимости.
$ sudo apt-get install libboost-dev libboost-test-dev libboost-опции-программы-dev libevent-dev automake libtool flex bison pkg-config g ++ libssl-dev
2. Перейдите в корневой каталог установки.
3. $ ./configure
4. $ make
5. Станьте супер-пользователем и
$ make install

Теперь давайте приступим к созданию сервиса и его использованию.

Определение сервиса

Здесь определяется сервис с простыми арифметическими операциями. Обратите внимание на использование директивы typedef для объявления альтернативных имен для базовых типов i64 и i32. Добавьте следующее в файл с именем ‘ arithmetic.thrift ‘.

1
2
3
4
5
6
7
8
namespace java tutorial.arithmetic.gen  // define namespace for java code
 
typedef i64 long
typedef i32 int
service ArithmeticService {  // defines simple arithmetic service
long add(1:int num1, 2:int num2),
long multiply(1:int num1, 2:int num2),
}

Код будет сгенерирован в пакете tutorial.arithmetic.gen .

Теперь сгенерируйте Java-код, используя следующую командную строку.

$ thrift –gen java arithmetic.thrift

Будет создан исходный файл tutorial.arithmetic.gen.ArithmeticService.java .

Режим блокировки

Давайте создадим сервер режима блокировки и клиент для использования сервиса.

Сначала нам нужно реализовать сервис с использованием сгенерированного каркаса сервиса. Интерфейс для реализации — ArithmeticService.Iface.

01
02
03
04
05
06
07
08
09
10
11
public class ArithmeticServiceImpl implements ArithmeticService.Iface {
 
    public long add(int num1, int num2) throws TException {
        return num1 + num2;
    }
 
    public long multiply(int num1, int num2) throws TException {
        return num1 * num2;
    }
 
}

Теперь, когда это сделано, давайте создадим сервер Thrift, который будет запрашивать сервер для этого сервиса. Помните, что это блокирующий сервер, поэтому потоки сервера, выполняющие ввод / вывод, будут ждать.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Server {
 
    private void start() {
        try {
            TServerSocket serverTransport = new TServerSocket(7911);
 
            ArithmeticService.Processor processor = new ArithmeticService.Processor(new ArithmeticServiceImpl());
 
            TServer server = new TThreadPoolServer(new TThreadPoolServer.Args(serverTransport).
                    processor(processor));
            System.out.println("Starting server on port 7911 ...");
            server.serve();
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        Server srv = new Server();
        srv.start();
    }
 
}

Здесь используется реализация TThreadPoolServer, которая будет использовать пул потоков для обслуживания входящих запросов.

Теперь давайте напишем клиент.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class ArithmeticClient {
 
    private void invoke() {
        TTransport transport;
        try {
            transport = new TSocket("localhost", 7911);
 
            TProtocol protocol = new TBinaryProtocol(transport);
 
            ArithmeticService.Client client = new ArithmeticService.Client(protocol);
            transport.open();
 
            long addResult = client.add(100, 200);
            System.out.println("Add result: " + addResult);
            long multiplyResult = client.multiply(20, 40);
            System.out.println("Multiply result: " + multiplyResult);
 
            transport.close();
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        ArithmeticClient c = new ArithmeticClient();
        c.invoke();
 
    }
}

TBinaryProtocol используется для кодирования данных, передаваемых между сервером и клиентом. Теперь запустите сервер и вызовите сервис, используя клиент для результатов.

Неблокирующий режим
Теперь давайте создадим неблокирующий сервер, который использует Java неблокирующий ввод / вывод. Мы можем использовать ту же реализацию сервиса, что и раньше (ArithmeticServiceImpl).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
public class NonblockingServer {
 
    private void start() {
        try {
            TNonblockingServerTransport serverTransport = new TNonblockingServerSocket(7911);
            ArithmeticService.Processor processor = new ArithmeticService.Processor(new ArithmeticServiceImpl());
 
            TServer server = new TNonblockingServer(new TNonblockingServer.Args(serverTransport).
                    processor(processor));
            System.out.println("Starting server on port 7911 ...");
            server.serve();
        } catch (TTransportException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        NonblockingServer srv = new NonblockingServer();
        srv.start();
    }
}

Здесь используется TNonblockingServerSocket, который инкапсулирует ServerSocketChannel.

Код для неблокирующего клиента выглядит следующим образом.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class NonblockingClient {
 
    private void invoke() {
        TTransport transport;
        try {
            transport = new TFramedTransport(new TSocket("localhost", 7911));
            TProtocol protocol = new TBinaryProtocol(transport);
 
            ArithmeticService.Client client = new ArithmeticService.Client(protocol);
            transport.open();
 
            long addResult = client.add(100, 200);
            System.out.println("Add result: " + addResult);
            long multiplyResult = client.multiply(20, 40);
            System.out.println("Multiply result: " + multiplyResult);
 
            transport.close();
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        NonblockingClient c = new NonblockingClient();
        c.invoke();
    }
 
}

Обратите внимание на использование TFramedTransport для переноса обычного транспорта TSocket. Неблокирующий сервер требует, чтобы клиент использовал TFramedTransport, который бы формировал данные, передаваемые по проводам. Запустите сервер и отправьте запрос с помощью клиента. Вы увидите те же результаты, что и раньше, на этот раз в режиме без блокировки.

Асинхронный режим

Мы можем написать асинхронные клиенты для вызова службы Thrift. Необходимо зарегистрировать обратный вызов, который будет вызван при успешном завершении запроса. Сервер в режиме блокировки не работает (вызов метода возвращается с пустым ответом) с асинхронным клиентом (возможно, это потому, что мы используем TNonblockingSocket на стороне клиента. См. Конструкцию ArithmeticService.AsyncClient . Так что это может быть правильным поведением). Сервер без блокировки режима, кажется, работает без проблем. Таким образом, вы можете использовать неблокирующий сервер с клиентом, показанным ниже.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public class AsyncClient {
 
    private void invoke() {
        try {
            ArithmeticService.AsyncClient client = new ArithmeticService.
                    AsyncClient(new TBinaryProtocol.Factory(), new TAsyncClientManager(),
                                new TNonblockingSocket("localhost", 7911));
 
            client.add(200, 400, new AddMethodCallback());
 
            client = new ArithmeticService.
                    AsyncClient(new TBinaryProtocol.Factory(), new TAsyncClientManager(),
                                new TNonblockingSocket("localhost", 7911));
            client.multiply(20, 50, new MultiplyMethodCallback());
 
        } catch (TTransportException e) {
            e.printStackTrace();
        } catch (TException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
 
    public static void main(String[] args) {
        AsyncClient c = new AsyncClient();
        c.invoke();
 
    }
 
    class AddMethodCallback
            implements AsyncMethodCallback<ArithmeticService.AsyncClient.add_call> {
 
        public void onComplete(ArithmeticService.AsyncClient.add_call add_call) {
            try {
                long result = add_call.getResult();
                System.out.println("Add from server: " + result);
            } catch (TException e) {
                e.printStackTrace();
            }
        }
 
        public void onError(Exception e) {
            System.out.println("Error : ");
            e.printStackTrace();
        }
 
    }
 
    class MultiplyMethodCallback
            implements AsyncMethodCallback<ArithmeticService.AsyncClient.multiply_call> {
 
        public void onComplete(ArithmeticService.AsyncClient.multiply_call multiply_call) {
            try {
                long result = multiply_call.getResult();
                System.out.println("Multiply from server: " + result);
            } catch (TException e) {
                e.printStackTrace();
            }
        }
 
        public void onError(Exception e) {
            System.out.println("Error : ");
            e.printStackTrace();
        }
 
    }
 
}

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

« Исключение в потоке« main »java.lang.IllegalStateException: клиент в настоящее время выполняет другой метод: tutorial.arithmetic.gen.ArithmeticService $ AsyncClient $ add_call »

Так что это завершает мой быстрый старт на Thrift с различными режимами работы. Надеюсь, кто-нибудь может найти это полезным. Любые предложения или исправления не стесняйтесь комментировать.

[1] http://thrift.apache.org/static/thrift-20070401.pdf

Ссылка: Краткое руководство по Apache Thrift от нашего партнера JCG