Статьи

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

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

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

Комиссионная установка

Инструкции по установке можно найти по адресу  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’ .

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.

	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, который будет запрашивать сервер для этого сервиса. Помните, что это блокирующий сервер, поэтому потоки сервера, выполняющие ввод / вывод, будут ждать.

	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, которая будет использовать пул потоков для обслуживания входящих запросов.

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

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

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.

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

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 . Так что это может быть правильным поведением). Сервер без блокировки режима, кажется, работает без проблем. Таким образом, вы можете использовать неблокирующий сервер с клиентом, показанным ниже.

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

Источник:  http://chamibuddhika.wordpress.com/2011/10/02/apache-thrift-quickstart-tutorial/