1 简介
Thrift 源于 facebook,2007 年 facebook 提交 Apache 基金将其作为一个开源的项目。
Apache Thrift 是 facebook 实现的一种高效的、支持多种编程语言的远程服务调用框架,包括一个软件库和一组代码生成工具,以加快高效率、可扩展的后端服务的开放和实现速度。
技术的关键在于:它采用接口描述语言定义并创建服务,支持可扩展的跨语言服务开发,它的代码生成器引擎可以在多种语言中创建高效的、无缝的服务,传输数据格式采用二进制格式,相对于 JSON 和 XML 体积更小,对于高并发、大数据量和多语言的环境更有优势。
2 优缺点
2.1 优点
- 支持多种编程语言:C++、Java、python、php、Ruby、Erlang、Perl、Haskell、C#、Cocoa、Smalltalk;
- 在多种不同的语言之间通信,thrift 可以作为二进制的高性能的通讯中间件,支持数据对象的序列化和多种类型的 RPC 服务。
- Thrift 适用于搭建大型数据的交换及存储的通用工具,对于大型系统中的内部数据传输相对于 JSON 和 XML 无论在性能和传输大小上有明显的优势。
2.2 缺点
Thrift 适用于程序对程序静态的数据交换,需先确定好它的数据结构,它是完全静态化的,当数据节后发生变化时,必须重新编译 IDL 文件,实现类接口,Client 端和 Server 端做相应的修改
3 安装及开发流程
3.1 安装
3.2 开发流程
定义 IDL 文件 -> 使用代码生成工具生成代码 -> 实现类接口 -> 实现 client 端及 server 端
4 Thrift 架构
Thrift 包含一个完整的堆栈结构用于构建客户端和服务器端。下图描绘了 Thrift 的整体架构。
如上图所示:
- 黄色部分是用户实现的业务逻辑
- 褐色部分是根据 Thrift 定义的服务接口描述文件(IDL)生成的客户端和服务器端代码框架
- 红色部分是根据 Thrift 文件生成代码实现数据的读写操作
- 红色以下部分分别是 Thrift 的传输协议、体系及底层 I/O 通信
使用 Thrift 可以很方便的定义一个服务并且选择不同的传输协议和传输层而不要重新生成代码。
Thrift 服务器包含用于绑定协议和传输层的基础架构,它提供阻塞、非阻塞、单线程和多线程的模式运行在服务器上,可以配合服务器 / 容器一起运行,可以和现有的 J2EE 服务器 / Web 容器无缝的结合。
5 数据类型
Thrift 脚本可定义的数据类型包括以下几种类型:基本类型、结构体类型、容器类型、异常类型、服务类型。
6 传输协议
Thrift 可以让用户选择客户端和服务端之间传输通信协议的类别,在传输协议上总体划分为文本和二进制传输协议。
常用协议有以下几种:
- TBinaryProtocol:二进制编码格式进行数据传输
- TCompactProtocol:高效率的、密集的二进制编码格式进行数据传输
- TJSONProtocol:使用 JSON 的数据编码协议进行数据传输
- TSimpleJSONProtocol:只提供 JSON 只写的协议,适用于通过脚本语言解析
7 传输层
常用的传输层有以下几种:
- TSocket:使用阻塞式 I/O 进行传输,是最常见的模式
- TFramedTransport:使用非阻塞方式,按块的大小进行传输,类似于 Java 中的 NIO
- TNonblockingTransport:使用非阻塞方式,用于构建异步客户端
8 服务端类型
常见的服务端类型有以下几种:
- TSimpleServer:单线程服务器端使用标准的阻塞式 I/O
- TThreadPoolServer:多线程服务器端使用标准的阻塞式 I/O
- TNonblockingServer:多线程服务器端使用非阻塞式 I/O
9 代码实例
IDL文件
namespace cpp drgs
namespace d drgs // "shared" would collide with the eponymous D keyword.
namespace dart drgs
namespace java drgs
namespace perl drgs
namespace php drgs
namespace haxe drgs
// namespace netcore drgs
// 参数
struct MedicalRecord {
1: string id,
2: string username,
3: string jbdm,
4: double zfy,
5: i16 nl,
6: string xb,
7: i16 sjzyts,
8: string lyfs,
9: string bah,
10: string zyzd
}
struct Param {
1: string data_type,
2: string icd_standard,
3: string with_name,
4: list<MedicalRecord> data
}
// 返回值
struct ResultData {
1: string id,
2: string drg,
3: string mdc,
4: optional string error,
5: string mdc_name,
6: string drg_name;
}
struct Result {
1: string success,
2: list<ResultData> data
}
service DRGsService {
Result getDRG(1: Param params);
}
Server 端
# -*- coding: utf-8 -*-
import urllib3
import json
from gen.drgs import DRGsService
from gen.drgs.ttypes import *
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class DRGsServiceImpl:
def __init__(self):
pass
def getDRG(self, params):
"""
:param params:
:return:
"""
# 替换成查询 DRGs 信息的逻辑代码
medical_records = []
for key, value in enumerate(params.data):
medical_record = {
'id': value.id,
'username': value.username,
'zfy': value.zfy,
'nl': value.nl,
'xb': value.xb,
'sjzyts': value.sjzyts,
'lyfs': value.lyfs,
'jbdm': value.jbdm,
'bah': value.bah,
'zyzd': value.zyzd
}
medical_records.append(medical_record)
fields = {
'data_type': params.data_type,
'icd_standard': params.icd_standard,
'with_name': params.with_name,
'data': json.dumps(medical_records)
}
print fields
url = 'http://test/'
http = urllib3.PoolManager()
result = http.request('POST', url, fields=fields)
result = self.translateResult(result)
print result
return result
def translateResult(self, resultStr):
result_obj = json.loads(resultStr.data.decode('utf-8'))
results = result_obj['result']
result_datas = []
for i, v in enumerate(results):
error = ''
mdc_name = ''
drg_name = ''
if v.has_key('error'):
error = v['error']
if v.has_key('mdc_name'):
mdc_name = v['mdc_name']
if v.has_key('drg_name'):
drg_name = v['drg_name']
result_data = ResultData(v['id'], v['drg'], v['mdc'], error, mdc_name, drg_name)
result_datas.append(result_data)
result = Result(str(result_obj['success']), result_datas)
return result
if __name__ == '__main__':
handler = DRGsServiceImpl()
# exit(handler.getDRG({}))
processor = DRGsService.Processor(handler)
transport = TSocket.TServerSocket("0.0.0.0", 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.')
client端
# -*- coding: utf-8 -*-
from gen.drgs import DRGsService
from thrift import Thrift
from thrift.transport import TSocket
from thrift.protocol import TBinaryProtocol
from gen.drgs.ttypes import *
try:
# 建立socket
transport = TSocket.TSocket('192.168.150.253', 9090)
# 选择传输层,这块要和服务端的设置一致
transport = TTransport.TBufferedTransport(transport)
# 选择传输协议,这个也要和服务端保持一致,否则无法通信
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# 创建客户端
client = DRGsService.Client(protocol)
transport.open()
dataType = "N041"
icdStandard = "CN-1.x"
withName = "1"
medical_records = []
medical_record = MedicalRecord("1", "34344", "E11.901", 2394.88, 55, "男", 14, "1", "0", "II型糖尿病[非胰岛素依赖型糖尿病")
medical_records.append(medical_record)
param = Param(dataType, icdStandard, withName, medical_records)
result = client.getDRG(param)
print result
# 关闭传输
transport.close()
# 捕获异常
except Thrift.TException, ex:
print "%s" % (ex.message)