thrift框架C++和python实例

Thrift允许开发者在一个单独的语言无关的文件里,定义数据类型和服务接口(thrift文件),然后生成用来构建RPC客户和服务器所需的全部代码。

网络环境下,跨语言交互的一些关键组件:

  1. 类型(Types)——需要一种通用的类型系统。类型的转换上,编程者不需编写任何应用层以下的代码。
  2. 传输(Transport)——各个语言必须有一种双向传输原始数据的通用接口。一个给定的传输是如何实现的,应该与服务开发者无关。
  3. 协议(Protocol)——数据类型必须有某种方法,来使用传输层对它们自身编码和解码。同样地,应用开发者不需要关心该层。重要的是,数据能够以一种一致的、确定的方式被读写。
  4. 版本化(Versioning)——健壮的服务相关的数据类型必须提供一种自身版本化的机制。具体来说,它应当能在一个对象中添加或移除域,或改变一个函数的参数列表,而不干扰服务。
  5. 处理器(Processors)——最后,我们生成能够处理数据流以实现远程过程调用的代码。

1、定义thrift文件(一些用到的结构体和服务端口)

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),
}

 2、生成接口代码,可以生成不同的语言,C++、python、Java等

我们这里分别生成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

thrift框架C++和python实例_第1张图片

GetKeywordsString.h:接口的基类get_keywords接口在其中声明,具体的实现从该类来派生实现;

keywords_res_types.h:接口中用的数据类型,DocContent和KeywordsResult就在这个文件中定义;

3、服务端代码

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 handler(new GetKeywordsStringHandler());
  shared_ptr processor(new GetKeywordsStringProcessor(handler));

  其中类GetKeywordsStringProcessor也是在GetKeywordsString.h中定义的.

TServerTransport:实现RPC的传输,传输层的实现包括:socket,非阻塞的socket,MemoryBuffer等

   shared_ptr serverTransport(new TServerSocket(port));

TTransportFactory

TProtocolFactory:制定传输的类型,可以是JSON,Binary等类型,例如:

   shared_ptr protocolFactory(new TBinaryProtocolFactory());

// 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;
}

4、客户端代码

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;
}

5、启动服务和调用客户端结果

6、thrift一些知识的补充

传输协议、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

 

你可能感兴趣的:(thrift框架C++和python实例)