Did you know? DZone has great portals for Python, Cloud, NoSQL, and HTML5!
NoSQL Zone is brought to you in partnership with:

Buddhika is currently employeed as a software engineer. He received his BSc. in Computer Science and Engineering from University of Moratuwa. Being an open source enthusiast he has participated in two Google Summer of Code programs at Apache. His current interests include data mining, distributed computing and business process monitoring. Buddhika is a DZone MVB and is not an employee of DZone and has posted 6 posts at DZone. You can read more from them at their website. View Full User Profile

Apache Thrift Quickstart Tutorial

02.09.2012
Email
Views: 2754
  • submit to reddit
This article is part of the DZone NoSQL Resource Portal, which is brought to you in collaboration with Neo Technology and DataStax. Visit the NoSQL Resource Portal for additional tutorials, videos, opinions, and other resources on this topic.

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

Source:  http://chamibuddhika.wordpress.com/2011/10/02/apache-thrift-quickstart-tutorial/

 

 

Published at DZone with permission of Buddhika Chamith, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Neo Technology and DataStax are leading the charge for the NoSQL movement.  You can learn more about the Neo4j Graph Database in the project discussion forums and try out the new Spring Data Neo4j, which enables POJO-based development.  You can also see how Apache Cassandra, a ColumnFamily data store, is pushing the boundaries of persistence with cloud capabilities and deployments at SocialFlow and Netflix.