%% proxy behaviour with round robin backend choice -module(gproxy2). -export([start_be/1,start_gw/1,stop/1,rpc/2, change_code/2]). %% backends management -export([rrobin/1, change_elect/2, add_be/2, del_be/2, list_be/1]). -define(TIMEOUT, 3000). %% TODO: %% - balance: gloop([S1, S2, S3]) %% - backend failover %% simple round robin rotation rrobin(Pool) -> if Pool /= [] -> [First|TheRest] = Pool, {First,lists:flatten(TheRest,[First])}; true -> {empty,[]} end. %% start the server (backend) start_be(Fun) -> spawn(fun() -> loop(Fun) end). %% start the gateway (frontend) %% Pool - a list of possible backends start_gw(Pool) -> spawn(fun() -> gloop(Pool,fun(X)->gproxy2:rrobin(X) end) end). %% stop gateway or backend stop(Pid) -> Pid ! stop. %% Pid can be gateway or server rpc(Pid, Query) -> Pid ! {self(), Query}, receive {Pid, crash} -> exit(rpc); {Pid, mustwait} -> io:format("~p not ready. please wait...~n",[Pid]), exit(rpc); {Pid, Reply} -> Reply end. log_msg(Type, Pid, Query, Why) -> case Type of err -> io:format("[E] Server: ~p, Query: ~p, Reason: ~p~n", [Pid, Query, Why]); done -> io:format("[*] Done. Server: ~p, Query: ~p, Reply: ~p~n", [Pid, Query, Why]); timeout -> io:format("[T] Server: ~p, Timeout after: ~p~n", [Pid, Query]) end. %% function code change %% send to the SERVER change_code(Server, Fun) -> Server ! {swap_code,Fun}. change_elect(Gw, Elect) -> Gw ! {swap_elect,Elect}. add_be(Gw, Server) -> Gw ! {be_add, Server}. del_be(Gw, Server) -> Gw ! {be_del, Server}. list_be(Gw) -> Gw ! {self(), list_be}, receive {ok,Reply} -> Reply end. %% server loop loop(Fun) -> receive stop -> void; {swap_code,Fun1} -> loop(Fun1); %% direct request {Client, Data} -> case catch Fun(Data) of {'EXIT', Why} -> log_msg(err, Client, Data, Why), Client ! {self(), crash}, loop(Fun); Reply -> Client ! {self(), Reply}, loop(Fun) end; %% request via gateway {Gw, Client, Data} -> case catch Fun(Data) of {'EXIT', Why} -> log_msg(err, Client, Data, Why), Gw ! {self(), Client, crash}, loop(Fun); Reply -> Gw ! {self(), Client, ok, Reply}, loop(Fun) end end. %% gateway loop gloop(Pool,Elect) -> receive stop -> void; {swap_elect,Elect1} -> gloop(Pool, Elect1); {be_add,Server} -> gloop([Server|Pool], Elect); {be_del,Server} -> gloop(Pool, Elect); {Client,list_be} -> Client ! {ok, Pool}, gloop(Pool, Elect); {Client, Data} -> if Pool /= [] -> case catch Elect(Pool) of {empty,[]} -> Client ! {self(), mustwait}, gloop(Pool, Elect); {'EXIT', Why} -> Client ! {self(), mustwait}, gloop(Pool, Elect); {Backend,Pool1} -> Backend ! {self(), Client, Data}, gloop(Pool1,Elect) end; true -> %% empty pool Client ! {self(), mustwait}, gloop(Pool, Elect) end; {Server, Client, crash} -> Client ! {self(), crash}, gloop(Pool, Elect); {Server, Client, ok, Data} -> Client ! {self(), Data}, gloop(Pool, Elect) end.