4
%% Copyright Ericsson AB 2010. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
20
%%----------------------------------------------------------------------
21
%% Purpose: The HDLT client module.
22
%% This is the traffic generator
23
%%----------------------------------------------------------------------
40
-include("hdlt_logger.hrl").
42
-define(CTRL, hdlt_ctrl).
43
-define(PROXY, hdlt_proxy).
63
proc_lib:start_link(?MODULE, proxy, [Debug]).
66
(catch erlang:send(?PROXY, stop)),
72
start_service(Args) ->
73
?PROXY ! {start_client, Args, self()},
76
%% ?LOG("client service started"),
84
?PROXY ! {node_info, self()},
86
{node_info, NodeInfo} ->
91
%% ---------------------------------------------------------------------
97
process_flag(trap_exit, true),
98
erlang:register(?PROXY, self()),
99
SName = lists:flatten(
100
io_lib:format("HDLT PROXY[~p,~p]", [self(), node()])),
103
?LOG("starting", []),
104
Ref = await_for_controller(10),
105
CtrlNode = node(Ref),
106
erlang:monitor_node(CtrlNode, true),
107
proc_lib:init_ack({ok, self()}),
108
?DEBUG("started", []),
109
proxy_loop(Ref, CtrlNode, undefined).
111
await_for_controller(N) when N > 0 ->
112
case global:whereis_name(hdlt_ctrl) of
113
Pid when is_pid(Pid) ->
114
erlang:monitor(process, Pid);
117
await_for_controller(N-1)
119
await_for_controller(_) ->
120
proc_lib:init_ack({error, controller_not_found, nodes()}),
125
proxy_loop(Ref, CtrlNode, Client) ->
126
?DEBUG("await command", []),
134
?LOG("start the inets service framework", []),
135
%% inets:enable_trace(max, "/tmp/inets-httpc-trace.log", all),
136
case (catch inets:start()) of
138
?LOG("framework started", []),
139
proxy_loop(Ref, CtrlNode, Client);
141
?LOG("failed starting inets service framework: "
142
"~n Error: ~p", [Error]),
147
{start_client, Args, From} ->
148
?LOG("start client with"
149
"~n Args: ~p", [Args]),
150
Client2 = spawn_link(fun() -> client(Args) end),
151
From ! client_started,
152
proxy_loop(Ref, CtrlNode, Client2);
157
proxy_loop(Ref, CtrlNode, Client);
160
?LOG("received requets for node info", []),
161
NodeInfo = get_node_info(),
162
Pid ! {node_info, NodeInfo},
163
proxy_loop(Ref, CtrlNode, Client);
165
{'EXIT', Client, normal} ->
166
?LOG("received normal exit message from client (~p)",
170
{'EXIT', Client, Reason} ->
171
?INFO("received exit message from client (~p)"
172
"~n Reason: ~p", [Client, Reason]),
173
%% Unexpected client termination, inform the controller and die
174
global:send(hdlt_ctrl, {client_exit, Client, node(), Reason}),
175
exit({client_exit, Reason});
177
{nodedown, CtrlNode} ->
178
?LOG("received nodedown for controller node - terminate", []),
181
{'DOWN', Ref, process, _, _} ->
182
?INFO("received DOWN message for controller - terminate", []),
183
%% The controller has terminated, dont care why, time to die
190
%% ---------------------------------------------------------------------
192
%% The client process
195
client([SocketType, CertFile, URLBase, Sizes, Time, SendRate, Debug]) ->
196
SName = lists:flatten(
197
io_lib:format("HDLT CLIENT[~p,~p]", [self(), node()])),
203
"~n SendRate: ~p", [SocketType, Time, SendRate]),
204
httpc:set_options([{max_pipeline_length, 0}]),
206
(SocketType =:= ssl) orelse
207
(SocketType =:= ossl) orelse
208
(SocketType =:= essl) ->
209
%% Ensure crypto and ssl started:
215
State = #state{mode = idle,
218
send_rate = SendRate,
220
socket_type = SocketType,
221
cert_file = CertFile},
222
?DEBUG("started", []),
225
%% The point is to first start all client nodes and then this
226
%% process. Then, when they are all started, the go-ahead, go,
227
%% message is sent to let them lose at the same time.
228
client_loop(#state{mode = idle,
230
send_rate = SendRate} = State) ->
231
?DEBUG("[idle] awaiting the go command", []),
234
?LOG("[idle] received go", []),
235
erlang:send_after(Time, self(), stop),
236
NewState = send_requests(State, SendRate),
237
client_loop(NewState#state{mode = generating,
238
nof_reqs = SendRate})
241
%% In this mode the client is generating traffic.
242
%% It will continue to do so until the stop message
244
client_loop(#state{mode = generating} = State) ->
247
?LOG("[generating] received stop", []),
248
StopTime = timestamp(),
250
client_loop(State#state{mode = stopping, stop_time = StopTime});
252
{http, {_, {{_, 200, _}, _, _}}} ->
253
%% ?DEBUG("[generating] received reply - send another request", []),
254
NewState = send_requests(State, 1),
255
client_loop(NewState#state{nof_reps = NewState#state.nof_reps + 1,
256
nof_reqs = NewState#state.nof_reqs + 1});
258
{http, {ReqId, {error, Reason}}} ->
259
?INFO("[generating] request ~p failed: "
263
[ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]),
264
exit({Reason, generating, State#state.nof_reqs, State#state.nof_reps});
267
?LOG("[generating] received unexpected message: "
269
unexpected_data(Else),
273
%% The client no longer issues any new requests, instead it
274
%% waits for replies for all the oustanding requests to
276
client_loop(#state{mode = stopping,
278
last_req = LastReqId} = State) ->
280
{http, {LastReqId, {{_, 200, _}, _, _}}} ->
281
?DEBUG("[stopping] received reply for last request (~p)", [LastReqId]),
282
time_to_complete(State),
285
{http, {ReqId, {{_, 200, _}, _, _}}} ->
286
?DEBUG("[stopping] received reply ~p", [ReqId]),
289
{http, {ReqId, {error, Reason}}} ->
290
?INFO("[stopping] request ~p failed: "
294
[ReqId, Reason, State#state.nof_reqs, State#state.nof_reps]),
295
exit({Reason, stopping, State#state.nof_reqs, State#state.nof_reps});
298
?LOG("[stopping] received unexpected message: "
300
unexpected_data(Else),
305
"~n Number of requests: ~p"
306
"~n Number of replies: ~p",
307
[State#state.nof_reqs, State#state.nof_reps]),
308
exit({timeout, State#state.nof_reqs, State#state.nof_reps})
311
req_reply(#state{nof_reqs = NofReqs, nof_reps = NofReps}) ->
312
load_data({req_reply, node(), NofReqs, NofReps}).
314
time_to_complete(#state{stop_time = StopTime}) ->
315
StoppedTime = os:timestamp(),
316
load_data({time_to_complete, node(), StopTime, StoppedTime}).
319
global:send(?CTRL, {load_data, Data}).
321
unexpected_data(Else) ->
322
global:send(?CTRL, {unexpected_data, Else}).
325
send_requests(#state{sizes = Sizes} = State, N) ->
326
send_requests(State, N, Sizes).
328
send_requests(State, 0, Sizes) ->
329
State#state{sizes = Sizes};
330
send_requests(#state{socket_type = SocketType,
331
cert_file = CertFile} = State, N, [Sz | Sizes]) ->
332
URL = lists:flatten(io_lib:format("~s~w", [State#state.url, Sz])),
340
SslOpts = [{verify, 0},
341
{certfile, CertFile},
342
{keyfile, CertFile}],
347
[{ssl, {ossl, SslOpts}}];
349
[{ssl, {essl, SslOpts}}]
352
Options = [{sync, false}],
353
{ok, Ref} = httpc:request(Method, Request, HTTPOptions, Options),
354
send_requests(State#state{last_req = Ref}, N-1, lists:append(Sizes, [Sz])).
362
[{cpu_topology, erlang:system_info(cpu_topology)},
363
{heap_type, erlang:system_info(heap_type)},
364
{nof_schedulers, erlang:system_info(schedulers)},
365
{otp_release, erlang:system_info(otp_release)},
366
{version, erlang:system_info(version)},
367
{system_version, erlang:system_info(system_version)},
368
{system_architecture, erlang:system_info(system_architecture)}].