Erlang-diameter: Issue when trying to add multiple servers to a relay

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

Erlang-diameter: Issue when trying to add multiple servers to a relay

Arshad Ansari
Hello there,

Firstly, I've just picked up erlang this week and I'm suppose to evaluate Erlang-Diameter for my company (I'm even new to telecom domain and hence to diameter protocol). So, I'm a complete noob in this area.  I'm using the example provided by OTP in lib/diameter/examples/code folder. I'm using the relay (listening on 3868) code given there to connect to the two instance of server that I have seperately started on 3871 and 3872. When I connect to these two servers, the connection is established with both but the Peer Up in relay_cb.erl is called only for the first server I tried to connect to. Hence forth, all the requests from clients are relayed to this single Peer. I'm trying to load balance the requests and therefore sharing client connections across multiple server. I would like and appreciate some help in that direction. I haven't changed much from the example directory. I'm just listing the params I'm using for connection as shown below:

Server uses the following config:
Server 1:
[{'Origin-Host', "3871.example.com"},
                          {'Origin-Realm', "example.com"},
                          {'Vendor-Id', 193},            
                          {'Product-Name', "3871-Server-Erlang"},
                          {'Auth-Application-Id', [0]},  
                          {restrict_connections, false}, 
                          {string_decode, false},        
                          {application, [{alias, common},
                                         {dictionary, diameter_gen_base_rfc6733},
                                         {module, server_cb}]}]).     

Server 2:
[{'Origin-Host', "3872.example.com"},
                          {'Origin-Realm', "example.com"},
                          {'Vendor-Id', 193},            
                          {'Product-Name', "3872-Server-Erlang"},
                          {'Auth-Application-Id', [0]},  
                          {restrict_connections, false}, 
                          {string_decode, false},        
                          {application, [{alias, common},
                                         {dictionary, diameter_gen_base_rfc6733},
                                         {module, server_cb}]}]).     

Relay:
[{'Origin-Host', "arshad.example.com"},
                         {'Origin-Realm', "example.com"},
                         {'Vendor-Id', 193},
                         {'Product-Name', "RelayAgent"},
                         {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]}, 
                         {string_decode, false},
                         {application, [{alias, relay},
                                        {dictionary, ?DIAMETER_DICT_RELAY},
                                        {module, relay_cb}]}]).


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions
Reply | Threaded
Open this post in threaded view
|

Re: Erlang-diameter: Issue when trying to add multiple servers to a relay

Arshad Ansari
Hello Chandru,

Thank you for the reply.

Here is what I have done. I'm using the example code given in OTP library available here at https://github.com/erlang/otp/tree/master/lib/diameter/examples/code. The node.erl being common across client.erl, server.erl and relay.erl. However, I have a seperate version of node.erl for each of these systems. My entire code is here: https://github.com/arshadansari27/erlang-diameter-eval

The code I'm using the run the client.erl and client_cb.erl is as follows:

-module(runner4).

-export([run/0, one_client/0, stop/0]).

run() ->
        diameter:start(),
        client:start(),
        client:connect(tcp).

stop() ->
        client:stop(),
        diameter:stop().


one_client()  ->
        client:call().


I'm calling runner4:run() first then runner4:one_client() and finally runner4:stop() so I can see tcp dumps and all after each and every step. Client connects to Relay and Relay forwards the requests to Server instances and gets a response from there and returns to the client. So that part is working. I was even able to do a little hack in pick_peer as shown below, so I can balance the incoming requests across multiple server instances. In my case, there are only two instances so I'm just selecting in a round robin manner and returning that peer.

pick_peer(Peers, _, _SvcName, _State, relayed) ->
        [{_, Peer_counter}] = ets:lookup(mytable_relay, peers),
        Peer_count = other_counter:inc(Peer_counter),
        Index = (Peer_count rem 2) + 1,
        % List = lists:map(fun(N) -> {Xs, _} = N, Xs end, Peers),
        Peer = lists:nth(Index, Peers),
    {ok, Peer}.

With this I'm also able to load balance across different server instances from relay at exact 50/50 ration. So I was able to solve much of the original problem I had. The problem that remains is that what I have done is firstly a hack and secondly inexplicable. And what follows is the current problem.
When I start relay and then start both the server instances, relay calls peer up for both the servers and the pick_peer's first argument (local nodes) has both the servers as peers. Whereas when I start the server instances first and then relay, then only one time pick_peer is called and with only one of the server instance. Even the tcp dump on wireshark does not show the CER exchange between relay and servers, whereas the CER exchange is there from client to relay. This is what is confusing me. Why would the order of starting the server/relay allow peers to be added and/or not as well as why no CER exchange between relay and server instances? They do have TCP packets exchanged between them, but not the Diameter protocol CER exchange that is present for Client-relay communication. 

 Given below is the code that starts the relay and connects it to both the server instances that are running on the same machine. 
% runner.erl
-module(runner).

-export([run/0]).

run() ->
        diameter:start(),
        relay:start(),
        relay:connect(tcp, 1, true),
        relay:connect(tcp, 2, true),
        relay:listen(tcp).

% relay.erl
-module(relay).

-include_lib("diameter/include/diameter.hrl").
-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl").
-include_lib("diameter/include/diameter_gen_relay.hrl").

-export([start/1,
         start/2,
         listen/2,
         connect/2,
         connect/3,
         stop/1,
         handle_info/2]).

-export([start/0,
         listen/1,
         connect/1,
         stop/0]).

-define(DEF_SVC_NAME, ?MODULE).

%% The service configuration.
-define(SERVICE(Name), [{'Origin-Host', "pcrf.relay.com"},
                        {'Origin-Realm', "relay.com"},
                        {'Vendor-Id', 193},
                        {'Product-Name', "RelayAgent"},
                        {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]}, % 16#FFFFFFFF
                        {string_decode, false},
                                                {use_shared_peers, true},
                        {application, [{alias, relay},
                                       {dictionary, ?DIAMETER_DICT_RELAY}, % diameter_gen_relay
                                       {module, relay_cb}]},
                                                {share_peers, true}
                       ]).
                       %% SKIPPING the code that is same as the original one given on that link.. 
                       
                      connect(T, SName, Multi) when Multi == true ->
                          node:connect(?DEF_SVC_NAME, T, SName).
% node.erl

%% SKIPPING the code that is same as the original one given on that link.. 
-define(RELAY_PORT, 3868).
-define(SERVER_PORT1, 3871).
-define(SERVER_PORT2, 3872).

%% connect/2

-spec connect(diameter:service_name(), client_opts())
   -> {ok, diameter:transport_ref()}
    | {error, term()}.

connect(Name, Opts)
  when is_list(Opts) ->
        io:format("Connection Options ~p\n", [{connect, Opts}]),
    diameter:add_transport(Name, {connect, Opts});

connect(Name, {T, Opts}) ->
    connect(Name, Opts ++ client_opts(T));

connect(Name, T) ->
    connect(Name, [{connect_timer, 5000} | client_opts(T)]).

connect(Name, T, SName) ->
    connect(Name, [{connect_timer, 5000} | client_opts({T, SName ,true})]).

client_opts({T, LA, RA, RP})
  when T == all;   %% backwards compatibility
       T == any ->
    [[S, {C,Os}], T] = [client_opts({P, LA, RA, RP}) || P <- [sctp,tcp]],
    [S, {C,Os,2000} | T];

client_opts({T, LA, RA, RP}) ->
    [{transport_module, tmod(T)},
     {transport_config, [{raddr, addr(RA)},
                         {rport, RP},
                         {reuseaddr, true}
                         | ip(LA)]}];

client_opts({T, S_Port, Multi}) when Multi == true ->
        Port = case S_Port of 
                1 -> ?SERVER_PORT1;
                2 -> ?SERVER_PORT2
        end,
    client_opts({T, loopback, remotelocal, Port});


client_opts({T, RA, RP}) ->
    client_opts({T, default, RA, RP});

addr(remotelocal) ->
    {172,16,101,146};

ip(remotelocal) ->
    {172,16,101,146};
 

REALM INFO
------------------

client: 
    Origin-Host: first.client.com
    Origin-Realm: client.com

Relay: 
    Origin-Host: pcrf.realy.com
    Origin-Realm: realy.com


Server 1: 
    Origin-Host: 3671.server.com
    Origin-Realm: server.com

Server 2: 
    Origin-Host: 3672.server.com
    Origin-Realm: server.com

I hope that covers everything. Any help or direction is very much appreciated.

Regards,
Arshad

On 28-Jul-2016 2:56 AM, "Chandru" <[hidden email]> wrote:
Hi Arshad,

First, some general advice. I've used the Erlang diameter stack quite extensively to build telecom applications and it is pretty rock solid. I wouldn't hesitate to recommend using it. There are several mission critical services built using this stack running in production at EE (largest mobile operator) in the UK.

Re the specific problem you are facing, how are you generating the requests on the client side? Some packet captures or the client code would be useful to see.

cheers,
Chandru

On 27 July 2016 at 09:00, Arshad Ansari <[hidden email]> wrote:
Hello there,

Firstly, I've just picked up erlang this week and I'm suppose to evaluate Erlang-Diameter for my company (I'm even new to telecom domain and hence to diameter protocol). So, I'm a complete noob in this area.  I'm using the example provided by OTP in lib/diameter/examples/code folder. I'm using the relay (listening on 3868) code given there to connect to the two instance of server that I have seperately started on 3871 and 3872. When I connect to these two servers, the connection is established with both but the Peer Up in relay_cb.erl is called only for the first server I tried to connect to. Hence forth, all the requests from clients are relayed to this single Peer. I'm trying to load balance the requests and therefore sharing client connections across multiple server. I would like and appreciate some help in that direction. I haven't changed much from the example directory. I'm just listing the params I'm using for connection as shown below:

Server uses the following config:
Server 1:
[{'Origin-Host', "3871.example.com"},
                          {'Origin-Realm', "example.com"},
                          {'Vendor-Id', 193},            
                          {'Product-Name', "3871-Server-Erlang"},
                          {'Auth-Application-Id', [0]},  
                          {restrict_connections, false}, 
                          {string_decode, false},        
                          {application, [{alias, common},
                                         {dictionary, diameter_gen_base_rfc6733},
                                         {module, server_cb}]}]).     

Server 2:
[{'Origin-Host', "3872.example.com"},
                          {'Origin-Realm', "example.com"},
                          {'Vendor-Id', 193},            
                          {'Product-Name', "3872-Server-Erlang"},
                          {'Auth-Application-Id', [0]},  
                          {restrict_connections, false}, 
                          {string_decode, false},        
                          {application, [{alias, common},
                                         {dictionary, diameter_gen_base_rfc6733},
                                         {module, server_cb}]}]).     

Relay:
[{'Origin-Host', "arshad.example.com"},
                         {'Origin-Realm', "example.com"},
                         {'Vendor-Id', 193},
                         {'Product-Name', "RelayAgent"},
                         {'Auth-Application-Id', [?DIAMETER_APP_ID_RELAY]}, 
                         {string_decode, false},
                         {application, [{alias, relay},
                                        {dictionary, ?DIAMETER_DICT_RELAY},
                                        {module, relay_cb}]}]).


_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions



_______________________________________________
erlang-questions mailing list
[hidden email]
http://erlang.org/mailman/listinfo/erlang-questions