Getting Started with Apache Thrift

Overview

This article explains how to write Thrift services and clients in several modes, including blocking, non-blocking, and asynchronous execution. The examples use Apache Thrift and Java.

Prerequisites

Apache Thrift must be installed first. For installation instructions, see the Apache Thrift overview.

Developing with Apache Thrift

Defining a Service with a Thrift Script

After installation, create a Thrift IDL file. A single IDL file can generate code for multiple languages.

namespace java com.devkuma.thrift.tutorial

typedef i64 long
typedef i32 int

service ArithmeticService {
  long add(1:int num1, 2:int num2),
  long multiply(1:int num1, 2:int num2),
}

The namespace becomes the Java package structure, such as com.devkuma.thrift.tutorial. Thrift supports 32-bit and 64-bit integer types, and typedef can be used to rename them. A service can be considered similar to a class that exposes methods for server-to-server or server-to-client communication.

Generate Java code with the Thrift compiler.

thrift --gen <language> <Thrift filename>
% thrift -r --gen java arithmetic.thrift

If the command succeeds, ArithmeticService.java is generated under gen-java/com/devkuma/thrift/tutorial/.

Creating a Java Project

Create a Java project with Gradle and add the libraries needed for Thrift communication, such as TSocket and TTransport.

dependencies {
    implementation 'org.apache.thrift:libthrift:0.18.1'
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    implementation 'ch.qos.logback:logback-classic:1.4.7'
    implementation 'org.slf4j:slf4j-api:2.0.7'
}

Implementing the Service

Implement the generated skeleton interface, 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;
    }
}

Blocking Mode

In blocking mode, the server thread waits while I/O is being processed. The example uses TThreadPoolServer to process incoming requests with a thread pool.

TServerSocket serverTransport = new TServerSocket(7911);
ArithmeticService.Processor processor =
    new ArithmeticService.Processor(new ArithmeticServiceImpl());
TServer server = new TThreadPoolServer(
    new TThreadPoolServer.Args(serverTransport).processor(processor));
server.serve();

The client opens a socket, creates a protocol, and calls methods through ArithmeticService.Client.

TTransport 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);
long multiplyResult = client.multiply(20, 40);

When the server and client are run, the result should be similar to:

Add result: 300
Multiply result: 800

TBinaryProtocol encodes data exchanged between the server and client. Other available protocols include TCompactProtocol, TDebugProtocol, TDenseProtocol, and TJSONProtocol.

Non-blocking Mode

A non-blocking server uses TNonblockingServerSocket and TNonblockingServer. It can receive other requests while an earlier request is being handled.

TNonblockingServerTransport serverTransport =
    new TNonblockingServerSocket(7911);
ArithmeticService.Processor processor =
    new ArithmeticService.Processor(new ArithmeticServiceImpl());
TServer server = new TNonblockingServer(
    new TNonblockingServer.Args(serverTransport).processor(processor));
server.serve();

The client uses TFramedTransport wrapping a normal TSocket. A non-blocking server requires clients to use framed transport so the transmitted data can be structured correctly.

TTransport transport = new TFramedTransport(new TSocket("localhost", 7911));
TProtocol protocol = new TBinaryProtocol(transport);
ArithmeticService.Client client = new ArithmeticService.Client(protocol);

Running the non-blocking server and client produces the same arithmetic results as the blocking example.

Asynchronous Mode

In asynchronous mode, the client registers callbacks that are invoked when requests complete. The asynchronous client cannot communicate correctly with the blocking server. Use TNonblockingSocket and the generated ArithmeticService.AsyncClient.

ArithmeticService.AsyncClient client =
    new ArithmeticService.AsyncClient(
        new TBinaryProtocol.Factory(),
        new TAsyncClientManager(),
        new TNonblockingSocket("localhost", 7911));

client.add(200, 400, new AddMethodCallback());

Each concurrent asynchronous operation needs its own client instance. If the same client tries to execute another method while a request is still running, Thrift raises an IllegalStateException.

References