Archive

Posts Tagged ‘rpc’

REMOTE PROCEDURE CALLs: C vs. Erlang (part 2)

December 21, 2009 Leave a comment

In the previous post, I have told you how to make a counter server in C and then call its functionalities through RPC calls.

In Erlang it is defined a module called rpc. It provides services which are similar to remote procedure calls. It also contains broadcast facilities and parallel evaluators. As I told you also in the previous blog a remote procedure call is a method to call a function on a remote node and collect the answer.

The most important function in this module is rpc:call(Node, Module, Function, Arguments), it executes the specific function defined in the specific module, with the specified arguments on the node Node and returns the corresponding that can be either the return value, or a tupla of the form {badrpc, Reason}.

Sounds easy! Thus let’s start by coding our counter server:

%%%-------------------------------------------------------------------
%%% File    : counter_svr.erl
%%% Author  : XXXXX
%%% Description : A counter server
%%%
%%% Created : 19 Dec 2009 by XXXXX
%%%-------------------------------------------------------------------
-module(counter_svr).

-behaviour(gen_server).

%% API
-export([start_link/0,count/0]).

%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
	 terminate/2, code_change/3]).

-record(state, {counter=0}).

%%====================================================================
%% API
%%====================================================================
%%--------------------------------------------------------------------
%% Function: start_link() -> {ok,Pid} | ignore | {error,Error}
%% Description: Starts the server
%%--------------------------------------------------------------------
start_link() ->
    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

%%====================================================================
%% gen_server callbacks
%%====================================================================

%%--------------------------------------------------------------------
%% Function: init(Args) -> {ok, State} |
%%                         {ok, State, Timeout} |
%%                         ignore               |
%%                         {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
init([]) ->
    {ok, #state{}}.

%%--------------------------------------------------------------------
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
%%                                      {reply, Reply, State, Timeout} |
%%                                      {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, Reply, State} |
%%                                      {stop, Reason, State}
%% Description: Handling call messages
%%--------------------------------------------------------------------
handle_call(count, _From, State) ->
    Reply = State#state.counter + 1,
    NewState = #state{counter=Reply},
    {reply, Reply, NewState};

handle_call(_Request, _From, State) ->
    Reply = ok,
    {reply, Reply, State}.

%%--------------------------------------------------------------------
%% Function: handle_cast(Msg, State) -> {noreply, State} |
%%                                      {noreply, State, Timeout} |
%%                                      {stop, Reason, State}
%% Description: Handling cast messages
%%--------------------------------------------------------------------
handle_cast(_Msg, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: handle_info(Info, State) -> {noreply, State} |
%%                                       {noreply, State, Timeout} |
%%                                       {stop, Reason, State}
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info(_Info, State) ->
    {noreply, State}.

%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
%% Description: This function is called by a gen_server when it is about to
%% terminate. It should be the opposite of Module:init/1 and do any necessary
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
terminate(_Reason, _State) ->
    ok.

%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
%% Description: Convert process state when code is changed
%%--------------------------------------------------------------------
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.

%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
count() ->
    gen_server:call(?MODULE, count).

As you can see I used the template of a gen_server for my implementation of the counter server. The value of the counter is stored inside its state, and it starts with the value of 0. The server handles a specific call that is aimed to retrieve the counter from the server, increment it by one and return that value to the client.  In the internal functions space, I defined a function named count that performs the specific call to the gen_server.

Easy, isn’t it?

Now we have only to start the server in a Erlang node (we name it server@ourlocalhost).

bellerofonte@pegaso:~$ erl -sname server@pegaso
Erlang R13B02 (erts-5.7.3)
Eshell V5.7.3  (abort with ^G)
(server@pegaso)1> counter_svr:start_link().
{ok,<0.40.0>}

At this point the server is up an running on our Erlang node, so the only thing left is to start another Erlang node (or many nodes) and perform some RPC from these. First of all we start the second node, you should know that this node must be connected to the previous one and share with it the same cookie, if you don’t see Erlang User Guide for more info. In brief I can tell you that you can check whether two nodes are connected by calling in one node the function net:ping(‘othernode@host’) that gives back a ‘pong’ if they are connected, or a ‘pang’ if they are not…..moreover you can check if the nodes have the same cookie by calling in both nodes the function erlang:get_cookie() (otherwise you can set the cookie with erlang:set_cookie(Cookie). Coming back to our discussion on rpc, inside the second node we call the function through RPC by using as stated above rpc:call(Node, Module, Function, Arguments)…in our specific case Node is the node where the server is running, Module is the module implementing the server, Function is the function that retrieves the counter and increments it and Arguments is an empty list, since the function takes no arguments.

bellerofonte@pegaso:~$ erl -sname client@pegaso
Erlang R13B02 (erts-5.7.3)
Eshell V5.7.3  (abort with ^G)
(client@pegaso)1> rpc:call('server@pegaso', counter_svr, count, []).
1
(client@pegaso)2> rpc:call('server@pegaso', counter_svr, count, []).
2
(client@pegaso)3> rpc:call('server@pegaso', counter_svr, count, []).
3
(client@pegaso)4> rpc:call('server@pegaso', counter_svr, count, []).
4

As you may see in the example I posted above, multiple RPCs to our server, give back the result expected…..

In my opinion this comparison explains why I still prefer using Erlang when talking or coding distributed systems!

Advertisements
Categories: C, English, Erlang Tags: , ,

REMOTE PROCEDURE CALLs: C vs. Erlang (part 1)

December 21, 2009 Leave a comment

In the last weeks I have been geeking a while with Remote Procedure Calls.

RPCs were invented at Sun Microsystems in the 80s, and can be seen as “normal” procedure calls on a distributed system. The concept behind RPCs is the following: you don’t have to declare the function you want to use in your client, you can declare that function on a server and than if you invoke it, a so-called “middleware” is going to forward the request from the client to the server, where the function is executed and the result is returned back to the client.

Since I’m still confident that Erlang is much more better than C for distributed systems I’ll try to code in both languages a server that keeps a counter in it, each time a client calls via RPC a function it will increment that value.

In C I could do it coding three files. Let’s start with the first: a file (counter.x) with the specifications of the service provided by the functions that can be called remotely. The specifications must be converted to stubs and header files for the middleware using the tool rpcgen (we will se it later).

When you write the specifications, you must identify the functions that are going to be called remotely. The functions must specify the return type and arguments. For every function (called RPC), you must assign an ID and must include it in a program that must be uniquely referred to by an ID and a version.

Our counter.x may look like this:

program COUNTERPROG {
  version COUNTERVERS {
    int COUNTER() = 1;
  } = 1;
} = 0x20000001;

where the remote procedure COUNTER has ID 1 and is included in the version 1 of the program COUNTERPROG. The ID of the program is 0x20000001.

After that you must write the procedure code as if you would write it locally, but with slight changes. The procedure must use a conventional name PROGRAM_VERSION_svc (in our case counter_1_svc). The procedure uses pointers to return the result and receive the arguments.
One more argument is required to identify the execution context.

Thus in our case we can create a file named counter_proc.c of the following form:

#include <stdio.h>
#include "counter.h"
int *counter_1_svc(void *msg, struct svc_req *req)
{
  static int result = 0;
  result++;
  return (&result);
}

The header file counter.h is one of the files generated by rpcgen. As you can see we create a STATIC variable result that lives in the space allocated for static variables so that it can survive when the execution of the fuction ends. Whenever the function is called we increment that value and return the address of that static variable.

Now we have to write to client! This client, before performing the remote call to the server, must connect to it. Then, after the call it can disconnect. The connection is done using the function clnt_create(server address, program id, program version, transport protocol). The disconnection is done using the function clnt_destroy().

Let’s create a file called rcounter.c of the following form:

#include <stdio.h>
#include <string.h>
#include "counter.h"
int main(int argc, char **argv)
{
  CLIENT *clnt;
  int *result;
  char *server;
  // try to get the server address from command line
  if (argc != 2) {
    fprintf(stderr, "Usage is the following: %s server\n", argv[0]);
    return -1;
  }
  server = argv[1];
  // create the client
  clnt = clnt_create(server, COUNTERPROG, COUNTERVERS, "udp"); //udp may be set to tcp
  if (clnt == NULL) {
    clnt_pcreateerror(server);
    return -1;
  }
  // call the function
  result = counter_1(NULL, clnt);
  if (result == NULL) {
    clnt_perror(clnt, server);
    return -1;
  }
  // print the value obtained from the call
  printf("The value of the counter is %d\n", *result);
  // destroy the client
  clnt_destroy(clnt);
  return 0;
}

Now let’s compile all the files:

bellerofonte@pegaso:~$ rpcgen counter.x

This generates: counter.h counter_svc.c counter_clnt.c

bellerofonte@pegaso:~$ gcc counter_svc.c counter_proc.c -o counter_svr -lnsl
bellerofonte@pegaso:~$ gcc counter_clnt.c rcounter.c -o counter_clt -lnsl

and execute them:

bellerofonte@pegaso:~$ ./counter_svr &
[1] 4898
bellerofonte@pegaso:~$ ./counter_clt localhost
Counter is 1
bellerofonte@pegaso:~$ ./counter_clt localhost
Counter is 2
bellerofonte@pegaso:~$ kill -9 4898

As you can see at every call the value returned is the previous one plus 1 (starting from zero).

I killed the server in the end because I started it in background mode…if you want you can run the two files in different shells to avoid this.

Ok, this post starts being long…so for the Erlang code I will open a new one 🙂

Categories: C, English, Erlang Tags: , , ,