Thrift 的使用

疯狂 Get 新技能 ing

安装 Thrift

  • 一般情况下,需要用 apt 来安装
  • Conda 安装的话只有 Python 相关的模块,C++用不了

写一个 thrift 文件

比如,叫做 hello.thrift

service DemoService{
    i32 add(1: i32 a, 2: i32 b),
    string say(1: string data),
    string ping()
}

这个例子只有服务。除此之外,还可以有数据结构定义。

生成对应的接口

Python

thrift --gen py hello.thrift

会生成 gen-py  文件夹,将里面的东西作为一个包拿出来用,或者是在代码里面将这个路径加入 path

import path
sys.path.append('gen-py')

C++(CMakefiles 文件要自己写啊~我不会)

thrift --gen cpp hello.thrift

如果有其他什么免写 CMakefiles 文件的方法……请告诉我!

使用

Python

客户端

import sys
import glob

from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol

from thrift_py.hello import DemoService

def main():
    # Make socket
    transport = TSocket.TSocket('localhost', 4134)

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

    # Wrap in a protocol
    protocol = TBinaryProtocol.TBinaryProtocol(transport)

    # Create a client to use the protocol encoder
    client = DemoService.Client(protocol)

    transport.open()

    print(client.ping())
    print('1+100=%d' % client.add(1, 100))
    
    transport.close()


if __name__ == '__main__':
    try:
        main()
    except Thrift.TException as tx:
        print('%s' % tx.message)

服务端有自动生成的代码,可以直接改改用,将里面几个业务函数(add/ping/say)补完整即可

C++

服务端,自动生成的代码没什么坑,只是不知道该怎么把声明和实现分开。

#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/server/TSimpleServer.h>
#include <thrift/transport/TBufferTransports.h>
#include <thrift/transport/TServerSocket.h>
#include <iostream>
#include "thrift/DemoService.h"

using namespace ::apache::thrift;
using namespace ::apache::thrift::protocol;
using namespace ::apache::thrift::transport;
using namespace ::apache::thrift::server;

using boost::shared_ptr;

class DemoServiceHandler : virtual public DemoServiceIf {
 public:
  DemoServiceHandler() = default;

  int32_t add(const int32_t a, const int32_t b) {
    // Your implementation goes here
    std::cout << "Add: " << a << "+" << b << std::endl;
    return a + b;
  }

  void say(std::string &_return, const std::string &data) {
    // Your implementation goes here
    std::cout << "Say: " << data << std::endl;
    _return = "You said:" + data;
  }

  void ping(std::string &_return) {
    // Your implementation goes here
    std::cout << "Ping" << std::endl;
    _return = "Pong";
  }
};

int main(int argc, char **argv) {
  int port = 4134;
  shared_ptr<DemoServiceHandler> handler(new DemoServiceHandler());
  shared_ptr<TProcessor> processor(new DemoServiceProcessor(handler));
  shared_ptr<TServerTransport> serverTransport(new TServerSocket(port));
  shared_ptr<TTransportFactory> transportFactory(
      new TBufferedTransportFactory());
  shared_ptr<TProtocolFactory> protocolFactory(new TBinaryProtocolFactory());

  TSimpleServer server(processor, serverTransport, transportFactory,
                       protocolFactory);
  std::cout << "Start server at port " << port << std::endl;
  server.serve();
  std::cout << "Done." << std::endl;

  return 0;
}

客户端,也直接用官方 Demo 吧:

#include <iostream>

#include <thrift/protocol/TBinaryProtocol.h>
#include <thrift/transport/TSocket.h>
#include <thrift/transport/TTransportUtils.h>

#include "thrift/DemoService.h"

using namespace apache::thrift;
using namespace apache::thrift::protocol;
using namespace apache::thrift::transport;

int main() {
  std::cout << "I'm Client" << std::endl;
  std::shared_ptr<TTransport> socket(new TSocket("localhost", 4134));
  std::shared_ptr<TTransport> transport(new TBufferedTransport(socket));
  std::shared_ptr<TProtocol> protocol(new TBinaryProtocol(transport));
  DemoServiceClient client(protocol);

  try {
    transport->open();

    std::string send = "Lalala";
    std::string received = "";

    std::cout << "ping()" << std::endl;
    client.ping(received);
    std::cout << received << std::endl;

    std::cout << "1 + 1 = " << client.add(1, 1) << std::endl;

    client.say(received, send);
    std::cout << received << std::endl;

    transport->close();
  } catch (TException& tx) {
    std::cout << "ERROR: " << tx.what() << std::endl;
  }

  return 0;
}

另外就是要注意一下参数的顺序和要求……比如,thrift 里返回为 string 的,在客户端这里都是 string&,需要先定义一个 string 再传进去,并且好像一般返回值是第一个参数。这一点比较不适应。

使用建议

用 C++写服务端(运行速度比较快)用 Python 写客户端(开发速度比较快)

为啥这样说呢?——gen 之后,默认生成的代码里面,cpp 给的是服务端代码,python 给的是客户端代码。——人家都替你想好啦~

使用建议 2

Python 的话,有个东西叫 ThriftPy2,可以直接加载 thrift idl 文件,而不用首先生成 Python 代码,而且可以一键生成 Client,甚是方便。

留下评论