Thrift Tutorial

git-svn-id: https://svn.apache.org/repos/asf/incubator/thrift/trunk@665051 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Mark Slee 2007-03-07 05:45:10 +00:00
parent 02d1edc6f0
commit 07a3aab207
11 changed files with 705 additions and 0 deletions

26
tutorial/README Normal file
View File

@ -0,0 +1,26 @@
Thrift Tutorial
Author: Mark Slee (mcslee@facebook.com)
Thrift is distributed under the Thrift open source software license.
Please see the included LICENSE file.
Tutorial
========
1) First things first, you'll need to install the Thrift compiler and the
language libraries. Do that using the instructions in the top level
README file.
2) Read tutorial.thrift to learn about the syntax of a Thrift file
3) Run tutorial.thrift to compile the code:
./tutorial.thrift
4) Take a look at the generated code.
5) Look in the language directories for sample client/server code.
6) That's about it for now. This tutorial is intentionally brief. It should be
just enough to get you started and ready to build your own project.

View File

@ -0,0 +1,65 @@
#include <stdio.h>
#include <unistd.h>
#include <sys/time.h>
#include <protocol/TBinaryProtocol.h>
#include <transport/TSocket.h>
#include <transport/TTransportUtils.h>
#include "../gen-cpp/Calculator.h"
using namespace std;
using namespace facebook::thrift;
using namespace facebook::thrift::protocol;
using namespace facebook::thrift::transport;
using namespace tutorial;
using namespace shared;
using namespace boost;
int main(int argc, char** argv) {
shared_ptr<TTransport> socket(new TSocket("localhost", 9090));
shared_ptr<TTransport> transport(new TBufferedTransport(socket));
shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
CalculatorClient client(protocol);
try {
transport->open();
client.ping();
printf("ping()\n");
int32_t sum = client.add(1,1);
printf("1+1=%d\n", sum);
Work work;
work.op = DIVIDE;
work.num1 = 1;
work.num2 = 0;
try {
int32_t quotient = client.calculate(1, work);
printf("Whoa? We can divide by zero!\n");
} catch (InvalidOperation &io) {
printf("InvalidOperation: %s\n", io.why.c_str());
}
work.op = SUBTRACT;
work.num1 = 15;
work.num2 = 10;
int32_t diff = client.calculate(1, work);
printf("15-10=%d\n", diff);
// Note that C++ uses return by reference for complex types to avoid
// costly copy construction
SharedStruct ss;
client.getStruct(ss, 1);
printf("Check log: %s\n", ss.value.c_str());
transport->close();
} catch (TException &tx) {
printf("ERROR: %s\n", tx.what());
}
}

133
tutorial/cpp/CppServer.cpp Normal file
View File

@ -0,0 +1,133 @@
#include <concurrency/ThreadManager.h>
#include <concurrency/PosixThreadFactory.h>
#include <protocol/TBinaryProtocol.h>
#include <server/TSimpleServer.h>
#include <server/TThreadPoolServer.h>
#include <server/TThreadedServer.h>
#include <transport/TServerSocket.h>
#include <transport/TTransportUtils.h>
#include <iostream>
#include <stdexcept>
#include <sstream>
#include "../gen-cpp/Calculator.h"
using namespace std;
using namespace facebook::thrift;
using namespace facebook::thrift::protocol;
using namespace facebook::thrift::transport;
using namespace facebook::thrift::server;
using namespace tutorial;
using namespace shared;
using namespace boost;
class CalculatorHandler : public CalculatorIf {
public:
CalculatorHandler() {}
void ping() {
printf("ping()\n");
}
int32_t add(const int32_t n1, const int32_t n2) {
printf("add(%d,%d)\n", n1, n2);
return n1 + n2;
}
int32_t calculate(const int32_t logid, const Work &work) {
printf("calculate(%d,{%d,%d,%d})\n", logid, work.op, work.num1, work.num2);
int32_t val;
switch (work.op) {
case ADD:
val = work.num1 + work.num2;
break;
case SUBTRACT:
val = work.num1 - work.num2;
break;
case MULTIPLY:
val = work.num1 * work.num2;
break;
case DIVIDE:
if (work.num2 == 0) {
InvalidOperation io;
io.what = work.op;
io.why = "Cannot divide by 0";
throw io;
}
val = work.num1 / work.num2;
break;
default:
InvalidOperation io;
io.what = work.op;
io.why = "Invalid Operation";
throw io;
}
SharedStruct ss;
ss.key = logid;
ss.value = "5";
log[logid] = ss;
return val;
}
void getStruct(SharedStruct &ret, const int32_t logid) {
printf("getStruct(%d)\n", logid);
ret = log[logid];
}
void zip() {
printf("zip()\n");
}
protected:
map<int32_t, SharedStruct> log;
};
int main(int argc, char **argv) {
shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());
shared_ptr<CalculatorHandler> handler(new CalculatorHandler());
shared_ptr<TProcessor> processor(new CalculatorProcessor(handler));
shared_ptr<TServerTransport> serverTransport(new TServerSocket(9090));
shared_ptr<TTransportFactory> transportFactory(new TBufferedTransportFactory());
TSimpleServer server(processor,
serverTransport,
transportFactory,
protocolFactory);
/**
* Or you could do one of these
shared_ptr<ThreadManager> threadManager =
ThreadManager::newSimpleThreadManager(workerCount);
shared_ptr<PosixThreadFactory> threadFactory =
shared_ptr<PosixThreadFactory>(new PosixThreadFactory());
threadManager->threadFactory(threadFactory);
threadManager->start();
TThreadPoolServer server(processor,
serverTransport,
transportFactory,
protocolFactory,
threadManager);
TThreadedServer server(processor,
serverTransport,
transportFactory,
protocolFactory);
*/
printf("Starting the server...\n");
server.serve();
printf("done.\n");
return 0;
}

16
tutorial/cpp/Makefile Normal file
View File

@ -0,0 +1,16 @@
BOOST_DIR = /usr/local/boost/include/boost-1_33_1/
THRIFT_DIR = /usr/local/include/thrift
LIB_DIR = /usr/local/lib
GEN_SRC = ../gen-cpp/SharedService.cpp ../gen-cpp/shared_types.cpp ../gen-cpp/tutorial_types.cpp ../gen-cpp/Calculator.cpp
default: server client
server: CppServer.cpp
g++ -o CppServer -I${THRIFT_DIR} -I${BOOST_DIR} -I../gen-cpp -L${LIB_DIR} -lthrift CppServer.cpp ${GEN_SRC}
client: CppClient.cpp
g++ -o CppClient -I${THRIFT_DIR} -I${BOOST_DIR} -I../gen-cpp -L${LIB_DIR} -lthrift CppClient.cpp ${GEN_SRC}
clean:
rm -fr CppClient CppServer

64
tutorial/php/PhpClient.php Executable file
View File

@ -0,0 +1,64 @@
#!/usr/bin/env php
<?php
$GLOBALS['THRIFT_ROOT'] = '../../lib/php/src';
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';
/**
* Suppress errors in here, which happen because we have not installed into
* $GLOBALS['THRIFT_ROOT'].'/packages/tutorial' like we are supposed to!
*
* Normally we would only have to include Calculator.php which would properly
* include the other files from their packages/ folder locations, but we
* include everything here due to the bogus path setup.
*/
error_reporting(E_NONE);
$GEN_DIR = '../gen-php';
require_once $GEN_DIR.'/SharedService.php';
require_once $GEN_DIR.'/shared_types.php';
require_once $GEN_DIR.'/Calculator.php';
require_once $GEN_DIR.'/tutorial_types.php';
error_reporting(E_ALL);
$socket = new TSocket('localhost', 9090);
$transport = new TBufferedTransport($socket, 1024, 1024);
$protocol = new TBinaryProtocol($transport);
$client = new CalculatorClient($protocol);
$transport->open();
$client->ping();
print "ping()\n";
$sum = $client->add(1,1);
print "1+1=$sum\n";
$work = new tutorial_Work();
$work->op = tutorial_Operation::DIVIDE;
$work->num1 = 1;
$work->num2 = 0;
try {
$client->calculate(1, $work);
print "Whoa! We can divide by zero?\n";
} catch (tutorial_InvalidOperation $io) {
print "InvalidOperation: $io->why\n";
}
$work->op = tutorial_Operation::SUBTRACT;
$work->num1 = 15;
$work->num2 = 10;
$diff = $client->calculate(1, $work);
print "15-10=$diff\n";
$log = $client->getStruct(1);
print "Log: $log->value\n";
$transport->close();
?>

58
tutorial/py/PythonClient.py Executable file
View File

@ -0,0 +1,58 @@
#!/usr/bin/env python
import sys
sys.path.append('../gen-py')
from tutorial import Calculator
from tutorial.ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
# Make socket
transport = TSocket.TSocket('localhost', 9090)
# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)
# Wrap in a protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the protocol encoder
client = Calculator.Client(protocol)
# Connect!
transport.open()
client.ping()
print 'ping()'
sum = client.add(1,1)
print '1+1=%d' % (sum)
work = Work()
work.op = Operation.DIVIDE
work.num1 = 1
work.num2 = 0
try:
quotient = client.calculate(1, work)
print 'Whoa? You know how to divide by zero?'
except InvalidOperation, io:
print 'InvalidOperation: %s' % (io.__str__())
work.op = Operation.SUBTRACT
work.num1 = 15
work.num2 = 10
diff = client.calculate(1, work)
print '15-10=%d' % (diff)
log = client.getStruct(1)
print 'Check log: %s' % (log.value)
# Close!
transport.close()

77
tutorial/py/PythonServer.py Executable file
View File

@ -0,0 +1,77 @@
#!/usr/bin/env python
import sys
sys.path.append('../gen-py')
from tutorial import Calculator
from tutorial.ttypes import *
from shared.ttypes import SharedStruct
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class CalculatorHandler:
def __init__(self):
self.log = {}
def ping(self):
print 'ping()'
def add(self, n1, n2):
print 'add(%d,%d)' % (n1, n2)
return n1+n2
def calculate(self, logid, work):
print 'calculate(%d, %s)' % (logid, work.__str__())
if work.op == Operation.ADD:
val = work.num1 + work.num2
elif work.op == Operation.SUBTRACT:
val = work.num1 - work.num2
elif work.op == Operation.MULTIPLY:
val = work.num1 * work.num2
elif work.op == Operation.DIVIDE:
if work.num2 == 0:
x = InvalidOperation()
x.what = work.op
x.why = 'Cannot divide by 0'
raise x
val = work.num1 / work.num2
else:
x = InvalidOperation()
x.what = work.op
x.why = 'Invalid operation'
raise x
log = SharedStruct()
log.key = logid
log.value = '%d' % (val)
self.log[logid] = log
return val
def getStruct(self, key):
print 'getStruct(%d)' % (key)
return self.log[key]
def zip(self):
print 'zip()'
handler = CalculatorHandler()
processor = Calculator.Processor(handler)
transport = TSocket.TServerSocket(9090)
tfactory = TTransport.TBufferedTransportFactory()
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
server = TServer.TSimpleServer(processor, transport, tfactory, pfactory)
# You could do one of these for a multithreaded server
#server = TServer.TThreadedServer(processor, transport, tfactory, pfactory)
#server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
print 'Starting the server...'
server.serve()
print 'done.'

43
tutorial/rb/RubyClient.rb Executable file
View File

@ -0,0 +1,43 @@
#!/usr/bin/env ruby
$:.push('../gen-rb')
require 'thrift/transport/tsocket'
require 'thrift/protocol/tbinaryprotocol'
require 'Calculator'
transport = TSocket.new('localhost', 9090)
protocol = TBinaryProtocol.new(transport)
client = Calculator::Client.new(protocol)
transport.open()
client.ping()
print "ping()\n"
sum = client.add(1,1)
print "1+1=", sum, "\n"
work = Work.new()
begin
work.op = Operation::DIVIDE
work.num1 = 1
work.num2 = 0
quot = client.calculate(1, work)
puts "Whoa, we can divide by 0 now?"
rescue InvalidOperation => io
print "InvalidOperation: ", io.why, "\n"
end
work.op = Operation::SUBTRACT
work.num1 = 15
work.num2 = 10
diff = client.calculate(1, work)
print "15-10=", diff, "\n"
log = client.getStruct(1)
print "Log: ", log.value, "\n"
transport.close()

78
tutorial/rb/RubyServer.rb Executable file
View File

@ -0,0 +1,78 @@
#!/usr/bin/env ruby
$:.push('../gen-rb')
require 'thrift/transport/tsocket'
require 'thrift/protocol/tbinaryprotocol'
require 'thrift/server/tserver'
require 'Calculator'
require 'shared_types'
class CalculatorHandler
include Calculator::Iface
def initialize()
@log = {}
end
def ping()
puts "ping()"
end
def add(n1, n2)
print "add(", n1, ",", n2, ")\n"
return n1 + n2
end
def calculate(logid, work)
print "calculate(", logid, ", {", work.op, ",", work.num1, ",", work.num2,"})\n"
if work.op == Operation::ADD
val = work.num1 + work.num2
elsif work.op == Operation::SUBTRACT
val = work.num1 - work.num2
elsif work.op == Operation::MULTIPLY
val = work.num1 * work.num2
elsif work.op == Operation::DIVIDE
if work.num2 == 0
x = InvalidOperation.new()
x.what = work.op
x.why = "Cannot divide by 0"
raise x
end
val = work.num1 / work.num2
else
x = InvalidOperation()
x.what = work.op
x.why = "Invalid operation"
raise x
end
entry = SharedStruct.new()
entry.key = logid
entry.value = "#{val}"
@log[logid] = entry
return val
end
def getStruct(key)
print "getStruct(", key, ")\n"
return @log[key]
end
def zip()
print "zip\n"
end
end
handler = CalculatorHandler.new()
processor = Calculator::Processor.new(handler)
transport = TServerSocket.new(9090)
server = TSimpleServer.new(processor, transport)
puts "Starting the server..."
server.serve()
puts "done."

18
tutorial/shared.thrift Executable file
View File

@ -0,0 +1,18 @@
#!/usr/local/bin/thrift -cpp -java -py -php -xsd
/**
* This Thrift file can be included by other Thrift files that want to share
* these definitions.
*/
cpp_namespace shared
java_package shared
struct SharedStruct {
1: i32 key
2: string value
}
service SharedService {
SharedStruct getStruct(1: i32 key)
}

127
tutorial/tutorial.thrift Normal file
View File

@ -0,0 +1,127 @@
#!/usr/local/bin/thrift -cpp -java -py -php -rb -xsd -r
#
# Thrift Tutorial
# Mark Slee (mcslee@facebook.com)
#
# This file aims to teach you how to use Thrift, in a .thrift file. Neato. The
# first thing to notice is that .thrift files support standard shell comments.
# This lets you make your thrift file executable and include your Thrift build
# step on the top line. And you can place comments like this anywhere you like.
#
# Before running this file, you will need to have installed the thrift compiler
# into /usr/local/bin.
/**
* The first thing to know about are types. The available types in Thrift are:
*
* bool Boolean, one byte
* byte Signed byte
* i16 Signed 16-bit integer
* i32 Signed 32-bit integer
* i64 Signed 64-bit integer
* double 64-bit floating point value
* string String
* map<t1,t2> Map from one type to another
* list<t1> Ordered list of one type
* set<t1> Set of unique elements of one type
*
* Did you also notice that Thrift supports C style comments?
*/
// Just in case you were wondering... yes. We support simple C comments too.
/**
* Thrift files can reference other Thrift files to include common struct
* and service definitions. These are found using the current path, or by
* searching relative to any paths specified with the -I compiler flag.
*
* Included objects are accessed using the name of the .thrift file as a
* prefix. i.e. shared.SharedObject
*/
include "shared.thrift"
/**
* Thrift files can namespace, package, or prefix their output in various
* target languages.
*/
cpp_namespace tutorial
java_package tutorial
php_namespace tutorial
/**
* Thrift lets you do typedefs to get pretty names for your types. Standard
* C style here.
*/
typedef i32 MyInteger
/**
* Thrift also lets you define constants for use across languages. Complex
* types and structs are specified using JSON notation.
*/
const i32 INT32CONSTANT = 9853
const map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}
/**
* You can define enums, which are just 32 bit integers. Values are optional
* and start at 1 if not supplied, C style again.
*/
enum Operation {
ADD = 1,
SUBTRACT = 2,
MULTIPLY = 3,
DIVIDE = 4
}
/**
* Structs are the basic complex data structures. They are comprised of fields
* which each have an integer identifier, a type, a symbolic name, and an
* optional default value.
*/
struct Work {
1: i32 num1 = 0,
2: i32 num2,
3: Operation op
}
/**
* Structs can also be exceptions, if they are nasty.
*/
exception InvalidOperation {
1: i32 what,
2: string why
}
/**
* Ahh, now onto the cool part, defining a service. Services just need a name
* and can optionally inherit from another service using the extends keyword.
*/
service Calculator extends shared.SharedService {
/**
* A method definition looks like C code. It has a return type, arguments,
* and optionally a list of exceptions that it may throw. Note that argument
* lists and exception lists are specified using the exact same syntax as
* field lists in struct or exception definitions.
*/
void ping(),
i32 add(1:i32 num1, 2:i32 num2),
i32 calculate(1:i32 logid, 2:Work w) throws (1:InvalidOperation ouch),
/**
* This method has an async modifier. That means the client only makes
* a request and does not listen for any response at all. Async methods
* must be void.
*/
async void zip()
}
/**
* That just about covers the basics. Take a look in the test/ folder for more
* detailed examples. After you run this file, your generated code shows up
* in folders with names gen-<language>. The generated code isn't too scary
* to look at. It even has pretty indentation.
*/