OX Thrift (ox-thrift
) is a reimplementation of the
Apache Thrift Erlang library with an
emphasis on speed of encoding and decoding. In its current
incarnation, it uses the structure definitions produced by the Apache
Thrift code generator. However it has the following differences from
the Apache Thrift Erlang library.
-
It supports only framed transport.
-
It gives up the ability to stream to or from the transport. Instead, the processor layer decodes the thrift message from a binary buffer and encodes to an iolist. This simplifies the implementation and avoids a lot of record modifications that are inefficient in Erlang.
-
The Thrift server uses the Ranch acceptor pool instead of rolling its own.
-
The
HandlerModule:handle_function(Function, Args)
interface expects the HandlerModule to take its arguments as a list instead of a tuple.
The OX Thrift client interface is provided by the ox_thrift_client
module. This module exports two functions, new
, and call
.
{ok, Client} = ox_thrift_client:new(SocketFun, Transport, Protocol, Service)
- SocketFun: A zero-arity function that returns a new passive-mode connection to the thrift server. The OX Thrift client will call this function to open the initial connection to the Thrift server, and to open a new connection whenever it encounters an error on the existing connection.
- Transport: A module that provides the transport layer, e.g.,
gen_tcp
. This module is expected to supplysend/2
,recv/2
, andclose/1
functions. - Protocol: A module that provides the Thrift protocol layer, e.g.,
ox_thrift_protocol_binary
. - Service: A module, produced by the Thrift IDL compiler from the service's Thrift definition, that provides the Service layer.
The following shows an example SocketFun for use with the gen_tcp
Transport.
make_get_socket (Host, Port) ->
fun () ->
case gen_tcp:connect(Host, Port, [ binary, {active, false} ]) of
{ok, Socket} -> Socket;
{error, Reason} -> error({connect, Host, Port, Reason})
end
end.
The OX Thrift library expects to be able to call
ok = Transport:send(Socket, IOData)
,{ok, Binary} = Transport:recv(Socket, Length)
, andok = Transport:close(Socket)
on this Socket.
{OClient, Result} = ox_thrift_client:call(IClient, Function, Args)
- IClient: An
ox_thrift_client
record, as produced by theox_thrift_client:new
call. - Function: An atom representing the function to call.
- Args: A list containing the arguments to Function.
The OX Thrift server interface is provided by the ox_thrift_server
module. This module is designed to be used with the
Ranch acceptor pool. Your
application needs to start Ranch, and then to register the
ox_thrift_server
module with Ranch as a protocol handler.
ranch:start_listener(?THRIFT_SERVICE_REF,
10, % Number of acceptors.
ranch_tcp,
[ {port, Port} % https://github.com/ninenines/ranch/blob/master/doc/src/manual/ranch_tcp.asciidoc
, {reuseaddr, true}
],
ox_thrift_server, % Ranch protocol module.
#ox_thrift_config{
service_module = service_thrift,
codec_module = ox_thrift_protocol_binary,
handler_module = ?MODULE}).
Your Thrift server must supply two functions, handle_function
and
handle_error
. These functions are defined by the ox_thrift_server
behaviour.
Unlike the Apache Thrift's handle_function
Erlang interface, OX
Thrift passes the Args parameter as a list instead of a tuple.
-behaviour(ox_thrift_server).
handle_function (Function, Args) when is_atom(Function), is_list(Args) ->
case apply(?MODULE, Function, Args) of
ok -> ok;
Reply -> {reply, Reply}
end.
handle_error(Function, Reason) ->
ok.
The handle_function
function may return ok
for a void function, or
{reply, Reply}
for a non-void function. In addition, the function
may request that the Thrift server close the client's connection after
returning the reply by returning {ok, close}
for a void function or
{reply, Reply, close}
for a non-void function.
If the Thrift function wants to return one of its declared exceptions,
E
, it may call {throw, E}
. The Thrift server will catch the
exception and return a message to the client, where the exception will
be re-thrown.
The OX Thrift server will optionally call a handle_stat
function in
a module that you define when you start the Ranch listener.
#ox_thrift_config{options = [ { stats_module, StatsModule } ]}
The interface for the stats-collection function is:
handle_stat (Function, Stat, Value) ->
ok.
The statistics currently collected are:
decode_time
: The call arguments decoding time in microseconds.encode_time
: The call reply encoding time in microseconds.connect_time
: The total connect time in microseconds.call_count
: The number of calls to the Thrift server.
These numbers were taken from benchmarks of a production SSRTB system.
Apache Thrift | OX Thrift | Speedup | |
---|---|---|---|
Encoding | 736 us | 171 us | 4.3 x |
Decoding | 1051 us | 250 us | 4.2 x |
See the message protocol documentation.
-
Anthony pointed me a talk by Péter Gömöri on The Fun Part of Writing a Thrift Codec, which describes his work to speed up Thrift encoding and decoding. Unfortunately, the code is not public. The author claims the following speedups:
Optimization Encoding Decoding Layer Squashing 8x 2x Generated Funs 16x 16x -
Erlang documentation on Constructing and Matching Binaries, which may be useful for optimization.
-
The Thrift paper, which describes the design and data encoding protocol for Thrift.