Thrift
Введение | |
Установка | |
Пример | |
Похожие статьи |
Введение
Thrift (с англ. — «бережливость», произносится как [θrift]) —
язык описания интерфейсов
, который используется для определения и создания служб под
разные языки программирования.
Является фреймворком к удалённому вызову процедур (RPC). Используется компанией Facebook в качестве масштабируемого кросс-языкового сервиса по разработке.
Сочетает в себе программный конвейер с движком генерации кода для разработки служб, в той или иной степени эффективно и легко работающих между такими языками как
C#
,
C++
, Cappuccino, Cocoa, Delphi, Erlang,
Go
, Haskell,
Java
, OCaml, Perl,
PHP
,
Python
,
Ruby
, Rust, Smalltalk и
JavaScript
.
Проще говоря, Thrift является двоичным протоколом связи. С апреля 2007 разрабатывается как open source проект некоммерческой организации Apache Software Foundation.
РЕКЛАМА от Яндекса. Может быть недоступна в вашем регионе
Конец рекламы. Если там пусто считайте это рекламой моей телеги
Установка
Про установку в Windows читайте здесь
PowerShell скрипт для скачивания thrift.exe
$THRIFT_URL = "https://dlcdn.apache.org/thrift/0.19.0/thrift-0.19.0.exe" $FilePath = ".\thrift.exe" If (Test-Path -path $FilePath -PathType Leaf) { Write-Host "thrift.exe file exists" -f Green } Else { Write-Host "thrift.exe file does not exist - starting download" -f Yellow Invoke-WebRequest $THRIFT_URL -OutFile thrift.exe }
Пример
Предположим, что под Windows с официального сайта скачан и помещён в
C:\DEV\Thrift
.exe файл, например thrift-0.19.0.exe
Также заранее установлен
Python
, например, по
этой
инструкции.
Из
документации
к Thrift берем пример
multiplication.thrift
убрав для простоты часть про
Java
namespace py tutorial /* C like comments are supported */ // This is also a valid comment typedef i32 int // We can use typedef to get pretty names for the types we are using service MultiplicationService { int multiply(1:int n1, 2:int n2), }
Открываем консоль в директории с multiplication.thrift и выполняем
C:\DEV\Thrift\thrift-0.19.0.exe --gen py .\multiplication.thrift
Сгенерируется директория gen-py со следующим содержимым
gen-py/ |-- __init__.py `-- tutorial |-- MultiplicationService-remote |-- MultiplicationService.py |-- __init__.py |-- constants.py `-- ttypes.py
Файл
__init__.py
в корне пустой
внутри tutorial
__all__ = ['ttypes', 'constants', 'MultiplicationService']
constants.py
# # Autogenerated by Thrift Compiler (0.19.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # # options string: py # from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException from thrift.protocol.TProtocol import TProtocolException from thrift.TRecursive import fix_spec import sys from .ttypes import *
MultiplicationService.py
# # Autogenerated by Thrift Compiler (0.19.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # # options string: py # from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException from thrift.protocol.TProtocol import TProtocolException from thrift.TRecursive import fix_spec import sys import logging from .ttypes import * from thrift.Thrift import TProcessor from thrift.transport import TTransport all_structs = [] class Iface(object): def multiply(self, n1, n2): """ Parameters: - n1 - n2 """ pass class Client(Iface): def __init__(self, iprot, oprot=None): self._iprot = self._oprot = iprot if oprot is not None: self._oprot = oprot self._seqid = 0 def multiply(self, n1, n2): """ Parameters: - n1 - n2 """ self.send_multiply(n1, n2) return self.recv_multiply() def send_multiply(self, n1, n2): self._oprot.writeMessageBegin('multiply', TMessageType.CALL, self._seqid) args = multiply_args() args.n1 = n1 args.n2 = n2 args.write(self._oprot) self._oprot.writeMessageEnd() self._oprot.trans.flush() def recv_multiply(self): iprot = self._iprot (fname, mtype, rseqid) = iprot.readMessageBegin() if mtype == TMessageType.EXCEPTION: x = TApplicationException() x.read(iprot) iprot.readMessageEnd() raise x result = multiply_result() result.read(iprot) iprot.readMessageEnd() if result.success is not None: return result.success raise TApplicationException(TApplicationException.MISSING_RESULT, "multiply failed: unknown result") class Processor(Iface, TProcessor): def __init__(self, handler): self._handler = handler self._processMap = {} self._processMap["multiply"] = Processor.process_multiply self._on_message_begin = None def on_message_begin(self, func): self._on_message_begin = func def process(self, iprot, oprot): (name, type, seqid) = iprot.readMessageBegin() if self._on_message_begin: self._on_message_begin(name, type, seqid) if name not in self._processMap: iprot.skip(TType.STRUCT) iprot.readMessageEnd() x = TApplicationException(TApplicationException.UNKNOWN_METHOD, 'Unknown function %s' % (name)) oprot.writeMessageBegin(name, TMessageType.EXCEPTION, seqid) x.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() return else: self._processMap[name](self, seqid, iprot, oprot) return True def process_multiply(self, seqid, iprot, oprot): args = multiply_args() args.read(iprot) iprot.readMessageEnd() result = multiply_result() try: result.success = self._handler.multiply(args.n1, args.n2) msg_type = TMessageType.REPLY except TTransport.TTransportException: raise except TApplicationException as ex: logging.exception('TApplication exception in handler') msg_type = TMessageType.EXCEPTION result = ex except Exception: logging.exception('Unexpected exception in handler') msg_type = TMessageType.EXCEPTION result = TApplicationException(TApplicationException.INTERNAL_ERROR, 'Internal error') oprot.writeMessageBegin("multiply", msg_type, seqid) result.write(oprot) oprot.writeMessageEnd() oprot.trans.flush() # HELPER FUNCTIONS AND STRUCTURES class multiply_args(object): """ Attributes: - n1 - n2 """ def __init__(self, n1=None, n2=None,): self.n1 = n1 self.n2 = n2 def read(self, iprot): if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) return iprot.readStructBegin() while True: (fname, ftype, fid) = iprot.readFieldBegin() if ftype == TType.STOP: break if fid == 1: if ftype == TType.I32: self.n1 = iprot.readI32() else: iprot.skip(ftype) elif fid == 2: if ftype == TType.I32: self.n2 = iprot.readI32() else: iprot.skip(ftype) else: iprot.skip(ftype) iprot.readFieldEnd() iprot.readStructEnd() def write(self, oprot): if oprot._fast_encode is not None and self.thrift_spec is not None: oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) return oprot.writeStructBegin('multiply_args') if self.n1 is not None: oprot.writeFieldBegin('n1', TType.I32, 1) oprot.writeI32(self.n1) oprot.writeFieldEnd() if self.n2 is not None: oprot.writeFieldBegin('n2', TType.I32, 2) oprot.writeI32(self.n2) oprot.writeFieldEnd() oprot.writeFieldStop() oprot.writeStructEnd() def validate(self): return def __repr__(self): L = ['%s=%r' % (key, value) for key, value in self.__dict__.items()] return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) def __eq__(self, other): return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ def __ne__(self, other): return not (self == other) all_structs.append(multiply_args) multiply_args.thrift_spec = ( None, # 0 (1, TType.I32, 'n1', None, None, ), # 1 (2, TType.I32, 'n2', None, None, ), # 2 ) class multiply_result(object): """ Attributes: - success """ def __init__(self, success=None,): self.success = success def read(self, iprot): if iprot._fast_decode is not None and isinstance(iprot.trans, TTransport.CReadableTransport) and self.thrift_spec is not None: iprot._fast_decode(self, iprot, [self.__class__, self.thrift_spec]) return iprot.readStructBegin() while True: (fname, ftype, fid) = iprot.readFieldBegin() if ftype == TType.STOP: break if fid == 0: if ftype == TType.I32: self.success = iprot.readI32() else: iprot.skip(ftype) else: iprot.skip(ftype) iprot.readFieldEnd() iprot.readStructEnd() def write(self, oprot): if oprot._fast_encode is not None and self.thrift_spec is not None: oprot.trans.write(oprot._fast_encode(self, [self.__class__, self.thrift_spec])) return oprot.writeStructBegin('multiply_result') if self.success is not None: oprot.writeFieldBegin('success', TType.I32, 0) oprot.writeI32(self.success) oprot.writeFieldEnd() oprot.writeFieldStop() oprot.writeStructEnd() def validate(self): return def __repr__(self): L = ['%s=%r' % (key, value) for key, value in self.__dict__.items()] return '%s(%s)' % (self.__class__.__name__, ', '.join(L)) def __eq__(self, other): return isinstance(other, self.__class__) and self.__dict__ == other.__dict__ def __ne__(self, other): return not (self == other) all_structs.append(multiply_result) multiply_result.thrift_spec = ( (0, TType.I32, 'success', None, None, ), # 0 ) fix_spec(all_structs) del all_structs
MultiplicationService-remote.py
#!/usr/bin/env python # # Autogenerated by Thrift Compiler (0.19.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # # options string: py # import sys import pprint if sys.version_info[0] > 2: from urllib.parse import urlparse else: from urlparse import urlparse from thrift.transport import TTransport, TSocket, TSSLSocket, THttpClient from thrift.protocol.TBinaryProtocol import TBinaryProtocol from tutorial import MultiplicationService from tutorial.ttypes import * if len(sys.argv) <= 1 or sys.argv[1] == '--help': print('') print('Usage: ' + sys.argv[0] + ' [-h host[:port]] [-u url] [-f[ramed]] [-s[sl]] [-novalidate] [-ca_certs certs] [-keyfile keyfile] [-certfile certfile] function [arg1 [arg2...]]') print('') print('Functions:') print(' int multiply(int n1, int n2)') print('') sys.exit(0) pp = pprint.PrettyPrinter(indent=2) host = 'localhost' port = 9090 uri = '' framed = False ssl = False validate = True ca_certs = None keyfile = None certfile = None http = False argi = 1 if sys.argv[argi] == '-h': parts = sys.argv[argi + 1].split(':') host = parts[0] if len(parts) > 1: port = int(parts[1]) argi += 2 if sys.argv[argi] == '-u': url = urlparse(sys.argv[argi + 1]) parts = url[1].split(':') host = parts[0] if len(parts) > 1: port = int(parts[1]) else: port = 80 uri = url[2] if url[4]: uri += '?%s' % url[4] http = True argi += 2 if sys.argv[argi] == '-f' or sys.argv[argi] == '-framed': framed = True argi += 1 if sys.argv[argi] == '-s' or sys.argv[argi] == '-ssl': ssl = True argi += 1 if sys.argv[argi] == '-novalidate': validate = False argi += 1 if sys.argv[argi] == '-ca_certs': ca_certs = sys.argv[argi+1] argi += 2 if sys.argv[argi] == '-keyfile': keyfile = sys.argv[argi+1] argi += 2 if sys.argv[argi] == '-certfile': certfile = sys.argv[argi+1] argi += 2 cmd = sys.argv[argi] args = sys.argv[argi + 1:] if http: transport = THttpClient.THttpClient(host, port, uri) else: if ssl: socket = TSSLSocket.TSSLSocket(host, port, validate=validate, ca_certs=ca_certs, keyfile=keyfile, certfile=certfile) else: socket = TSocket.TSocket(host, port) if framed: transport = TTransport.TFramedTransport(socket) else: transport = TTransport.TBufferedTransport(socket) protocol = TBinaryProtocol(transport) client = MultiplicationService.Client(protocol) transport.open() if cmd == 'multiply': if len(args) != 2: print('multiply requires 2 args') sys.exit(1) pp.pprint(client.multiply(eval(args[0]), eval(args[1]),)) else: print('Unrecognized method %s' % cmd) sys.exit(1) transport.close()
ttypes.py
# # Autogenerated by Thrift Compiler (0.19.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # # options string: py # from thrift.Thrift import TType, TMessageType, TFrozenDict, TException, TApplicationException from thrift.protocol.TProtocol import TProtocolException from thrift.TRecursive import fix_spec import sys from thrift.transport import TTransport all_structs = [] fix_spec(all_structs) del all_structs
Демо
hello.thrift
service HelloSvc { string hello_func() }
thrift --gen py hello.thrift
Проект будет иметь следующую структуру
thrift_hello/ |-- gen-py | |-- __init__.py | `-- hello | |-- HelloSvc-remote | |-- HelloSvc.py | |-- __init__.py | |-- constants.py | `-- ttypes.py |-- hello.thrift |-- hello_client.py |-- hello_server.py |-- requirements.txt `-- venv
hello_server.py
import sys sys.path.append("gen-py") from hello import HelloSvc from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol from thrift.server import TServer class HelloHandler: def hello_func(self): print("(Server) Handling client request") return "Hello from the python server" handler = HelloHandler() proc = HelloSvc.Processor(handler) trans_svr = TSocket.TServerSocket(port=9090) trans_fac = TTransport.TBufferedTransportFactory() proto_fac = TBinaryProtocol.TBinaryProtocolFactory() server = TServer.TSimpleServer(proc, trans_svr, trans_fac, proto_fac) server.serve()
python hello_server.py
hello_client.py
import sys sys.path.append("gen-py") from hello import HelloSvc from thrift.transport import TSocket from thrift.transport import TTransport from thrift.protocol import TBinaryProtocol trans = TSocket.TSocket("localhost", 9090) trans = TTransport.TBufferedTransport(trans) proto = TBinaryProtocol.TBinaryProtocol(trans) client = HelloSvc.Client(proto) trans.open() msg = client.hello_func() print(f"(Client) received: {msg}") trans.close()
В другом терминале
python hello_client.py
(Client) received: Hello from the python server
В терминале сервера появится лог
(Server) Handling client request
Обмен трафиком через трифт протокол будет выглядеть примерно так (используется WireShark)

WireShark
Ошибки Thrift | |
Make |