Backends failover tracking

Posted by stoyan

Pretty useful piece of code:
is_pid_alive(Pid) 
  when is_pid(Pid) ->
    rpc:call(node(Pid), erlang, is_process_alive, [Pid]).
Checking if some process is alive. But working only with Erlang processes. I cannot check the status of some general TCP server. Still pretty cool. Based on that function I added a status check to Reverl. Maybe will use it for the other proxy nodes checks. The result: gateway behaviour with RR backend choice and backends failover tracking . Usage:
$ erl
1> l(gproxy3).
{module,gproxy3}
2> S1=gproxy3:start_be(fun(X) -> 2*X end).
<0.33.0>
3> S2=gproxy3:start_be(fun(X) -> 3*X end).
<0.35.0>
4> S3=gproxy3:start_be(fun(X) -> 4*X end).
<0.37.0>
5> G=gproxy3:start_gw([S1,S2,S3]).
<0.39.0>
6> gproxy3:rpc(G,21).
42
7> gproxy3:rpc(G,21).
63
8> gproxy3:rpc(G,21).
84
9> S4=gproxy3:start_be(fun(X) -> 5*X end).
<0.44.0>
10> gproxy3:add_be(G,S4).
{be_add,<0.44.0>}
11> gproxy3:del_be(G,S2).
{be_del,<0.35.0>}
12> gproxy3:list_be(G).  
[<0.44.0>,<0.33.0>,<0.37.0>]
13> gproxy3:stop(S1).
stop
14> gproxy3:rpc(G,21).                     
105
15> gproxy3:rpc(G,21).

=ERROR REPORT==== 26-Feb-2007::17:55:38 ===
(<0.39.0> gproxy3:30) backend <0.33.0> is down
84
16> gproxy3:rpc(G,21).
105
Next step: to figure what parts can be handled by the already existing gen_server behaviour and rewrite my code to separate the differences.

reverl - REVerse proxy in ERLang

Posted by stoyan

Created the reverl project on Google Code .

Reverl is reverse proxy, load balancer and HTTPS front-end for Web server(s), implemented in Erlang.

Before to start the real coding, I needed a more general proxy(gateway) behaviour.

What exactly is proxy (or gateway)? It’s the middle-man between the client and a set of backend service providers, the guy who get the requests, resend them to some backend, get the response for that backend and return it back to the client. The interesting part is the choice of backend with possible load balancing and tracking backends failover. In general the proxy(gateway) give you High Availabity (HA) of the provided services.

So on the question. The first code approximation:
-module(gproxy0).
-export([start_be/1,start_gw/1,change_be/2,rpc/2]).

%% start the server (backend)
start_be(Fun) -> spawn(fun() -> loop(Fun) end).

%% start the proxy (frontend)
start_gw(Backend) -> spawn(fun() -> gloop(Backend) end).

%% Pid can be gateway or server 
rpc(Pid, Q) ->
  Pid ! {self(), Q},
  receive {Pid, Reply} -> Reply
  end.

%% change the backend , send to the GATEWAY
change_be(Gw,Backend) -> Gw ! {be_change,Backend}.

%% server (backend) loop
loop(Fun) ->
  receive
    %% direct request
    {From, X} -> From ! {self(), Fun(X)}, loop(Fun);
    %% request via gateway
    {Gw, From, X} -> Gw ! {From, ok, Fun(X)}, loop(Fun)
  end.

%% gateway (frontend) loop
gloop(Backend) ->
  receive
    {be_change,Backend1} -> gloop(Backend1);
    {Client, X} -> Backend ! {self(), Client, X}, gloop(Backend);
    {Client, ok, X} -> Client ! {self(), X}, gloop(Backend)
  end.
Usage
$ erl
1> c(gproxy0).
{ok,gproxy0}
2> F1=fun(X) -> 2*X end.      % provided service
#Fun<erl_eval.6.56006484>
3> B1 = gproxy0:start_be(F1). % start the backend
<0.38.0>
4> gproxy0:rpc(B1,21).        % service request to the backend
42
5> G=gproxy0:start_gw(B1).    % create the gateway with one backend
<0.41.0>
6> gproxy0:rpc(G,21).         % service request to the gateway
42
7> B2=gproxy0:start_be(fun(X) -> 3*X end).  % new backend
<0.44.0>
8> gproxy0:change_be(G,B2).   % change the backend
{be_change,<0.44.0>}
9> gproxy0:rpc(G,21).                     
63
More sophisticated code versions (changing service code on the fly, handling errors etc.) are available for download :
  • gproxy.erl—General gateway behaviour
  • gproxy1.erl—General gateway behaviour with some errors protection
  • gproxy2.erl—Gateway behaviour with simple round robin between backends
Stay tuned…