Thrift允许开发者在一个单独的语言无关的文件里,定义数据类型和服务接口(thrift文件),然后生成用来构建RPC客户和服务器所需的全部代码。
网络环境下,跨语言交互的一些关键组件:
- 类型(Types)——需要一种通用的类型系统。类型的转换上,编程者不需编写任何应用层以下的代码。
- 传输(Transport)——各个语言必须有一种双向传输原始数据的通用接口。一个给定的传输是如何实现的,应该与服务开发者无关。
- 协议(Protocol)——数据类型必须有某种方法,来使用传输层对它们自身编码和解码。同样地,应用开发者不需要关心该层。重要的是,数据能够以一种一致的、确定的方式被读写。
- 版本化(Versioning)——健壮的服务相关的数据类型必须提供一种自身版本化的机制。具体来说,它应当能在一个对象中添加或移除域,或改变一个函数的参数列表,而不干扰服务。
- 处理器(Processors)——最后,我们生成能够处理数据流以实现远程过程调用的代码。
thrift的类型
1)基本类型:不支持无符号类型
- byte: 有符号字节
- i16: 16位有符号整数
- i32: 32位有符号整数
- i64: 64位有符号整数
- double: 64位浮点数
- string: 字符串
2)容器类型:集合中的元素可以是除了service之外的任何类型,包括exception
- list
: 一系列由T类型的数据组成的有序列表,元素可以重复。直接翻译为STL vector,Java ArrayList,或脚本语言中的native array。 - set
: 一系列由T类型的数据组成的无序集合,元素不可重复。翻译为STL set,Java HashSet,Python中的set,或PHP/Ruby中的native dictionary。 - map
: 一个字典结构,严格唯一的键(keys)到值(values)的映射。翻译为STL map,Java HashMap,PHP associative array,或Python/Ruby dictionary。 3)结构体(struct)
struct DocContent{
1:string content,
2:string title,
}4)枚举(enum)
enum DocContent{
MALE,
FEMALE
}5)异常(exception)
- 异常在语法和功能上都与结构体相同,生成的对象继承自各目标编程语言中适当的异常基类,以便与任何给定语言中的本地异常处理无缝地整合。
exception RequestException{
1: i32 code;
2: string reason;
}6)常量(const)
const i32 MAX_RETRIES_TIME = 10
7)可选与必选
struct People {
1: required string name;
2: optional i32 age;
}8)服务(Services)
- 对一个服务的定义在语法上等同于在面向对象编程中定义一个接口(或一个纯虚抽象类)。Thrift编译器生成实现该接口的客户与服务器存根。
service GetKeywordsString {
KeywordsResult get_keywords(1: DocContent doc),
string get(1:i32 key) throws (1:KeyNotFound knf),
}注意:
- 除其他所有定义的Thrift类型外,void也是一个有效的函数返回类型。Void函数可添加一个async修饰符,产生的代码不等待服务器的应答。一个纯void函数会向客户端返回一个应答,保证服务器一侧操作已完成。应用开发者应小心,仅当方法调用失败是可以接受的,或传输层已知可靠的情况下,才使用async优化。
keywords_res.thrift
// input
struct DocContent{
1:string content,
2:string title,
}
// output
struct KeywordsResult {
1:string keywords,
}
// service
service GetKeywordsString {
// 方法
KeywordsResult get_keywords(1: DocContent doc),
}
我们这里分别生成C++、python,会生成两个文件夹gen-cpp和gen-py
thrift -r --gen cpp student.thrift //生成python代码
thrift -r --gen py student.thrift //生成python代码
#thrift -r --gen java student.thrift //生成java代码
gen-py(还没仔细研究)
gen-cpp
GetKeywordsString.h:接口的基类get_keywords接口在其中声明,具体的实现从该类来派生实现;
keywords_res_types.h:接口中用的数据类型,DocContent和KeywordsResult就在这个文件中定义;
python:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
sys.path.append("./gen-py/")
from keywords_res import GetKeywordsString
from keywords_res.ttypes import DocContent, KeywordsResult
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from thrift.server import TServer
class GetKeywordsServe:
def __init__(self):
self.log = {}
def get_keywords(self, doc):
str = doc.title + '\t' + doc.content
return KeywordsResult(str)
if __name__ == '__main__':
# 创建服务端
handler = GetKeywordsServe()
processor = GetKeywordsString.Processor(handler)
# 监听端口
transport = TSocket.TServerSocket(host = "0.0.0.0" ,port=10000)
# 选择传输层
tfactory = TTransport.TBufferedTransportFactory()
# 选择传输协议
pfactory = TBinaryProtocol.TBinaryProtocolFactory()
# 创建服务端
server = TServer.TThreadPoolServer(processor, transport, tfactory, pfactory)
# 设置连接线程池数量
server.setNumThreads(10)
# 启动服务
server.serve()
C++:根据生成文件夹gen-cpp里的GetKeywordsString_server.skeleton.cpp改写的。
1)在server中派生一个类来实现具体的接口:
class GetKeywordsStringHandler : virtual public GetKeywordsStringIf{};
GetKeywordsStringIf 在自动生成的文件GetKeywordsString.h中声明,GetKeywordsStringHandler 类会具体完成方法get_keywords的实现。
2)实现server
TProcessor:作为协议层和用户服务实现之间的纽带,负责服务实现的接口,例如:
shared_ptr
shared_ptr
其中类GetKeywordsStringProcessor也是在GetKeywordsString.h中定义的.
TServerTransport:实现RPC的传输,传输层的实现包括:socket,非阻塞的socket,MemoryBuffer等
shared_ptr
TTransportFactory
TProtocolFactory:制定传输的类型,可以是JSON,Binary等类型,例如:
shared_ptr
// This autogenerated skeleton file illustrates how to build a server.
// You should copy it to another filename to avoid overwriting it.
#include "util/global_init/global_init.h"
#include
#include
#include "util/keywords/proto_1/gen-cpp/GetKeywordsString.h"
#include "third_party/thrift/include/protocol/TBinaryProtocol.h"
#include "third_party/thrift/include/server/TSimpleServer.h"
// #include "third_party/thrift/include/server/TNonblockingServer.h"
#include "third_party/thrift/include/transport/TSocket.h"
#include "third_party/thrift/include/transport/TServerSocket.h"
#include "third_party/thrift/include/transport/TBufferTransports.h"
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;
using boost::shared_ptr;
using namespace std;
class GetKeywordsStringHandler : virtual public GetKeywordsStringIf {
public:
GetKeywordsStringHandler() {
// Your initialization goes here
}
void get_keywords(KeywordsResult& _return, const DocContent& doc) {
// Your implementation goes here
const string content = doc.content;
const string title = doc.title;
string result;
result = title;
result += "+=+";
result += content;
_return.keywords = result;
printf("get_keywords\n");
}
};
int main(int argc, char **argv) {
util::GlobalInit global_init(&argc, &argv);
int port = 9090;
shared_ptr handler(new GetKeywordsStringHandler());
shared_ptr processor(new GetKeywordsStringProcessor(handler));
shared_ptr serverTransport(new TServerSocket(port));
shared_ptr transportFactory(new TBufferedTransportFactory());
shared_ptr protocolFactory(new TBinaryProtocolFactory());
// 创建server对象
TSimpleServer server(processor, serverTransport, transportFactory, protocolFactory);
// 启动server
server.serve();
return 0;
}
python:
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import sys
sys.path.append("./gen-py/")
from keywords_res import GetKeywordsString
from keywords_res.ttypes import DocContent, KeywordsResult
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
if __name__ == '__main__':
transport = TSocket.TSocket('127.0.0.1', 10000)
transport = TTransport.TBufferedTransport(transport)
protocol = TBinaryProtocol.TBinaryProtocol(transport)
client = GetKeywordsString.Client(protocol)
# connect
transport.open()
#DocContent param
demo_obj = client.get_keywords(DocContent("cont", "totle"))
print '%s' % (demo_obj.keywords)
# close
transport.close()
C++:
#include "util/global_init/global_init.h"
#include
#include
#include "util/keywords/proto_1/gen-cpp/GetKeywordsString.h"
#include "third_party/thrift/include/protocol/TBinaryProtocol.h"
#include "third_party/thrift/include/transport/TSocket.h"
#include "third_party/thrift/include/transport/TBufferTransports.h"
using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using boost::shared_ptr;
using namespace std;
DEFINE_int32(server_port, 9090, "");
DEFINE_string(host_name, "localhost", "");
int main(int argc, char **argv) {
util::GlobalInit global_init(&argc, &argv);
shared_ptr socket(new TSocket(FLAGS_host_name, FLAGS_server_port));
// shared_ptr transport(new TFramedTransport(socket));
shared_ptr transport(new TBufferedTransport(socket));
shared_ptr protocol(new TBinaryProtocol(transport));
// client实例创建
GetKeywordsStringClient client(protocol);
transport->open();
DocContent doc;
doc.content = "cont";
doc.title = "title";
KeywordsResult result;
client.get_keywords(result, doc);
std::cout << "input " << result.keywords << std::endl;
std::cout << std::endl;
transport->close();
return 0;
}
传输协议、serve服务的选择等后续整理
????
TSimpleServer : 一次只服务一个连接
TNonblockingServer: 单线程处理多个连接
THshaServer: 用一个单独的线程处理网络 I/O,一个独立的 work 线程池来处理消息
TThreadedSelectorServer:维护 2 个线程池,一个用来处理网络 I/O,一个用来处理消息
TThreadPoolServer:一旦接受了一个连接,即开启一个线程,直到关闭。对系统资源消耗不太友好
https://www.jianshu.com/p/0f4113d6ec4b
https://www.cnblogs.com/zhaoxd07/p/5387215.html
https://www.cnblogs.com/lpxblog/p/5384891.html