Apache Thrift Quickstart Tutorial

Thrift is a cross language RPC framework initially developed at Facebook, now open sourced as an Apache project. This post will describe how to write a thrift service and client in different modes such as blocking, non blocking and asynchronous.

(I felt latter two modes are less documented and needed some tutorial type introduction, hence the motivation of this post). To easily follow the tutorial it’s beneficial that you have a basic understanding of Thrift architecture consisting of Transports, Protocols and Processors. (A good paper can be found at [1]). Here I will be using Thrift version 0.7 and Thrift’s Java binding.

Thrift Installation
Installation instructions can be found at http://wiki.apache.org/thrift/ThriftInstallation.

To sum up Ubuntu installation steps.

1. Install required dependencies.
$ sudo apt-get install libboost-dev libboost-test-dev libboost-program-options-dev libevent-dev automake libtool flex bison pkg-config g++ libssl-dev
2. Go to the installation root directory.
3. $ ./configure
4. $ make
5. Become super user and
   $ make install

Now let’s get on with creating the service and consuming it.

Service Definition

Here a service with simple arithmetic operations is defined. Note the use of typedef directive to declare alternative names for base types i64 and i32. Add following in a file named ‘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),
}

Code will be generated under ‘tutorial.arithmetic.gen’ package.

Now generate Java code using following command line.

$ thrift –gen java arithmetic.thrift

The source tutorial.arithmetic.gen.ArithmeticService.java will be generated.

Blocking Mode

Let’s create a blocking mode server and a client to consume the service.

First we need to implement the service using generated service skeleton. The interface to implement is 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;
    }

}

Now that being done let’s create the Thrift server which would server request for this service. Remember this is a blocking server so the server threads doing I/O will wait.

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

}

Here TThreadPoolServer implementation is used which would utilize a thread pool to serve incoming requests.

Now let’s write the client.

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 is used for encoding data transferred between server and client. Now start the server and invoke the service using client to results.

Non Blocking Mode
Now lets create a non blocking server which uses Java non blocking I/O underneath. We can use the same service implementation as before (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();
    }
}

Here TNonblockingServerSocket is used which encapsulates a ServerSocketChannel.

Code for the non blocking client is as follows.

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

}

Note the usage of TFramedTransport wrapping normal TSocket transport. Non blocking server requires client to use TFramedTransport which would frame the data sent over the wire. Fire up the server and send a request using the client. You will see the same results as before, this time using non blocking mode.

Asynchronous Mode

We can write asynchronous clients to call a Thrift service. A callback needs to be registered which will get invoked at successful completion of the request. Blocking mode server didn’t work (method invocation returns with an empty response) with the asynchronous client (May be it’s because we are using TNonblockingSocket at the client side. See construction of ArithmeticService.AsyncClient. So this may be the proper behaviour). Non blocking mode server seems to work without an issue. So you can use the non blocking server from earlier with the client shown below.

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

    }

}

Two callbacks have been defined corresponding to each operation of the service. Note the usage of two client instances for the two invocations. Each invocation needs a separate client instance or otherwise client will fail with following exception

Exception in thread “main” java.lang.IllegalStateException: Client is currently executing another method: tutorial.arithmetic.gen.ArithmeticService$AsyncClient$add_call

So this wraps up my quick start on Thrift with different modes of operation. Hope somebody may find this useful. For any suggestions or corrections do not hesitate to comment.

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

Reference: Apache Thrift Quickstart Tutorial from our JCG partner Buddhika Chamith at the Source Open blog.

Do you want to know how to develop your skillset to become a Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

JPA Mini Book

Learn how to leverage the power of JPA in order to create robust and flexible Java applications. With this Mini Book, you will get introduced to JPA and smoothly transition to more advanced concepts.

JVM Troubleshooting Guide

The Java virtual machine is really the foundation of any Java EE platform. Learn how to master it with this advanced guide!

Given email address is already subscribed, thank you!
Oops. Something went wrong. Please try again later.
Please provide a valid email address.
Thank you, your sign-up request was successful! Please check your e-mail inbox.
Please complete the CAPTCHA.
Please fill in the required fields.

One Response to "Apache Thrift Quickstart Tutorial"

  1. serj_f says:

    hello

    I have a question: it is possible to have one server having behind 2 protocols (for example json and binary)?

    thanks

Leave a Reply


+ 7 = twelve



Java Code Geeks and all content copyright © 2010-2014, Exelixis Media Ltd | Terms of Use | Privacy Policy
All trademarks and registered trademarks appearing on Java Code Geeks are the property of their respective owners.
Java is a trademark or registered trademark of Oracle Corporation in the United States and other countries.
Java Code Geeks is not connected to Oracle Corporation and is not sponsored by Oracle Corporation.
Do you want to know how to develop your skillset and become a ...
Java Rockstar?

Subscribe to our newsletter to start Rocking right now!

To get you started we give you two of our best selling eBooks for FREE!

Get ready to Rock!
You can download the complementary eBooks using the links below:
Close