• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
CL.TANG
非官方言论,知识谨慎吸收
博客园    首页    新随笔    联系   管理    订阅  订阅

thrift文件编写

1.thrift的数据类型。(这里主要为翻译官方文档)

  a. 基本数据类型

    1) boolean型,值为true或false

    2) byte型,值为单字节字母

    3) i16型,值长度为16位的integer带符号数字.

    4) i32型,值长度为32位的integer带符号数字.

    5) i64型,值长度为64位的integer带符号数字.

    6) double型,值长度为64的浮点数.

    7) string型,字符串或者binary数据。

  b. 结构体

    有点类似c的结构体。不怎么懂c的同学可以理解为不带方法的类,只有属性而已。

    型如:

    

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

 

  c. 容器

    1) list类型。

      指一组有序元素的集合,类似于java的ArrayList类。

    

typedef i32 MyInteger
MyInteger a;
struct hello{
    list<MyInteger> d; }

指hello结构体中含有一个list类型的数据,且里面的元素都必须是a数据类型的MyInteger数据类型的数据,主要这里的typedef起一个别名的作用。

    2) set类型

      一个不可重复的排序元素的集合,类似java的hashset,python的set.使用同list

    3) map<type1,type2>类型。

      使用如:

map<string,string> MAPCONSTANT = {'hello':'world', 'goodnight':'moon'}

    4) 枚举类型

 

2. 服务

  有了数据结构,那么就该对数据进行操作。thrift这里所说的服务,不可能针对我们自己的业务逻辑,而是指的是服务端从端口读取数据,客户端传入数据的一套方法。

service <服务名>{
     <返回类型> <方法名>(参数) throws <异常>
}

3. 实践:

根据前面所说数据类型和结构体,编写如下thrift文件,名为 test.thrift

i32 a;
i32 b;
string c;
list<string> d;
map<string, string> e = {"hello" : "world", "dd" : "ee"};

struct f{
    1:a,
    2:b,
    3:c=2,
    4:d="ceshi",
    5:e,
}

service Hello{
    f get_f(1:f gg) throws (Exception, e)
}

以上代码使用thrift的标准命令(thrift.exe -gen py test.thrift)是不成功,因为语法还是不正确!

修改

1.常量量如a,b,c,d, e要使用const修饰。

2. 如果都使用了const修饰,但有;号,还是不成功,尽管有的人说thrift对;不敏感,那还是估计看版本的,这里我使用的是最新的0.8版本。也就是说,如果你是一个java或者php程序员,请注意没有";"

3. 静态变量请赋值,如const i32 a = "helloworld"

4. 结构体内部的属性请指定参数类型,因为结构体内部属性和外部的静态属性没有任何关系。

5. 异常之间没有“,”分割,以空格代替

6. Exception类型请定义。

7. throws参数也要指定是第几个参数

好了,修改后的thrift脚本变为

const i32 a = 1
const i32 b = 2
const string c = "helloworld"
const list<string> d = "test"
const map<string, string> e = {"hello" : "world", "dd" : "ee"}

struct f{
    1:i32 a,
    2:i32 b,
    3:string c,
    4:list<string> d=["ceshi"],
    5:map<string,string> e = {"hello":"world"},
}
exception Exception{
    1:i32 what;
    2:string where;
}
service Hello{
    f get_f(1:f gg) throws (1:Exception e)
}

稍微有那么一点像生产环境了,生成代码结构:

我们可以让这个随便编写的脚本为我们完成一点点功能,比如get_f让他对结构体f的对象gg的各种值计算,当然生成环境也可以集成数据库了。

 

那么现在需要几个数据通信接口了。

1. 读取数据:readMessageBegin()

  (fname, mtype, rseqid) = self._iprot.readMessageBegin()

  从端口读取请求流,获取3个值。

  readMessageEnd() 读取数据结束。

2. readStructBegin() 开始读取结构体

  readStructEnd() 读取结构体结束

3. readFieldBegin() 读取属性开始

  readFieldEnd()

4. readMapBegin()读取map开始

  readMapEnd()读取map结束。

这里的数据接口还有很多,不一一列举,因为我们在实际使用thrift的时候,只要不类似于修改thrift源码的操作,都不需要关心这些具体的数据操作。

 

接下来我们做客户端和服务端的操作,同时在服务端进行具体业务的处理。

 

1. 编写借口类.

  

import sys
sys.path.append("../gen-py")
from cc import Hello

from cc import ttypes

class My_Handler(Hello.Iface):

    def get_f(self, gg):
        #对对象gg进行梳理
        gg.a = gg.a + gg.b
        gg.b = gg.a - gg.b
        return gg

官方事例的CalculatorHandler是不继承Iface的,而我这里采用继承Hello.Iface,并没有别的深意,是表示一定要有我们定义thrift文件的get_f方法。假如我们的逻辑更加复杂,handler处理里有数据库操作等等,继承不继承Iface都是可以的。

官方的handler(部分):

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

接下来要对这个handler要对这个hander进行处理:

import sys
sys.path.append("../gen-py")
from cc import Hello

from cc import ttypes

class My_Handler(Hello.Iface):

    def get_f(self, gg):
        #对对象gg进行梳理
        gg.a = gg.a + gg.b
        gg.b = gg.a - gg.b
        return gg

handler = My_Handler()
process = Hello.Processor(handler)
from thrift.transport.TSocket import TServerSocket
server = TServerSocket(host="localhost", port = 9090)
from thrift.transport.TTransport import TBufferedTransportFactory
tfactory = TBufferedTransportFactory()
from thrift.protocol.TBinaryProtocol import TBinaryProtocolFactory
bfactory = TBinaryProtocolFactory()

from thrift.server.TServer import  TSimpleServer
servers = TSimpleServer(process, server, tfactory, bfactory)
print "starting the server..."
servers.serve()
print "done..."

直接上代码了。注意,这里的TServerSocket一定要指定,因为官方的类初始化时host赋初值居然写的none,等于没有写麽。

class TServerSocket(TSocketBase, TServerTransportBase):
  """Socket implementation of TServerTransport base."""

  def __init__(self, host=None, port=9090, unix_socket=None):

一个没有host的服务,可以想象会是什么样子,使用netstat -na查看端口:

 TCP    [::]:9090              [::]:0                 LISTENING

就是没有host导致的情况。

一个host定义为localhost的服务,使用netstat -na查看端口:

TCP    127.0.0.1:9090         0.0.0.0:0              LISTENING

 

接下来编写客户端,客户端的编写主要是是实例化Hello.Client对象

__author__ = 'CLTANG'
#! -*- encoding:utf-8 -*-

'''
    客户端
'''

import sys
sys.path.append("../gen-py")
from cc import Hello
from cc import ttypes


from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol.TBinaryProtocol import TBinaryProtocol
transport = TSocket.TSocket('localhost', 9090)

# Buffering is critical. Raw sockets are very slow
transport = TTransport.TBufferedTransport(transport)

# Wrap in a protocol
protocol = TBinaryProtocol(transport)
client = Hello.Client(protocol)

transport.open()

ff = ttypes.f(a=1,b=2)

results = client.get_f(ff)
print results
transport.close()

执行客户端程序,最终将输出:

f(a=3, c='helloworld', b=1, e={'cc': 'dd', 'ee': 'ff'}, d=['hello', 'world'])

达到了我们最早定义在test.thrift中的service的内容,即传入f类型的实例化对象,调用get_f方法,返回一个经过处理的f类型的实例化对象。

 

posted @ 2012-09-20 12:49  CL.TANG  阅读(2381)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3