Awesome
Hello
Hello is a pragmatic RPC toolkit for Erlang. It enables you to rapidly create rich RPC services. Hello is also built for extensibility, allowing new transport and RPC protocol implementations.
Out of the box, it supports the JSON-RPC 2.0 protocol over HTTP and ZeroMQ transports.
Building and installing
Use rebar for building:
rebar get-deps compile
You can use hello in your own project:
{deps, [
{hello, ".*", {git, "https://github.com/travelping/hello.git", "hello_v3"}}
]}.
RPC Server Model
Bindings
RPC endpoints are called bindings. A binding represents a mapping from a unique transport URL to a handler module.
Example:
hello:bind("http://127.0.0.1:8080", handler_module),
Listeners
A listener usually wraps a network socket (e.g. a TCP acceptor). Listeners are created on demand when the first binding that references them is created. It is also automatically removed if no bindings exists that could use it.
Quick example, starting HTTP listener like this:
hello:start_listener("http://127.0.0.1:8080").
You can customize listener:
TransportOpts = [],
Protocol = hello_proto_jsonrpc,
ProtocolOpts = [],
Router = hello_router,
hello:start_listener("http://127.0.0.1:8080", TransportOpts, Protocol, ProtocolOpts, Router).
will start an HTTP server automatically.
Routes
You can implement your own router (see hello_router). For this you have to write module with function:
route(content(), request(), #ex_uri{}) ->
{error, Reason :: term()} |
{ok, ServiceName :: binary(), Id : term()}.
Handlers
Hello handlers are Erlang modules that implement an RPC service. A handler can be started for every peer resulting in a 1-to-1 mapping. This mechanism relies on transport specific implementations like the ZeroMQ peer identit. If started without that option the handler is available for N clients.
There is hello_handler:
- sync replies
- async replies
- is stateless (or statefull if you use register handler)
- a state to hold a rpc session
- receiving messages from other processes of the same node
- full jsonrpc support (e.g. two sided communication)
- covers the most 'usecases'
Client
Hello also contains an RPC client which is also capable of handling different rpc protocols. Out of the box it supports jsonrpc as well. Connecting to a server can be as simple as:
TransportOpts = [],
ProtocolOpts = [],
ClientOpts = [],
{ok, Client} = hello_client:start_link("http://127.0.0.1:8080/example", TransportOpts, ProtocolOpts, ClientOpts),
hello_client:call(Client, {<<"my_method">>, [1, 2, <<"argument">>], []}).
You can start client with keep alive support:
ClientOpts = [{keep_alive_interval, 3000}],
{ok, Client} = hello_client:start_link("http://127.0.0.1:8080/example", [], [], ClientOpts).
Logging
Hello logs with lager on level 'debug' and 'info'. There is a strong tracing support, e.g. traces like {class, hello}, {hello_request_method, some_method} or {hello_request_status, ok | error} are available (see hello_log.hrl for more).
To write all bad request to a file use:
{lager, [
{handlers, [
{lager_file_backend, [{file, "bad_request.log"}, {level, info}]}
]},
{traces, [
{{lager_file_backend, "bad_request.log"}, [{hello_request_status, error}], info}
]}
]}
To write all requests for a module hello_handler_example to a file use:
{lager, [
{handlers, [
{lager_file_backend, [{file, "hello_handler_example.log"}, {level, none}]}
]},
{traces, [
{{lager_file_backend, "hello_handler_example.log"}, [{hello_handler_callback, hello_handler_example}], info}
]}
]}
Metrics
Hello collects metrics via exometer. The following metrics are considered:
* Request counter and request handle time in ms (listener, handler, client) grouped by
the types total, success, error and internal (for e.g. pings). For the
client also timeouts are available. The handle times for listeners and handlers
reflect the execution time of the callback modules.
* Last request time in ms (listener, handler, client)
* Last reset time in ms to see (re-) starts (listener, handler)
* uptime since last reset in ms (listener)
* packet in/out counter (listener)
* packet in/out size in bytes (listener)
Exometer entry types (see include/hello_metrics.hrl for definitions):
* Request counter/timeout and packet in/out counter: counter
* Request handle time and packet size: histogram
* Last request and last reset: gauge
* Uptime: function
All histograms have a time span of 60s.
Use exometer_report:list_metrics([hello])
to see a full list of the exometer IDs.
For listeners and clients the IDs are instantly visible after start. However, due to the dynamic handling
of callback modules the IDs for handlers are only visible after the first call.
Elixir
You can use hello with Elixir environment via Hello
and Hello.Client
modules which are delegating :hello
and :hello_client
For Developers
If you want to implement server on another framework you have to know two things about protocols:
- JSON RPC has signature:
- For HTTP it is
application/json
in Content-Type header. - For ZMTP it is 0xAA, 0xFF as second frame (first frame should be empty).
- For HTTP it is
- Hello has keep alive mechanism via ping-pong messaging:
- Client send
$PING
message and server reply with$PONG
message. - HTTP signature for those messages is
application/octet-stream
in Content-Type header. - For ZMTP is 0xAA, 0xAA as second frame (first frame should be empty).
- Client send