Orbis is an Erlang library which helps writing applications that uses consistent hashing to distribute requests amongst a set of workers in a pool. The library provides no distribution amongst networked workers, but the library is a lot simpler to use, and get started with, than larger, more feature-rich and complex libraries like Riak Core.
The API of Orbis is greatly inspired by the fantastic Poolboy Erlang library.
-
Create a named pool using
orbis:child_spec/3
(ororbis:child_spec/4
), which allows you to specify a name of the worker pool, size of the worker pool, which must be a number of power of two (1, 2, 4, 8, ...), a worker module and an optional set of arguments passed to the worker'sstart_link
function. Pass the result of theorbis:child_spec
function to your supervisor. -
Implement your worker module with the
orbis_worker
behaviour. -
Use
orbis:dispatch/3
to distribute work amongst your workers.
Here's the interesting files of an example application using Orbis to distribute
requests amongst a set of gen_server
workers. You can see and download the
full example application here.
This module implements the API which distributes the events amongst our workers.
-module(orbis_example).
%% API.
-export([ping/1, crash/1]).
ping(Key) ->
dispatch(Key, fun (Worker) ->
orbis_example_worker:ping(Worker)
end).
crash(Key) ->
dispatch(Key, fun (Worker) ->
orbis_example_worker:crash(Worker)
end).
dispatch(Key, Fun) ->
orbis:dispatch(orbis_example_pool, Key, Fun).
This module implements the gen_server
which will handle our requests. It
exposes two API functions: ping/1
, which will return a pong
together with
some information about the server which handled the given request, and a
crash/1
which will simply crash the server handling the request.
-module(orbis_example_worker).
-behaviour(orbis_worker).
-behaviour(gen_server).
%% API.
-export([ping/1,
crash/1]).
%% Orbis Worker.
-export([start_link/1]).
-export([init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2,
code_change/3]).
-record(state, {
name :: orbis:pool(),
partition :: orbis_chash_bucket:partition()
}).
-define(SERVER, ?MODULE).
ping(Worker) ->
gen_server:call(Worker, ping).
crash(Worker) ->
gen_server:cast(Worker, crash).
start_link(Arguments) ->
gen_server:start_link(?SERVER, Arguments, []).
init([Name, Partition | _Arguments]) ->
{ok, #state { name = Name, partition = Partition }}.
handle_call(ping, _From, #state { partition = Partition } = State) ->
{reply, {pong, #{ partition => Partition, worker => self() }}, State};
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
handle_cast(crash, State) ->
erlang:error(crash),
{noreply, State};
handle_cast(_Message, State) ->
{noreply, State}.
handle_info(_Info, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.
code_change(_OldVersion, State, _Extra) ->
{ok, State}.
This module implements the supervisor which managers the workers. The call to
orbis:child_spec/3
(or orbis:child_spec/4
) will initialize the state needed
for the Orbis pool and will return a child spec which we pass to the supervisor,
which will take care of starting and restarting our workers.
-module(orbis_example_sup).
-behaviour(supervisor).
-export([start_link/0]).
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
%% Start a pool named orbis_example_pool with 64 workers and use
%% orbis_example_worker as worker.
Procs = orbis:child_spec(orbis_example_pool, 64, orbis_example_worker),
{ok, {{one_for_one, 10, 60}, Procs}}.
-
Orbis Example Application: A simple example application for Orbis.
-
Orbis UUID: A UUID Generator that uses Orbis.