Thrift 简介

Thrift是一个跨语言服务部署框架,最初由Facebook于2007年开发,后于2008年进入Apache孵化器(Apache Incubator)。

类似于SOAP,COM 和CORBA,Thrift通过定义一个中间定义语言和Thrift代码生成工具,生成指定语言的代码。目前,Thrift支持C++,Java, Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, Smalltalk和OCaml的代码生成。

简单分析其机理,Thrift就是实现C/S模式,通过代码生成工具将接口定义文件生成服务器端和客户端代码(可以为不同语言),从而实现服务端和客户端跨语言的支持。

 

更多的内容可以参看下面的链接

http://thrift.apache.org/

 

下面贴一些使用的例子,更便于理解。

1、首先安装thrift,这个就不多说了

./configure

make

makeinstall

2、编写.thrift文件,告诉Thrift你需要怎样的数据结构以及服务形式

#RTDCCmd.thrift

struct Rusult {
  1: i32 status,
  2: string output
}

service RTDCCommand{
  Rusult osCmd(1: string cmd)
  oneway void osCmdAsync(1: string cmd)
}

 

文件里定义了两个东西,一个是数据结构Rusult,类似于一个结构体,由两个元素组成,一个整型,一个字符型;另一个是服务RTDCCommand,里面定义了两个接口,一个是osCmd,用于执行一个操作系统命令,一个osCmdAsync,用于执行异步命令的调用(可以用于RPC场景)。

3、生成thrift相应的包、模块,下面分别生成PYTHON和PERL的

thrift -r --gen py RTDCCmd.thrift
thrift -r --gen perl RTDCCmd.thrift

执行完成以后,目录下分别会生成gen-py 和gen-perl两个文件夹,里面分别是需要用到的一些包、模块。

4、仅仅靠thrift自动生成的那些东西当然还不够,前面我们定义了一些接口,接下来我们需要对其进行实现。我把自己写的几个程序代码都贴上吧,便于理解

#RTDCCommandHandler.py 

 

#!/bin/env python
from RTDCCmd.ttypes import *
from RTDCCmd import RTDCCommand
import commands
import os
import sys

class RTDCCommandHandler(RTDCCommand.Iface):
   
    def __init__(self):
        #RTDCCommand.Iface.__init__(self)
        pass

    def osCmd(self,cmd):
        ret = commands.getstatusoutput(cmd)
        result = Rusult(ret[0],ret[1])
        return result
    def osCmdAsync(self,cmd):
        commands.getstatusoutput(cmd) 


继承RTDCCommand.Iface,对其接口进行实现。

 

#RTDCServer.py  

 

#!/bin/env python
from thrift.server.TServer import TServer
from thrift.server.TServer import TSimpleServer
from thrift.server.TServer import TThreadedServer
from thrift.transport.TSocket import TServerSocket
from thrift.transport.TTransport import *
from RTDCCommandHandler import RTDCCommandHandler
from RTDCCmd import *


if __name__ == '__main__':
    handler = RTDCCommandHandler()
    processor = RTDCCommand.Processor(handler)
    serverTransport = TServerSocket(9091)
    server = TThreadedServer(processor,serverTransport)
    print "start server......"
    server.serve()

 

这个其实就是一个服务进程,client端连接server,进行相关请求。

 

#RTDCClient.py

 

#!/bin/env python
from thrift.transport import TSocket 
from thrift.transport import TTransport 
from thrift.protocol import TBinaryProtocol
from RTDCCmd.RTDCCommand import Client
from RTDCCmd.ttypes import *

class RTDCClient():

    #static member
    port = 9091
    #constructor
    def __init__():
        pass

    @staticmethod
    def remoteOSCmd(host,cmd):
        ret=[1,'err']
        try:
            transport = TSocket.TSocket(host,RTDCClient.port)
            transport = TTransport.TBufferedTransport(transport)
            #wrap in a protocol
            protocol = TBinaryProtocol.TBinaryProtocol(transport)

            client = Client(protocol)
            #open transport
            transport.open()
            result = client.osCmd(cmd)
            ret = [result.status,result.output]
        except:
            s=sys.exc_info()
            print str(s[1]) + ' on line ' + str(s[2].tb_lineno)
        finally:
            if transport:
                transport.close()
        return ret
       
    @staticmethod
    def remoteOSCmdAsync(host,cmd):
        try:
            transport = TSocket.TSocket(host,RTDCClient.port)
            transport = TTransport.TBufferedTransport(transport)
            #wrap in a protocol
            protocol = TBinaryProtocol.TBinaryProtocol(transport)

            client = Client(protocol)
            #open transport
            transport.open()
            client.osCmdAsync(cmd)
        except:
            s=sys.exc_info()
            print str(s[1]) + ' on line ' + str(s[2].tb_lineno)
        finally:
            if transport:
                transport.close()

 

这个是CLIENT端代码的实现,在其他程序中只要引用CLINET端里面的两个静态方法就可以实现远程调用的功能了。

下面给个示例程序,功能很简单,就是远程执行一个cat命令把testfile文件的内容打印出来,并返回。最后在程序中把程序的运行状态和输出结果分别print到标准输出上

#1.py

 

#!/bin/env python
from RTDCClient import RTDCClient
import time

ISOTIMEFORMAT='%Y-%m-%d %X'

ret = RTDCClient.remoteOSCmd("localhost","cat /home/gpadmin1/joe.wangh/thrift/test/gen-py/testfile")
print time.strftime( ISOTIMEFORMAT, time.localtime() )
print ret[0]
print ret[1]

 

首先运行server进程

nohup python RTDCServer.py &

然后运行示例程序

[gpadmin1@hadoop5 gen-py]$ python 1.py

2011-05-24 14:40:45
0
haha
hehe

 

前面的那一堆东西,全是用PYTHON写的,没实现跨语言调用,不给力,总觉得没发挥Thrift价值,呵呵,下面用PERL来实现一个CLIENT试试。

#RTDCClient.pl

 

#!/usr/bin/perl
use strict;             #declare using perl strict syntax
use Thrift;
use Thrift::Socket;
use Thrift::FramedTransport;
use Thrift::BinaryProtocol;
use Thrift::BufferedTransport;
use Thrift::Constants;
use Thrift::RTDCCommand;
use Thrift::Types;

package RTDCClient;

my $port = 1987;
my $host = "";
my $cmd = "";
my $result = Rusult->new(undef,undef);

#connect to db
sub remoteOSCmd
{
        $host = @_[0];
        $cmd = @_[1];
        my $socket = Thrift::Socket->new($host,$port);
        $socket->setRecvTimeout(1000000000);
        my $transport = Thrift::BufferedTransport->new($socket);
        my $protocol = Thrift::BinaryProtocol->new($transport);
        my $client = RTDCCommandClient->new($protocol);
        eval {$transport->open()};
        if($@)
        {
                my $ex = $@;
                die "error when transport open: $@".$ex->{message}."/n";
        }
        #$result = RTDCCommandClient->osCmd($client,$cmd);
        $result = $client->osCmd($cmd);
        return $result;

}

sub remoteOSCmdAsync
{
        $host = @_[0];
        $cmd = @_[1];
        my $socket = Thrift::Socket->new($host,$port);
        $socket->setRecvTimeout(1000000000);
        my $transport = Thrift::BufferedTransport->new($socket);
        my $protocol = Thrift::BinaryProtocol->new($transport);
        my $client = RTDCCommandClient->new($protocol);
        eval {$transport->open()};
        if($@)
        {
                my $ex = $@;
                die "error when transport open: $@".$ex->{message}."/n";
        }
        #$result = RTDCCommandClient->osCmd($client,$cmd);
        $client->osCmdAsync($cmd);
}

1;      #terminate the package with the required 1

 

示例程序如下

#1.pl

use strict; 
use RTDCClient;

RTDCClient::remoteOSCmdAsync("localhost","sleep 10;touch /home/dwapp/joe.wangh/gen-py/testfile1");

该程序的功能是实现一个远程异步调用,在程序中我故意先sleep 10,你会看到在客户端程序很快就返回了。可是在服务端,过10秒钟以后才会生成一个testfile1文件。

 

差不多就写这些吧,在我学习Thrift的过程中比较费事的时有时环境不正确,比如说PYTHONPATH或者PERL5LIB,或者你机器上其他一些库没有装,其他的倒还好。

 

完。


#########下面转一篇#########

RPC技术及实现简介

首先思考一下分布式系统中的 RPC (Remote Procedure Call) 问题,一个完整的 RPC 模块需要可以分为三个层次

  • 服务层(service):RPC 接口定义与实现
  • 协议层(protocol):RPC 报文格式和数据编码格式
  • 传输层(transport):实现底层的通信(如 socket)以及系统相关的功能(如事件循环、多线程)

在实际的大型分布式系统中,不同的服务往往会使用不同的语言来实现,所以一般的 RPC 系统会提供一种跨语言的过程调用功能,比如一段用C++实现的客户端代码可以远程调用一个用 Java 实现的服务。实现跨语言 RPC 有两种方法:

  • 静态代码生成:开发者用一种中间语言(IDL,接口定义语言)来定义 RPC 的接口和数据类型,然后通过一个编译器来生成不同语言的代码(如C++, Java, Python),并由生成的代码来负责 RPC 协议层和传输层的实现。例如,服务的实现用C++,则服务端需要生成实现RPC协议和传输层的C++代码,服务层使用生成的代码来实现与客户端的通信;而如果客户端用 Python,则客户端需要生成Python代码。
  • 基于“自省”的动态类型系统来实现:协议和传输层可以只用一种语言实现成一个库,但是这种语言需要关联一个具备“自省”或者反射机制的动态类型系统,对外提供其他语言的绑定,客户端和服务端通过语言绑定来使用 RPC。比如,可以考虑用 C 和 GObject 实现一个 RPC 库,然后通过 GObject 实现其他语言的绑定。

第一种方法的优点是RPC的协议层和传输层的实现不需要和某种动态类型系统(如GObject)绑定在一起,同时避免了动态类型检查和转换,程序效率比较高,但是它的缺点是要为不同语言提供不同的 RPC 协议层和传输层实现。第二种方法的主要难度在于语言绑定和通用的对象串行化机制的实现,同时也需要考虑效率的问题。

Thrift 是一个基于静态代码生成的跨语言的RPC协议栈实现,它可以生成包括C++, Java, Python, Ruby, PHP 等主流语言的代码,这些代码实现了 RPC 的协议层和传输层功能,从而让用户可以集中精力于服务的调用和实现。Cassandra 的服务访问协议是基于 Thrift 来实现的。

Thrift介绍

Thrift源于大名鼎鼎的facebook之手,在2007年facebook提交Apache基金会将Thrift作为一个开源项目,对于当时的facebook来说创造thrift是为了解决facebook系统中各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于程序对程 序静态的数据交换,需要先确定好他的数据结构,他是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSON和xml无论在性能、传输大小上有明显的优势。

Thrift 主要由5个部分组成:

  • 类型系统以及 IDL 编译器:负责由用户给定的 IDL 文件生成相应语言的接口代码
  • TProtocol:实现 RPC 的协议层,可以选择多种不同的对象串行化方式,如 JSON, Binary。
  • TTransport:实现 RPC 的传输层,同样可以选择不同的传输层实现,如socket, 非阻塞的 socket, MemoryBuffer 等。
  • TProcessor:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口。
  • TServer:聚合 TProtocol, TTransport 和 TProcessor 几个对象。

上述的这5个部件都是在 Thrift 的源代码中通过为不同语言提供库来实现的,这些库的代码在 Thrift 源码目录的 lib 目录下面,在使用 Thrift 之前需要先熟悉与自己的语言对应的库提供的接口。


你可能感兴趣的:(Thrift 简介)