SkillAgentSearch skills...

Erpc

An alternative RPC implementation for Erlang.

Install / Use

/learn @bet365/Erpc
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

erpc

Contents

Introduction

This application is intended to be a replacement for native RPC. It tries to mimic the native rpc API as far as possible. It allows unidirectional connections between nodes. Where bi-directional connections are required, they need to be configured explicitly. There is host level and node level ACLs configurable. It is possible to setup application specific connections between nodes. It supports multiple TCP/TLS connections per host and load balancing of traffic across nodes.

From a client perspective, there is no message passing involved to make an erpc call - only one ETS lookup. To receive the response there are two messages passed. One from the socket to the socket handling process, and one message from the socket handling process to the calling process.

On the server side, a separate process is spawned for each request received.

The following figure shows a load-balanced connection.

Load balanced erpc connection

The following figure shows a "traditional" point-to-point unidirectional erpc connection. Point-to-point erpc connection

Build and Test

Compile

make

Run tests

Common test suite

make test

Native RPC load test

make rpc_load_test

ERPC load test

make erpc_load_test

Dialyzer checks

make dialyze

Configuration

Example configuration is shown below. Copy the erpc section into your sys.config and edit as appropriate.

[{erpc, [
         %%
         %% Change to 'allow' if you want to  enable any node
         %% to connect into this node. Node level ACLs take precedence over
         %% host level ACLs
         %%
         {default_node_acl, deny},

         %%
         %% Change to 'allow' if you want nodes on any host
         %% to connect into this node
         %%
         {default_host_acl, deny}, 

         %% {Module, Function} to output trace messages from the erpc
         %% application. Remove this config item if you don't want any
         %% trace messages from this application. If a different
         %% {Module, Function} is configured it should be accept
         %% arguments in the same format as io:format/2
         {logger_mf, {io, format}},

         %%
         {client_config, [
                          {a@localhost, [
                                         {hosts, [{"127.0.0.1", 9090}]},
                                         {num_connections, 1} %% Optional config item. Defaults to 1
                                        ]
                          },
                          %%
                          %% This is an example of a load balanced endpoint. Calls such as
                          %% erpc:call('DB', Mod, Fun, Args) will be load balanced evenly across
                          %% all the connected hosts
                          %%
                          {'DB', [
                                        {hosts, [{"10.1.1.1", 9090},
                                                 {"10.2.2.2", 9090},
                                                 "10.3.3.3"  %% In this case, erpc assumes a port value of 9090
                                                ]
                                        }
                                 ]
                          },
                          {c@localhost, [
                                         {hosts, [{"127.0.0.1", 9092}]}
                                        ]
                          }
			 ]
         },
         {server_config, [
                          %% Default listen port is 9090
			  {listen_port, 9090},

                          %% Default transport is tcp. Supported values are 'tcp' or 'ssl'
                          {transport, tcp},

                          %% ACLs specified here take precedence over
                          %% the 'default_host_acl' setting
                          {host_acls, [
                                       {{127,0,0,1}, all}
                                      ]
                          },

			  {node_acls, [
                                       %% Processes running on
                                       %% b@localhost are allowed to
                                       %% invoke any combination of
                                       %% {M, F} on this node
                                       {b@localhost, all},

                                       %% Processes running on the
                                       %% node 'c@localhost' are only
                                       %% allowed to invoke the
                                       %% specified combination of
                                       %% {Module, Function} pairs
                                       {c@localhost, [{module_1, func_1}, 
                                                      {module_1, func_2}]}
                                      ]
			  }
                         ]
         }
        ]
 }].

Dependencies

None. Yay!

TODO

  1. Automeshing of nodes as an option
  2. Bi-directional connections as an option
  3. DONE - Monitoring of node connections (similar to erlang:monitor_node/2)
  4. SCTP support
  5. DONE - Support for async_call and nb_yield similar to native RPC

Example usage

1> application:ensure_all_started(erpc).
...
{ok,[sasl,erpc]}

2> erpc:connect('TEST_1', [{hosts, [{"localhost", 9091}, {"localhost", 9092}]}]).
...
ok

3> erpc:conn_status().
[{'TEST_1',true}]

4> erpc:incoming_conns().
[]

5> erpc:outgoing_conns().
[erpc_test_1@localhost]

6> erpc:call('TEST_1', calendar, local_time, []).
{{2016,5,17},{15,30,31}}

%% You can connect to the same node with a different connection name
7> erpc:connect('TEST_2', [{hosts, [{"localhost", 9091}, {"localhost", 9092}]}]).
ok

8> erpc:conn_status().                                                           
[{'TEST_2',true},{'TEST_1',true}]

%% It will report a unique list of erlang nodes it is connected to
9> erpc:outgoing_conns().                                                        
[erpc_test_1@localhost]

10> erpc:call('TEST_2', calendar, local_time, []).                                
{{2016,5,17},{15,32,27}}

11> erpc:multicall(['TEST_1', 'TEST_2'], calendar, local_time, []).
{
  %% Successful responses
  [{{2016,5,17},{15,32,52}},
   {{2016,5,17},{15,32,52}}],

  %% List of failed connections
  []
}

%% Setup a connection to a non-existent end point
12> erpc:connect('TEST_3', [{hosts, [{"localhost", 12345}]}]).                    
ok

%% conn_status() reports that connection to 'TEST_3' is down
13> erpc:conn_status().                                            
[{'TEST_3',false},{'TEST_2',true},{'TEST_1',true}]

14> erpc:multicall(['TEST_1', 'TEST_2', 'TEST_3'], calendar, local_time, []).
{
  %% Successful responses
  [{{2016,5,17},{15,34,17}},
   {{2016,5,17},{15,34,17}}],

  %% List of failed connections
  ['TEST_3']
}

15> erpc:cast('TEST_1', calendar, local_time, []).
ok

16> erpc:multicast(['TEST_1', 'TEST_3'], calendar, local_time, []).
ok

17> erpc:is_connection_up('TEST_1').
true

18> erpc:is_connection_up('TEST_3').
false

%% Here, erpc figures out the host as localhost and assumes the default port value of 9090
19> erpc:connect(erpc_test_1@localhost).
ok

20> erpc:conn_status().
[{erpc_test_1@localhost,true},
 {'TEST_3',false},
 {'TEST_2',true},
 {'TEST_1',true}]

21> erpc:disconnect(erpc_test_1@localhost).   
ok

22> erpc:conn_status().                    
[{'TEST_3',false},{'TEST_2',true},{'TEST_1',true}]

API

Types

-type conn_name()       :: atom().
-type node_name()       :: atom(). %% An Erlang style node name
-type num_connections() :: integer().

-type port_number()     :: pos_integer().
-type hostname()        :: string().
-type host_spec()       :: {hostname(), port_number()}
-type conn_option()     :: {hosts, [host_spec()]} | {num_connections, pos_integer()}.
-type conn_status()     :: boolean().

call/4

Same as call(Name, Module, Function, Args, 5000).

call/5

call(Name :: conn_name(), Module::atom(), Function::atom(), Args::list(), Timeout::pos_integer()) -> {badrpc, term()} | term().

cast/4

cast(Name :: conn_name(), Module::atom(), Function::atom(), Args::list()) -> ok | {badrpc, not_connected}.

Fire and forget.

multicast/4

multicast(Conns :: [conn_name()], Module::atom(), Function::atom(), Args::list()) -> true.

Fire and forget.

multicall/4

Same as multicall/5 invoked with a timeout of 5000 milliseconds.

multicall/5

multicall(Names :: [conn_name()], Module::atom(), Function::atom(), Args::list(), Timeout::pos_integer()) -> {ResL :: [term()], BadNodes :: [conn_name()]}.

Mimics the behaviour of rpc:multicall/5.

conn_status/0

conn_status() -> [{conn_name(), conn_status()}].

incoming_conns/0

incoming_conns() -> [node_name()].

The list of inbound connections from peer erlang nodes. The actual node names of the connected nodes are returned here.

outgoing_conns/0

outgoing_conns() -> [node_name()].

The list of outbound connections to peer erlang nodes. The actual node names of the connected nodes are returned here.

connect/1

connect(Node_name :: node_name()) -> ok.

Establish an erpc connection to the specified node using the following default settings. The host part (from node@host) is extracted from the node name, and a single TCP connection will be setup on port 9090 which is the default listen port. This API is useful for applications to setup connections on demand. Note that the erpc a

View on GitHub
GitHub Stars49
CategoryDevelopment
Updated4mo ago
Forks10

Languages

Erlang

Security Score

87/100

Audited on Nov 26, 2025

No findings