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类型的实例化对象。

