In this post I’ll outline how to setup and connect a thrift client in PHP. If you’d like to learn how to setup and run a thrift server please see my friend Mike Cvet’s post here on setting up a C++ thrift server.

You’re in a meeting and some Sr. Dev from the systems or backend team says: “Great! just connect to my service via thrift, we already handle all the db connections and most of the caching. You guys use Ruby right? oh..  PHP? that’s cool too, sweet! *looks over to the PM* We should be done in a week…”. Oh snap! A week? What is this thrift thing? And what is this Ruby thing all those young whipper snappers keep talking about!?”. (Yes this is the use case I wish to satisfy with my post)

Apache Thrift is a great RPC framework originally developed and released by Facebook (added to the Apache incubator back in 2008).  I’ve used thrift for the last few years as a simple / efficient way to exchange data across machines, applications, and languages.  A coworker and I thought it’d be cool to do another Thrift tutorial, because at the time of this post, the official tutorials are being developed.  Following the nature of thrift, we also thought it’d be cool (we are nerds) to split the tutorial across 2 different blogs, I will explain the client, he will explain the server.   As a quick warning, this tutorial is rather verbose, I like hearing myself talk… Feel free to skim through the code if you want to skip my life story.  Warning #2, I wrote this tutorial after drinking an entire bottle of wine, you have been warned.

Download Source from GitHub – Client and Server

Step 1: Build the .thrift file
One of the great things about thrift is that it simplifies the communication between a client and server to a simple .thrift file.  This file describes the data structures, and functions available to your remote service.  In this tutorial, we extend a simple calculator and do something (slightly) more chunky. Dealing with Matricies is a pain. Dealing with Matricies in PHP is an even bigger pain. Wouldn’t it be nice if we could handle these Matrix operations in something more powerful, say C++? My friend Mike drafted the interface of a MatrixCalculator, did all the dirty work of the hard math stuff and provided access to these functions via a convenient Thrift server. He was kind enough to whip up a simple thrift interface file describing his service.

calculator.thrift

namespace cpp calculator

typedef list<double> Vector

enum BinaryOperation
{
	ADDITION = 1,
	SUBTRACTION = 2,
	MULTIPLICATION = 3,
	DIVISION = 4,
	MODULUS = 5,
}

struct ArithmeticOperation
{
	1:BinaryOperation op,
	2:double lh_term,
	3:double rh_term,
}

exception ArithmeticException
{
	1:string msg,
	2:optional double x,
}

struct Matrix
{
	1:i64 rows,
	2:i64 cols,
	3:list<Vector> data,
}

exception MatrixException
{
	1:string msg,
}

service Calculator
{
	/* Note you can't overload functions */

	double calc (1:ArithmeticOperation op) throws (1:ArithmeticException ae),

	Matrix mult (1:Matrix A, 2:Matrix B) throws (1:MatrixException me),
	Matrix transpose (1:Matrix A) throws (1:MatrixException me),
}

Thrift files are great. They are like 100 pages of technical documentation boiled down to a handful of lines. No lies, high level (its still basically pseudo code), and its all you need. If the “spec” changes, you can just diff the new vs. old and know exactly what is new.

Step 2: Compile and get the goods
Let’s make sure you have thrift installed and ready to go. If this blow you up, I recommend getting and installing the latest build of thrift here.

thrift -version
Thrift version 0.2.0-exported

Now we simply use the thrift compiler to autogenerate the PHP code which allows us to access these defined objects, and call these described functions.

thrift --gen php calculator.thrift

This will create a folder “gen-php” which will have a subfolder “calculator” which is our service. Contained are 2 files: Calculator.php which defines the PHP Object that represents the Calculator remote service. calculator_types.php which defines all the other PHP Objects involved with this service. The autogenerated code also conveniently encapsulates the buffered socket read/writes jazz that I haven’t had to write since University, and offers simple functions to call out.

Step3: Include the language specific libraries
After installing thrift, you’ll need to include the language specific libraries to facilitate access to thrift and all its goodness. Whenever you untar or installed the thrift files to, look for the folder at ./lib/php/src/ which contains a ton of library files you will need. Too see what other languages you can compile to for your client, just poke around in that lib folder if you are interested. For this example, I have a folder on my desktop called thriftcalc (this is what you will get if you checkout my repo here). You will also need to mv or cp the autogenerated thrift files for this project (the calculator pack folder containing: Calculator.php and calculator_types.php) into the packages folder of these library files. Here’s a screenshot of my directorys structure for this project.

The files/folder highlighted was autogenerated and moved into the packages folder.

In this example, I’ve moved the source php library files into a folder called “thrift” in my php root application folder, and I’ve moved the auto generated files from the previous step into the packages folder. Note that the package folder “calculator” was autogenerated based off the file name of the thrift file, this will also be reflected in *thriftname*_types.php.

Step4: Create a client application
The previous step showed a file called coolcalc.php, this is the simple php application I created which uses the thrift calculator service. Let’s take a look (my annotations will now continue in the code comments):

coolcalc.php

<?php

/* ----------------------------------------------
 * Calculator client
 *
 * A very simple example of interaction with
 * a calculator server application whose actions
 * are facilitated by thrift.  Both the client
 * and server negotiate on the common interface
 * defined by calculator.thrift
 *
 *@author Ian Chan
 *@date May 10, 2010
 * ----------------------------------------------
 */

// Setup the path to the thrift library folder
$GLOBALS['THRIFT_ROOT'] = 'thrift';

// Load up all the thrift stuff
require_once $GLOBALS['THRIFT_ROOT'].'/Thrift.php';
require_once $GLOBALS['THRIFT_ROOT'].'/protocol/TBinaryProtocol.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TSocket.php';
require_once $GLOBALS['THRIFT_ROOT'].'/transport/TBufferedTransport.php';

// Load the package that we autogenerated for this tutorial
require_once $GLOBALS['THRIFT_ROOT'].'/packages/calculator/Calculator.php';

// Several things might go wrong
try {
	// Create a thrift connection (Boiler plate)
	$socket = new TSocket('localhost', '9090');
	$transport = new TBufferedTransport($socket);
	$protocol = new TBinaryProtocol($transport);

	// Create a calculator client
	$client = new CalculatorClient($protocol);

	// Open up the connection
	$transport->open();

	// First, lets do something simple
	// Create a simple arithmatic operation (99 / 3)
	$operation = new ArithmeticOperation();
	$operation->op = BinaryOperation::DIVISON;
	$operation->lh_term = 99;
	$operation->rh_term = 3;

	// Perform operation on the server
	$sum = $client->calc($operation);
	print_r($sum);

	// Next, let's create the Matrix:
	//  | 1 2 3 |
	//  | 4 5 6 |
	//  | 7 8 9 |
	$m1 = new Matrix();
	$m1->rows = 3;
	$m1->cols = 3;
	$m1->data = array(array(1,2,3), array(4,5,6), array(7,8,9));

	// Let's calculate its transpose, much to difficult in PHP!
	$m2 = $client->transpose($m1);
	echo "The Trasnpose of m1 is :\r\n";
	print_r($m2);

	// Next, Let's now multiply m with its transpose, again too hard for PHP
	$m3 = $client->mult($m1, $m2);
	echo "The product of m1 and m2 is :\r\n";
	print_r($m3);

	// And finally, we close the thrift connection
	$transport->close();

} catch (ArithmaticException $ae) {
	// performed an illegal operation, like 10/0
	echo "ArithmatixException: ".$ae->msg."\r\n";

} catch (MatrixException $mx) {
	// performed an illegal matrix operation
	echo "MatrixException: ".$mx->msg."\r\n";

} catch (TException $tx) {
	// a general thrift exception, like no such server
	echo "ThriftException: ".$tx->getMessage()."\r\n";
}
?>

And there you have it, a simple thrift client. I hope you think about thrift next time you need to interface between 2 applications, or require some kind of client->server model for your system. Thrift makes it very fast and convenient to share data across different programming languages and makes it easy/flexible to develop to a shared interface contract. Cheers!

Advertisements