Examples
HTTP
[{"test" : {"id" : "payment-test",
"phases" : ["payment-phase"],
"plugins" : ["payment-server"]
}},
{"plugin" : {"id" : "payment-server",
"plugin_info"
: {"http-plugin" : {"servers" : [{"host" : "10.100.0.120",
"port" : 5050,
"ssl" : false}
]
}
}
}
},
{"phase" : {"id" : "payment-phase",
"arrival_rate" : 10,
"duration" : 600000,
"concurrent_scenarios" : 250,
"rate" : 500,
"scenarios" : [{"payment-scenario" : 1}]}},
{"scenario" :
{"id" : "payment-scenario",
"actions" : [
{"data-csv" : {"file" : "users_list.csv",
"name" : "all_users"
}
},
{"data-csv_value" : {"name" : "all_users",
"fields" : [1, 2],
"vars" : ["username", "password"]
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "POST",
"path" : "/shop/session",
"body" : {"jsonpath-multiset" :
{
"object" :
"{\"username\": \"user\",
\"password\": \"password\"}",
"pairs" : [
{"value" : {"var-get" : {"name" : "username"}},
"path" : "username"},
{"value" : {"var-get" : {"name" : "password"}},
"path" : "password"}
]
}
}
}
},
{"jsonpath-get" : {"path" : "headers.location",
"variable" : "shoppingSession"
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "GET",
"path" : {"var-get" : {"name" :
"shoppingSession"}}
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "GET",
"path" : {"url-join" :
{ "root" : "/shop/user",
"path" : {"var-get" : {"name" : "username"}
}
}
}
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "POST",
"path" : "/shop/basket/sonyDSCW800compactdigitalcamera",
"body" : "{\"price\": {\"amount\" : 59,
\"currency\": \"GBP\"}}"
}
},
{"jsonpath-get" : {"path" : "headers.location",
"variable" : "productDetails"
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "GET",
"path" : {"var-get" : {"name" : "productDetails"}}
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "GET",
"path" : "/shop/payment"
}
},
{"jsonpath-get" : {"path" : "body.payment_methods[0].uri",
"variable" : "paymentMethod"
}
},
{"http-request" : {"plugin_id" : "payment-server",
"method" : "POST",
"path" : {"var-get" : {"name" : "paymentMethod"}},
"body" : "{\"amount\" : 59, \"currency\" : \"GBP\",
\"security_code\" : \"123\"}"
}
}
]}}
]
XMPP
#!/usr/bin/env escript
%% Adapted from https://github.com/esl/amoc
-include_lib("exml/include/exml.hrl").
%% ===================================================================
-define(HOST, <<"localhost">>). %% The virtual host served by the server
-define(SERVER_IPS, {<<"127.0.0.1">>}). %% Tuple of servers, for example {<<"10.100.0.21">>, <<"10.100.0.22">>}
-define(CHECKER_SESSIONS_INDICATOR, 10). %% How often a checker session should be generated
-define(SLEEP_TIME_AFTER_SCENARIO, 10000). %% wait 10s after scenario before disconnecting
-define(NUMBER_OF_PREV_NEIGHBOURS, 4).
-define(NUMBER_OF_NEXT_NEIGHBOURS, 4).
-define(NUMBER_OF_SEND_MESSAGE_REPEATS, 73).
-define(SLEEP_TIME_AFTER_EVERY_MESSAGE, 20000).
%% ===================================================================
%% Counters and histograms
-define(MESSAGES_CT, [<<"xmpp">>, <<"counter">>, <<"messages_sent">>]).
-define(MESSAGE_TTD_H, [<<"xmpp">>, <<"histogram">>, <<"message_ttd">>]).
-define(CONNECTION_CT, [<<"xmpp">>, <<"counter">>, <<"connections">>]).
-define(CONNECTION_H, [<<"xmpp">>, <<"histogram">>, <<"connection">>]).
-define(CONNECTION_FAILURE_CT, [<<"xmpp">>, <<"counter">>, <<"connection_failures">>]).
%% ===================================================================
%% Define initial values.
%% The regulation will use the load_regulation_targets/2 callback afterwards
-rate(150).
-workers(100).
-arrival_rate(20). %% Number of new scenarios started per second
%% Set total of unique ids, will be split among nodes
%% Used to generate the user ids later on. Retrieved from the process
%% dictionary erlang:get(unique_id).
-unique_ids(15000).
%% ===================================================================
main([]) ->
MyId = erlang:get(unique_id),
Cfg = make_user(MyId, <<"res1">>),
IsChecker = MyId rem ?CHECKER_SESSIONS_INDICATOR == 0,
{ConnectionTime, ConnectionResult} = timer:tc(escalus_connection, start, [Cfg]),
Client = case ConnectionResult of
{ok, ConnectedClient, _, _} ->
loader_metrics:update_counter(?CONNECTION_CT, 1),
loader_metrics:update_histogram(?CONNECTION_H, ConnectionTime),
ConnectedClient;
_Error ->
loader_metrics:update_counter(?CONNECTION_FAILURE_CT, 1),
throw(abort_scenario)
end,
do(IsChecker, MyId, Client),
timer:sleep(?SLEEP_TIME_AFTER_SCENARIO),
send_presence_unavailable(Client),
escalus_connection:stop(Client).
%% This callback may return a list of metrics to regulate the load.
%% Otherwise, it's also useful to create all XMPP specific metrics.
init_load_regulation_metrics() ->
loader_metrics:new_counter(?MESSAGES_CT),
loader_metrics:new_histogram(?MESSAGE_TTD_H),
loader_metrics:new_counter(?CONNECTION_CT),
loader_metrics:new_counter(?CONNECTION_FAILURE_CT),
loader_metrics:new_histogram(?CONNECTION_H),
{ok, []}.
%% Regulation callback. Megaload will use the number of workers and rate
%% to set the maximum generated by the system. These could be capped lower
%% if the machine is not able to cope with the amount of load.
%% The gauges 'global_gauge_maxRate' and 'global_gauge_maxWorker' will show
%% this capped limits in the UI.
%% Megaload automatically regulates the number of workers, however the rate
%% will depend on the scenario described in the escript. Regulation will
%% only be triggered by short-lived scenarios.
%% Nonetheless, this kind of example regulates the rate with its
%% own timers, so built-in Megaload regulation would not be needed.
load_regulation_targets(_Metrics,_OldTargets) ->
NewTargets = {{workers, 1500}, {rate, 3000}},
{ok, NewTargets}.
user_spec(ProfileId, Password, Res) ->
[ {username, ProfileId},
{server, ?HOST},
{host, pick_server(?SERVER_IPS)},
{password, Password},
{carbons, false},
{stream_management, false},
{resource, Res}
].
make_user(Id, R) ->
BinId = integer_to_binary(Id),
ProfileId = <<"user_", BinId/binary>>,
Password = <<"password_", BinId/binary>>,
user_spec(ProfileId, Password, R).
do(false, MyId, Client) ->
escalus_connection:set_filter_predicate(Client, none),
send_presence_available(Client),
timer:sleep(5000),
NeighbourIds = lists:delete(MyId, lists:seq(max(1,MyId-?NUMBER_OF_PREV_NEIGHBOURS),
MyId+?NUMBER_OF_NEXT_NEIGHBOURS)),
send_messages_many_times(Client, ?SLEEP_TIME_AFTER_EVERY_MESSAGE, NeighbourIds);
do(_Other, _MyId, Client) ->
send_presence_available(Client),
receive_forever(Client).
receive_forever(Client) ->
Stanza = escalus_connection:get_stanza(Client, message, infinity),
Now = from_now(os:timestamp()),
case Stanza of
#xmlel{name = <<"message">>, attrs=Attrs} ->
case lists:keyfind(<<"timestamp">>, 1, Attrs) of
{_, Sent} ->
TTD = (Now - binary_to_integer(Sent)),
loader_metrics:update_histogram(?MESSAGE_TTD_H, TTD);
_ ->
ok
end;
_ ->
ok
end,
receive_forever(Client).
send_presence_available(Client) ->
Pres = escalus_stanza:presence(<<"available">>),
escalus_connection:send(Client, Pres).
send_presence_unavailable(Client) ->
Pres = escalus_stanza:presence(<<"unavailable">>),
escalus_connection:send(Client, Pres).
send_messages_many_times(Client, MessageInterval, NeighbourIds) ->
S = fun(_) ->
send_messages_to_neighbors(Client, NeighbourIds, MessageInterval)
end,
lists:foreach(S, lists:seq(1, ?NUMBER_OF_SEND_MESSAGE_REPEATS)).
send_messages_to_neighbors(Client, TargetIds, SleepTime) ->
[send_message(Client, make_jid(TargetId), SleepTime)
|| TargetId <- TargetIds].
send_message(Client, ToId, SleepTime) ->
MsgIn = make_message(ToId),
TimeStamp = integer_to_binary(from_now(os:timestamp())),
escalus_connection:send(Client, escalus_stanza:setattr(MsgIn, <<"timestamp">>, TimeStamp)),
loader_metrics:update_counter(?MESSAGES_CT, 1),
%% Increases counters of total, successful requests and histograms of
%% requests per second.
loader_requests:inc_successful(),
timer:sleep(SleepTime).
make_message(ToId) ->
Body = <<"hello sir, you are a gentelman and a scholar.">>,
Id = escalus_stanza:id(),
escalus_stanza:set_id(escalus_stanza:chat_to(ToId, Body), Id).
make_jid(Id) ->
BinInt = integer_to_binary(Id),
ProfileId = <<"user_", BinInt/binary>>,
Host = ?HOST,
<< ProfileId/binary, "@", Host/binary >>.
pick_server(Servers) ->
S = size(Servers),
N = erlang:phash2(self(), S) + 1,
element(N, Servers).
from_now({MegaSecs,Secs,Usecs}) ->
(MegaSecs * 1000000 + Secs) * 1000000 + Usecs.