thrift Tutorial 详解

最近有业务需要接触thrift,之前只是简单了解过,做下对 thrift 入门的笔记。

首先我们需要下载 shared.thrift 和 tutorial.thrift 两个文件,然后执行命令生成py文件:

命令代码示例:

thrift -r –gen
执行命令:

thrift -r –gen py tutorial.thrift

1、我们先分析看看 shared.thrift 和 tutorial.thrift 两个文件,这两个文件定义了通信双方的接口和数据格式

先看下 shared.thrift 文件

namespace cpp shared
namespace d share // “shared” would collide with the eponymous D keyword.
namespace java shared
namespace perl shared
namespace php shared
namespace haxe shared

//namespace 是指单独为某种语言制定生成的命名空间,否则就以当时的文件名命名
//比如在这里设置 namespace py sharedxxxxxx
//就会生成 sharedxxxxxx 目录,在目录中有init文件

//定义一种数据类型,SharedStruct 第一个数值类型为inter32,第二个数据类型为string
struct SharedStruct {
1: i32 key
2: string value
}

//定义一个接口类
service SharedService {
//定义一个函数名称为 getStruct,返回 SharedStruct 类型返回值,参数第一个是inter32的值
SharedStruct getStruct(1: i32 key)
}
然后我们分析下 tutorial.thrift 文件

/**

  • thrift支持的基础类型:
    *
  • bool 布尔
  • byte 字节
  • i16 16位整形
  • i32 32位整形
  • i64 64位整形
  • double 64位浮点
  • string 字符串
  • binary 二进制,字节数组
  • map<t1,t2> 字典,key,value
  • list 数组
  • set set,无重复项数组
    /

/**

  • thrift 可以include包含其他文件
    */
    include “shared.thrift”

/**

  • thrift 可以使用 typedef 来自定义类型的名字
    */
    typedef i32 MyInteger

/**

  • thrift允许我们定义跨语言常量
    */
    const i32 INT32CONSTANT = 9853
    const map<string,string> MAPCONSTANT = {‘hello’:’world’, ‘goodnight’:’moon’}

/**

  • 我们也可以定义枚举,如果没有提供将从1开始
    */
    enum Operation {
    ADD = 1,
    SUBTRACT = 2,
    MULTIPLY = 3,
    DIVIDE = 4
    }

/**

  • struct 结构是一个基本的复杂数据结构,

    • 需要一个整数的标示,一个类型,一个变量名和一个可选的默认值
  • 可选的声明表示这个字段不是必须被包含在这个结构里的
    */
    struct Work {
    1: i32 num1 = 0,
    2: i32 num2,
    3: Operation op,
    4: optional string comment,
    }

/**

  • 可以定义异常的类型struct结构体
    */
    exception InvalidOperation {
    1: i32 what,
    2: string why
    }

/**

  • 定义一个service,就是接口类
  • 可以通过extends用来继承其他 service
    */
    service Calculator extends shared.SharedService {

    /**

    • 定义一个方法,它有返回值和参数
    • 可选的定义一个他可能抛出的异常数组
    • 注意参数的定义和异常的定义,需要在之前定义过他们
      */

    void ping(),

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

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

    /**

    • oneway 修饰符表示这个方法客户端只是调用,并不关心它的返回值
    • 所以这个方法的返回值必须是void

      */
      oneway void zip()

}

2、我们现在分析thrift clinet的代码

import sys, glob
sys.path.append(‘gen-py’)
sys.path.insert(0, glob.glob(‘../../lib/py/build/lib.*’)[0])

from tutorial import Calculator
from tutorial.ttypes import *

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

try:

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类型是在之前的thrift接口文件定义的
work = Work()

work.op = Operation.DIVIDE
work.num1 = 1
work.num2 = 0

#执行1除以0,会抛出错误
try:
quotient = client.calculate(1, work)
print ‘Whoa? You know how to divide by zero?’
except InvalidOperation, io:
print ‘InvalidOperation: %r’ % io

#执行15-10
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()

except Thrift.TException, tx:
print ‘%s’ % (tx.message)
其实客户端的一些程序都是调用方法,具体的方法实现我们要进一步看服务器那边的实现。

3、分析一下服务器那边的实现

import sys, glob
sys.path.append(‘gen-py’)
sys.path.insert(0, glob.glob(‘../../lib/py/build/lib.*’)[0])

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

#定义实现接口类 CalculatorHandler
class CalculatorHandler:
def init(self):
self.log = {}

#定义ping方法
def ping(self):
print ‘ping()’

#定义add方法
def add(self, n1, n2):
print ‘add(%d,%d)’ % (n1, n2)
return n1+n2

#定义 calculate 方法
def calculate(self, logid, work):
print ‘calculate(%d, %r)’ % (logid, work)

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:                              #如果op不在枚举中,那就抛出错误
  x = InvalidOperation()
  x.what = work.op
  x.why = 'Invalid operation'
  raise x

#实例化 sharedStruct 类,用来记录这次操作的logid的记录
log = SharedStruct()
log.key = logid
log.value = '%d' % (val)
self.log[logid] = log

return val

#获得根据logid获得记录的计算值
def getStruct(self, key):
print ‘getStruct(%d)’ % (key)
return self.log[key]

def zip(self):
print ‘zip()’

#下面就是thrift启动服务的固定用法了
handler = CalculatorHandler()
processor = Calculator.Processor(handler)
transport = TSocket.TServerSocket(port=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.’

其实thrift刚接触比较容易一头雾水,真正看懂了入门示例,感觉还是很好上手的