Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add support for multiples servers. Use RoundRobin to choose the serve…

…r at runtime

SVN Revision: 613
  • Loading branch information...
commit f3816a54c2ce1008e617a75b68e6bd1b981fe51c 1 parent cf78827
@nniclausse nniclausse authored
View
4 examples/http_distributed.xml.in
@@ -34,7 +34,9 @@
</clients>
<!-- Server side setup -->
- <server host="myserver" port="80" type="tcp"></server>
+<servers>
+ <server host="myserver" port="80" type="tcp"/>
+</servers>
<!-- to start os monitoring (cpu, network, memory). Use an erlang
agent on the remote machine or SNMP. erlang is the default -->
View
2  examples/http_simple.xml.in
@@ -8,7 +8,9 @@
</clients>
<!-- Server side setup -->
+<servers>
<server host="myserver" port="80" type="tcp"></server>
+</servers>
<!-- to start os monitoring (cpu, network, memory). Use an erlang
agent on the remote machine or SNMP. erlang is the default -->
View
2  examples/jabber.xml.in
@@ -7,7 +7,9 @@
</clients>
<!-- Server side setup -->
+ <servers>
<server host="127.0.0.1" port="5222" type="tcp"></server>
+ </servers>
<load>
<arrivalphase phase="1" duration="1" unit="minute">
View
2  examples/pgsql.xml.in
@@ -7,7 +7,9 @@
</clients>
<!-- Server side setup -->
+ <servers>
<server host="127.0.0.1" port="5432" type="tcp"/>
+ </servers>
<monitoring>
<monitor host="localhost"/>
View
2  include/ts_config.hrl
@@ -38,7 +38,7 @@
controller, %% controller machine
clients=[], %% client machines
- server, %% server to test
+ servers =[], %% server(s) to test
monitor_hosts = [], %% Cluster host to monitor (for CPU, MEM usage)
arrivalphases = [], %% arrival process specs
thinktime, %% default thinktime specs
View
40 src/tsung/ts_client.erl
@@ -71,20 +71,19 @@ next({Pid}) ->
init({#session{id = Profile,
persistent = Persistent,
ssl_ciphers = Ciphers,
- type = CType}, Count, IP}) ->
- ?DebugF("Init ... started with count = ~p ~n",[Count]),
+ type = CType}, Count, IP, Server}) ->
+ ?DebugF("Init ... started with count = ~p~n",[Count]),
ts_utils:init_seed(),
- {ServerName, Port, Protocol} = get_server_cfg({Profile,1}),
- ?DebugF("Get dynparams for ~p ~n",[CType]),
+ ?DebugF("Get dynparams for ~p~n",[CType]),
DynData = CType:init_dynparams(),
StartTime= now(),
ts_mon:newclient({self(), StartTime}),
set_thinktime(?short_timeout),
- {ok, think, #state_rcv{ port = Port,
- host = ServerName,
+ {ok, think, #state_rcv{ port = Server#server.port,
+ host = Server#server.host,
profile = Profile,
- protocol = Protocol,
+ protocol = Server#server.type,
clienttype = CType,
session = CType:new_session(),
persistent = Persistent,
@@ -291,33 +290,6 @@ handle_next_action(State) ->
end.
-%%----------------------------------------------------------------------
-%% Func: get_server_cfg/1
-%% Args: Profile, Id
-%%----------------------------------------------------------------------
-get_server_cfg({Profile, Id}) ->
- get_server_cfg(ts_session_cache:get_req(Profile, Id), Profile, Id).
-
-%%----------------------------------------------------------------------
-%% Func: get_server_cfg/3
-%%----------------------------------------------------------------------
-get_server_cfg(#ts_request{host=undefined, port=undefined, scheme=undefined},_,_)->
- ?Debug("Server not configured in msg, get global conf ~n"),
- %% get global server profile
- ts_session_cache:get_server_config();
-get_server_cfg(#ts_request{host=ServerName, port=Port, scheme=Protocol},_,_) ->
- %% server profile can be overriden in the first URL of the session
- %% curently, the following server modifications in the session are not used.
- ?LOGF("Server setup overriden for this client host=~s port=~p proto=~p~n",
- [ServerName, Port, Protocol], ?INFO),
- {ServerName, Port, Protocol};
-get_server_cfg({transaction,_,_},Profile,Id) ->
- get_server_cfg(ts_session_cache:get_req(Profile, Id+1), Profile, Id+1);
-get_server_cfg({thinktime,_},Profile,Id) ->
- get_server_cfg(ts_session_cache:get_req(Profile, Id+1), Profile, Id+1);
-get_server_cfg(Other,_,_) ->
- ?LOGF("ERROR while getting cfg (~p)! ~n",[Other],?ERR),
- ts_session_cache:get_server_config().
%%----------------------------------------------------------------------
%% Func: handle_next_request/2
View
6 src/tsung/ts_http.erl
@@ -97,12 +97,16 @@ add_dynparams(true, DynData, Param, HostData) ->
add_dynparams(DynData#dyndata.proto,NewParam, HostData).
%% Function: add_dynparams/3
+add_dynparams(DynData,Param=#http_request{host_header=undefined}, {Host,Port})->
+ Header=Host++":"++ integer_to_list(Port),
+ ?DebugF("set host header dynamically: ~s~n",[Header]),
+ add_dynparams(DynData, Param#http_request{host_header=Header},{Host,Port});
%% no cookies
add_dynparams(#http_dyndata{cookies=[],user_agent=UA},Param, _) ->
Param#http_request{user_agent=UA};
%% cookies
add_dynparams(#http_dyndata{cookies=DynData,user_agent=UA}, Param, _) ->
-%% FIXME: should we use the Port value in the Cookie ?
+ %% FIXME: should we use the Port value in the Cookie ?
Param#http_request{cookie=DynData,user_agent=UA}.
init_dynparams() ->
View
20 src/tsung/ts_session_cache.erl
@@ -39,14 +39,13 @@
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
- add/1, code_change/3, get_server_config/0]).
+ add/1, code_change/3]).
-record(state, {
table, % ets table
hit =0.0, % number of hits
total=0.0, % total number of requests
- server_config,
stats=[] % cache stats msgs
}).
@@ -71,9 +70,6 @@ get_req(Id, Count)->
get_user_agent()->
gen_server:call(?MODULE,{get_user_agent}).
-get_server_config()->
- gen_server:call(?MODULE,{get_server_config}).
-
add(Data) ->
gen_server:cast(?MODULE, {add, Data}).
%%====================================================================
@@ -149,20 +145,6 @@ handle_call({get_user_agent}, _From, State) ->
{reply, Reply, State}
end;
-handle_call({get_server_config}, _From, State= #state{table=Tab}) ->
- case ets:lookup(Tab, {server_config}) of
- [{_, ServerConf}] ->
- {reply, ServerConf, State};
- [] -> %% no match, ask the config_server
- case catch ts_config_server:get_server_config() of
- {'EXIT',Reason} ->
- {reply, {error, Reason}, State};
- Reply ->
- ets:insert(Tab, {{get_server_config}, Reply}),
- {reply, Reply, State#state{server_config = Reply}}
- end
- end;
-
handle_call(_Request, _From, State) ->
Reply = ok,
{reply, Reply, State}.
View
10 src/tsung_controller/ts_config.erl
@@ -94,7 +94,7 @@ parse(Element = #xmlElement{parents = [], attributes=Attrs}, Conf=#config{}) ->
%% parsing the Server elements
-parse(Element = #xmlElement{name=server, attributes=Attrs}, Conf) ->
+parse(Element = #xmlElement{name=server, attributes=Attrs}, Conf=#config{servers=ServerList}) ->
Server = getAttr(Attrs, host),
Port = getAttr(integer, Attrs, port),
Type = case getAttr(Attrs, type) of
@@ -104,10 +104,10 @@ parse(Element = #xmlElement{name=server, attributes=Attrs}, Conf) ->
end,
lists:foldl(fun parse/2,
- Conf#config{server = #server{host=Server,
- port=Port,
- type=Type
- }},
+ Conf#config{servers = [#server{host=Server,
+ port=Port,
+ type=Type
+ }|ServerList]},
Element#xmlElement.content);
%% Parsing the cluster monitoring element (monitor)
View
29 src/tsung_controller/ts_config_http.erl
@@ -48,7 +48,7 @@
parse_config(Element = #xmlElement{name=dyn_variable}, Conf = #config{}) ->
ts_config:parse(Element,Conf);
parse_config(Element = #xmlElement{name=http},
- Config=#config{curid = Id, session_tab = Tab, server = Server,
+ Config=#config{curid = Id, session_tab = Tab, servers = Servers,
sessions = [CurS | _], dynvar=DynVar,
subst = SubstFlag, match=MatchRegExp}) ->
Version = ts_config:getAttr(Element#xmlElement.attributes, version),
@@ -93,9 +93,9 @@ parse_config(Element = #xmlElement{name=http},
Passwd = ts_config:getAttr(string,AuthEl#xmlElement.attributes,
passwd, undefined),
NewReq=Request2#http_request{userid=UserId, passwd=Passwd},
- set_msg(NewReq, 0, {SubstFlag, MatchRegExp, UseProxy, Server, PreviousHTTPServer, Tab, CurS#session.id} );
+ set_msg(NewReq, 0, {SubstFlag, MatchRegExp, UseProxy, Servers, PreviousHTTPServer, Tab, CurS#session.id} );
_ ->
- set_msg(Request2, 0, {SubstFlag, MatchRegExp, UseProxy, Server, PreviousHTTPServer, Tab, CurS#session.id} )
+ set_msg(Request2, 0, {SubstFlag, MatchRegExp, UseProxy, Servers, PreviousHTTPServer, Tab, CurS#session.id} )
end,
ts_config:mark_prev_req(Id-1, Tab, CurS),
@@ -152,7 +152,7 @@ get_previous_http_server(Ets, Id) ->
%% port and scheme from the URL and override the global setup of the
%% server. These informations are stored in the #ts_request record.
set_msg(HTTP=#http_request{url="http" ++ URL},
- ThinkTime, {SubstFlag, MatchRegExp, UseProxy, Server, _PrevHTTPServer, Tab, Id}) -> % full URL
+ ThinkTime, {SubstFlag, MatchRegExp, UseProxy, [Server|_], _PrevHTTPServer, Tab, Id}) ->
URLrec = parse_URL("http" ++ URL),
Path = set_query(URLrec),
HostHeader = set_host_header(URLrec),
@@ -174,23 +174,26 @@ set_msg(HTTP=#http_request{url="http" ++ URL},
scheme = RealServer#server.type,
port = RealServer#server.port});
-%% relative URL, no previous HTTP server (hence, first request in session)
-set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, false, Server, [],_Tab,_Id}) ->
- URL = server_to_url(Server) ++ HTTPRequest#http_request.url,
- set_msg(HTTPRequest#http_request{url=URL}, Think, {SubstFlag, MatchRegExp, false, Server, [],_Tab,_Id});
-
%% relative URL, no previous HTTP server, use proxy, error !
set_msg(_, _, {_, _, true, _Server, [],_Tab,_Id}) ->
?LOG("Need absolut URL when using a proxy ! Abort",?ERR),
throw({error, badurl_proxy});
+%% relative URL, no proxy, a single server => we can preset host header at configuration time
+set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, false, [Server], [],_Tab,_Id}) ->
+ ?LOG("Relative URL, single server ",?NOTICE),
+ URL = server_to_url(Server) ++ HTTPRequest#http_request.url,
+ set_msg(HTTPRequest#http_request{url=URL}, Think, {SubstFlag, MatchRegExp, false, [Server], [],_Tab,_Id});
+%% relative URL, no proxy, several servers: don't set host header
+%% since the real server will be choose at run time
+set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, false, _Servers, [],_Tab,_Id}) ->
+ set_msg2(HTTPRequest, Think,
+ #ts_request{ack = parse, subst = SubstFlag, match = MatchRegExp });
%% relative URL, no proxy
set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, false, _Server, {HostHeader,_},_Tab,_Id}) ->
- %% don't set host, port and scheme (undefined value), it will be
- %% dynamicaly set during the run (using default server or previous
- %% one used in the current session)
set_msg2(HTTPRequest#http_request{host_header= HostHeader}, Think,
#ts_request{ack = parse, subst = SubstFlag, match = MatchRegExp });
-set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, true, Server, {HostHeader, PrevScheme},Tab,Id}) -> % relative URL, use proxy
+%% relative URL, use proxy
+set_msg(HTTPRequest, Think, {SubstFlag, MatchRegExp, true, Server, {HostHeader, PrevScheme},Tab,Id}) ->
%% set absolut URL using previous Server used.
URL = atom_to_list(PrevScheme) ++ "://" ++ HostHeader ++ HTTPRequest#http_request.url,
set_msg(HTTPRequest#http_request{url=URL}, Think, {SubstFlag, MatchRegExp, true, Server, {HostHeader, PrevScheme},Tab,Id}).
View
75 src/tsung_controller/ts_config_server.erl
@@ -48,7 +48,7 @@
%%--------------------------------------------------------------------
%% External exports
-export([start_link/1, read_config/1, get_req/2, get_next_session/1,
- get_client_config/1, newbeam/1, newbeam/2, get_server_config/0,
+ get_client_config/1, newbeam/1, newbeam/2,
get_monitor_hosts/0, encode_filename/1, decode_filename/1,
endlaunching/1, status/0, start_file_server/1, get_user_agents/0]).
@@ -128,13 +128,6 @@ get_client_config(Host)->
gen_server:call({global,?MODULE},{get_client_config, Host}, ?config_timeout).
%%--------------------------------------------------------------------
-%% Function: get_server_config/0
-%% Returns: {Hostname, Port (integer), Protocol type (ssl|gen_tcp|gen_udp)}
-%%--------------------------------------------------------------------
-get_server_config()->
- gen_server:call({global,?MODULE},{get_server_config}).
-
-%%--------------------------------------------------------------------
%% Function: get_monitor_hosts/0
%% Returns: [Hosts]
%%--------------------------------------------------------------------
@@ -245,7 +238,8 @@ handle_call({get_next_session, HostName}, From, State) ->
{value, Client} = lists:keysearch(HostName, #client.host, Config#config.clients),
- {ok,IP,NewPos} = choose_client_ip(Client,State#state.lastips),
+ {ok,IP,TmpPos} = choose_client_ip(Client,State#state.lastips),
+ {ok, Server, NewPos} = choose_server(Config#config.servers, TmpPos),
?DebugF("get new session for ~p~n",[From]),
NewState=State#state{lastips=NewPos},
@@ -254,7 +248,7 @@ handle_call({get_next_session, HostName}, From, State) ->
?LOGF("Session ~p choosen~n",[Id],?INFO),
case ets:lookup(Tab, {Id, size}) of
[{_, Size}] ->
- {reply, {ok, {Session, Size, IP}}, NewState};
+ {reply, {ok, {Session, Size, IP, Server}}, NewState};
Other ->
{reply, {error, Other}, NewState}
end;
@@ -262,12 +256,6 @@ handle_call({get_next_session, HostName}, From, State) ->
{reply, {error, Other}, NewState}
end;
-%%
-handle_call({get_server_config}, _From, State) ->
- Config = State#state.config,
- Server = Config#config.server,
- {reply,{Server#server.host, Server#server.port, Server#server.type}, State};
-
%%
handle_call({get_client_config, Host}, _From, State) ->
?DebugF("get_client_config from ~p~n",[Host]),
@@ -423,27 +411,48 @@ code_change(_OldVsn, State, _Extra) ->
%%--------------------------------------------------------------------
%%----------------------------------------------------------------------
-%% Func: choose_client_ip/1
-%% Args: #client
+%% Func: choose_client_ip/2
+%% Args: #client, Dict
%% Purpose: choose an IP for a client
-%% Returns: {ok, {IP, NewList}} IP=IPv4 address {A1,A2,A3,A4}
+%% Returns: {ok, IP, NewDict} IP=IPv4 address {A1,A2,A3,A4}
%%----------------------------------------------------------------------
-choose_client_ip(#client{ip = []},RR) -> %% no ip, return default 0.0.0.0
- {ok, {0,0,0,0}, RR};
-choose_client_ip(#client{ip = [IP]},RR) -> %% only one IP
- {ok, IP, RR};
-choose_client_ip(Client,undefined) ->
- choose_client_ip(Client, dict:new());
+%% FIXME: and for IPV6 ?
choose_client_ip(#client{ip = IPList, host=Host},RRval) ->
- case dict:find(Host, RRval) of
+ choose_rr(IPList, Host, RRval, {0,0,0,0}).
+
+%%----------------------------------------------------------------------
+%% Func: choose_server/2
+%% Args: List, Dict
+%% Purpose: choose a server for a new client
+%% Returns: {ok, #server, NewDict}
+%%----------------------------------------------------------------------
+choose_server(ServerList, RRVal) ->
+ choose_rr(ServerList, server, RRVal, #server{}).
+
+%%----------------------------------------------------------------------
+%% Func: choose_rr/4
+%% Args: List, Key, Dict, Default
+%% Purpose: choose an value in list in a round robin way. Use last
+%% value stored in dict with key Key.
+% Return Default if list is empty
+%% Returns: {ok, Val, NewDict}
+%%----------------------------------------------------------------------
+choose_rr([],_, RR, Def) -> % no val, return default
+ {ok, Def, RR};
+choose_rr([Val],_,RR,_) -> % only one value
+ {ok, Val, RR};
+choose_rr(List,Key,undefined,Def) ->
+ choose_rr(List,Key, dict:new(),Def);
+choose_rr(List,Key,RRval,_) ->
+ case dict:find(Key, RRval) of
{ok, NextVal} ->
- NewRR = dict:update_counter(Host,1,RRval),
- I = (NextVal rem length(IPList))+1, %% round robin
- {ok, lists:nth(I, IPList), NewRR};
- error ->
- Dict = dict:store(Host,1,RRval),
- [IP|_] = IPList,
- {ok, IP, Dict}
+ NewRR = dict:update_counter(Key,1,RRval),
+ I = (NextVal rem length(List))+1, % round robin
+ {ok, lists:nth(I, List), NewRR};
+ error -> % first used of this key, init index to 1
+ Dict = dict:store(Key,1,RRval),
+ Val = lists:nth(1,List),
+ {ok, Val, Dict}
end.
%%----------------------------------------------------------------------
View
3  tsung-1.0.dtd
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8" ?>
-<!ELEMENT tsung (information?, clients, server, monitoring?, load, options?, sessions)>
+<!ELEMENT tsung (information?, clients, servers, monitoring?, load, options?, sessions)>
<!ELEMENT information (name|description|username|organisation)*>
@@ -14,6 +14,7 @@
loglevel (emergency|critical|error|warning|notice|info|debug) "notice"
version NMTOKEN #IMPLIED>
+<!ELEMENT servers (server+)>
<!ELEMENT server EMPTY>
<!ATTLIST server
host NMTOKEN #REQUIRED
Please sign in to comment.
Something went wrong with that request. Please try again.