Tuesday, December 22, 2009

Using Apache Thrift for RPC over TCP socket

Apache thrift is a framework for cross language RPC service/client implementation. Thrift has a small IDL (interface definition language) and comes with an IDL compiler that generates code to be used to easily build RPC clients and servers that communicate seamlessly across programming languages.

First you need to download thrift and then install Cygwin (i used 1.7 version of Cygwin).

Since the thrift is distributed in source form so we first need to build the thrift.exe IDL compiler. Follow this document to generate the compiler exe on windows (mine is Windows 7 home premium x64).

Following is an example usage. We intend to write a C# service and a Java client that implement the following interface (hello.thrift):

namespace java hello
namespace csharp hello

enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}


struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op,
4: string comment,
}


exception InvalidOperation {
1: i32 what,
2: string why
}


service HelloService {

void ping(),

i32 add(1:i32 num1, 2:i32 num2),

i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),

string greeting(),

}



Compile the above thrift file with the thrift compiler to generate both java and c# code.



thrift.exe -r --gen java hello.thrift



thrift.exe –r –gen csharp hello.thrift



Following is the service (Program.cs):



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Thrift;
using Thrift.Transport;
using Thrift.Protocol;
using Thrift.Server;
using hello;

namespace thriftcsserv
{
class Program
{
static void Main(string[] args)
{
try
{
HelloServiceHandler handler = new HelloServiceHandler();
HelloService.Processor processor = new HelloService.Processor(handler);
TServerTransport transport = new TServerSocket(34568);
TServer server = new TSimpleServer(processor, transport);

// Use this for a multithreaded server
// server = new TThreadPoolServer(processor, serverTransport);

Console.WriteLine("Starting the server...");
server.Serve();
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}

public class HelloServiceHandler : HelloService.Iface
{
public void ping()
{ }
public int add(int num1, int num2)
{
return (num1 + num2);
}
public int calculate(int logid, Work w)
{
int val = 0;
switch (w.Op)
{
case Operation.ADD:
val = w.Num1 + w.Num2;
break;
case Operation.SUBTRACT:
val = w.Num1 - w.Num2;
break;
case Operation.MULTIPLY:
val = w.Num1 * w.Num2;
break;
case Operation.DIVIDE:
if (w.Num2 == 0)
{
InvalidOperation io = new InvalidOperation();
io.What = (int)w.Op.GetTypeCode();
io.Why = "Cannot divide by 0";
throw io;
}
val = w.Num1 / w.Num2;
break;
default:
InvalidOperation io1 = new InvalidOperation();
io1.What = (int)w.Op.GetTypeCode();
io1.Why = "Unknown operation";
throw io1;
}
return val;
}
public string greeting()
{
return "Hello world of RPC using thrift from c# service";
}
}
}
}


And the HelloClient.java is below:



package hello.client;

import hello.HelloService;
import hello.InvalidOperation;
import hello.Operation;
import hello.Work;

import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;
import org.apache.thrift.transport.TTransport;

public class HelloClient {
public static void main(String[] args) {
try {

TTransport transport = new TSocket("localhost", 34568);
TProtocol protocol = new TBinaryProtocol(transport);
HelloService.Client client = new HelloService.Client(protocol);

transport.open();

client.ping();
System.out.println("ping()");

String str = client.greeting();
System.out.println(str);

int sum = client.add(1, 1);
System.out.println("1+1=" + sum);

Work work = new Work();

work.op = Operation.DIVIDE;
work.num1 = 1;
work.num2 = 0;
try {
int quotient = client.calculate(1, work);
System.out.println("Whoa we can divide by 0");
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
}

work.op = Operation.SUBTRACT;
work.num1 = 15;
work.num2 = 10;
try {
int diff = client.calculate(1, work);
System.out.println("15-10=" + diff);
} catch (InvalidOperation io) {
System.out.println("Invalid operation: " + io.why);
}

transport.close();

} catch (TException x) {
x.printStackTrace();
}
}
}


Now run the service and then the client. The o/p is as follows:



ping()

Hello world of RPC using thrift from c# service


1+1=2


Invalid operation: Cannot divide by 0


15-10=5



Hope you find this useful. I did not find a direct C# service example but i could easily translate the Java server example to C#.

6 comments:

TVRamana said...

Hi Watsh Rajneesh

I am new to thrift,

I tried to generate thrift.exe file by installing software's on my windows xp machine by following document link,

I installed cygwin successfully,but for autoconf ,I found autoconf-2.63-setup.exe file and autoconf-2.60 linix version.

which one I need to install on my windows xp machine.I was bit confusing....

Could you please give some detail explanation in this.

Thanks in advance.

TVRamana said...

Finally I was installed all dependent softwares in windows for building thrift compiler.

When I run the "make" command(using cygwin)from compiler/cpp,I got following exception..

13 [main] sh 1580 C:\cygwin\bin\sh.exe: *** fatal error - couldn't allocate heap, Win32 error 487, base 0x900000, top 0x940000, reserve_size 258048, allocsize 262144, page_const 4096

Is it mandatory to have 4GB ram in windowsXP machine to generate hrift.exe?

rwatsh said...

You may want to checkout http://everything-for-windows.blogspot.com/2009/03/error-fatal-error-couldnt-allocate-heap.html link for more information on the error you are getting while running make.

rwatsh said...

Also check http://cygwin.com/faq/faq.using.html#faq.using.bloda this out. If you are using any of the software in the list then you may want to try uninstalling them and then try out the make process.

TVRamana said...

Thanks a lot Rajneesh,
Finally I generated thrift.exe file.The problem is "anti virus" software,once I uninstall the anti-virus,Make command executed successfully.

TVRamana said...

Hi Rajneesh,

Could you please provide me steps to run the Service and client ?

actually when I run the client with localhost,8080.service is not responding.

Popular micro services patterns

Here are some popular Microservice design patterns that a programmer should know: Service Registry  pattern provides a  central location  fo...