Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

209 строки
7.9 KiB

10 лет назад
7 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
9 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
7 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
7 лет назад
10 лет назад
9 лет назад
10 лет назад
9 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. -module(ibrowse_load_test).
  2. %%-compile(export_all).
  3. -export([
  4. random_seed/0,
  5. start/3,
  6. query_state/0,
  7. shutdown/0,
  8. start_1/3,
  9. calculate_timings/0,
  10. get_mmv/2,
  11. spawn_workers/2,
  12. spawn_workers/4,
  13. wait_for_workers/1,
  14. worker_loop/2,
  15. update_unknown_counter/2
  16. ]).
  17. -ifdef(new_rand).
  18. -define(RAND, rand).
  19. random_seed() ->
  20. ok.
  21. -else.
  22. -define(RAND, random).
  23. random_seed() ->
  24. random:seed(os:timestamp()).
  25. -endif.
  26. -define(ibrowse_load_test_counters, ibrowse_load_test_counters).
  27. start(Num_workers, Num_requests, Max_sess) ->
  28. proc_lib:spawn(fun() ->
  29. start_1(Num_workers, Num_requests, Max_sess)
  30. end).
  31. query_state() ->
  32. ibrowse_load_test ! query_state.
  33. shutdown() ->
  34. ibrowse_load_test ! shutdown.
  35. start_1(Num_workers, Num_requests, Max_sess) ->
  36. register(ibrowse_load_test, self()),
  37. application:start(ibrowse),
  38. application:set_env(ibrowse, inactivity_timeout, 5000),
  39. Ulimit = os:cmd("ulimit -n"),
  40. case catch list_to_integer(string:strip(Ulimit, right, $\n)) of
  41. X when is_integer(X), X > 3000 ->
  42. ok;
  43. X ->
  44. io:format("Load test not starting. {insufficient_value_for_ulimit, ~p}~n", [X]),
  45. exit({insufficient_value_for_ulimit, X})
  46. end,
  47. ets:new(?ibrowse_load_test_counters, [named_table, public]),
  48. ets:new(ibrowse_load_timings, [named_table, public]),
  49. try
  50. ets:insert(?ibrowse_load_test_counters, [{success, 0},
  51. {failed, 0},
  52. {timeout, 0},
  53. {retry_later, 0},
  54. {one_request_only, 0}
  55. ]),
  56. ibrowse:set_max_sessions("localhost", 8081, Max_sess),
  57. Start_time = os:timestamp(),
  58. Workers = spawn_workers(Num_workers, Num_requests),
  59. erlang:send_after(1000, self(), print_diagnostics),
  60. ok = wait_for_workers(Workers),
  61. End_time = os:timestamp(),
  62. Time_in_secs = trunc(round(timer:now_diff(End_time, Start_time) / 1000000)),
  63. Req_count = Num_workers * Num_requests,
  64. [{_, Success_count}] = ets:lookup(?ibrowse_load_test_counters, success),
  65. case Success_count == Req_count of
  66. true ->
  67. io:format("Test success. All requests succeeded~n", []);
  68. false when Success_count > 0 ->
  69. io:format("Test failed. Some successes~n", []);
  70. false ->
  71. io:format("Test failed. ALL requests FAILED~n", [])
  72. end,
  73. case Time_in_secs > 0 of
  74. true ->
  75. io:format("Reqs/sec achieved : ~p~n", [trunc(round(Success_count / Time_in_secs))]);
  76. false ->
  77. ok
  78. end,
  79. io:format("Load test results:~n~p~n", [ets:tab2list(?ibrowse_load_test_counters)]),
  80. io:format("Timings: ~p~n", [calculate_timings()])
  81. catch Err ->
  82. io:format("Err: ~p~n", [Err])
  83. after
  84. ets:delete(?ibrowse_load_test_counters),
  85. ets:delete(ibrowse_load_timings),
  86. unregister(ibrowse_load_test)
  87. end.
  88. calculate_timings() ->
  89. {Max, Min, Mean} = get_mmv(ets:first(ibrowse_load_timings), {0, 9999999, 0}),
  90. Variance = trunc(round(ets:foldl(fun({_, X}, X_acc) ->
  91. (X - Mean)*(X-Mean) + X_acc
  92. end, 0, ibrowse_load_timings) / ets:info(ibrowse_load_timings, size))),
  93. Std_dev = trunc(round(math:sqrt(Variance))),
  94. {ok, [{max, Max},
  95. {min, Min},
  96. {mean, Mean},
  97. {variance, Variance},
  98. {standard_deviation, Std_dev}]}.
  99. get_mmv('$end_of_table', {Max, Min, Total}) ->
  100. Mean = trunc(round(Total / ets:info(ibrowse_load_timings, size))),
  101. {Max, Min, Mean};
  102. get_mmv(Key, {Max, Min, Total}) ->
  103. [{_, V}] = ets:lookup(ibrowse_load_timings, Key),
  104. get_mmv(ets:next(ibrowse_load_timings, Key), {max(Max, V), min(Min, V), Total + V}).
  105. spawn_workers(Num_w, Num_r) ->
  106. spawn_workers(Num_w, Num_r, self(), []).
  107. spawn_workers(0, _Num_requests, _Parent, Acc) ->
  108. lists:reverse(Acc);
  109. spawn_workers(Num_workers, Num_requests, Parent, Acc) ->
  110. Pid_ref = spawn_monitor(fun() ->
  111. random_seed(),
  112. case catch worker_loop(Parent, Num_requests) of
  113. {'EXIT', Rsn} ->
  114. io:format("Worker crashed with reason: ~p~n", [Rsn]);
  115. _ ->
  116. ok
  117. end
  118. end),
  119. spawn_workers(Num_workers - 1, Num_requests, Parent, [Pid_ref | Acc]).
  120. wait_for_workers([]) ->
  121. ok;
  122. wait_for_workers([{Pid, Pid_ref} | T] = Pids) ->
  123. receive
  124. {done, Pid} ->
  125. wait_for_workers(T);
  126. {done, Some_pid} ->
  127. wait_for_workers([{Pid, Pid_ref} | lists:keydelete(Some_pid, 1, T)]);
  128. print_diagnostics ->
  129. io:format("~1000.p~n", [ibrowse:get_metrics()]),
  130. erlang:send_after(1000, self(), print_diagnostics),
  131. wait_for_workers(Pids);
  132. query_state ->
  133. io:format("Waiting for ~p~n", [Pids]),
  134. wait_for_workers(Pids);
  135. shutdown ->
  136. io:format("Shutting down on command. Still waiting for ~p workers~n", [length(Pids)]);
  137. {'DOWN', _, process, _, normal} ->
  138. wait_for_workers(Pids);
  139. {'DOWN', _, process, Down_pid, Rsn} ->
  140. io:format("Worker ~p died. Reason: ~p~n", [Down_pid, Rsn]),
  141. wait_for_workers(lists:keydelete(Down_pid, 1, Pids));
  142. X ->
  143. io:format("Recvd unknown msg: ~p~n", [X]),
  144. wait_for_workers(Pids)
  145. end.
  146. worker_loop(Parent, 0) ->
  147. Parent ! {done, self()};
  148. worker_loop(Parent, N) ->
  149. Delay = ?RAND:uniform(100),
  150. Url = case Delay rem 10 of
  151. %% Change 10 to some number between 0-9 depending on how
  152. %% much chaos you want to introduce into the server
  153. %% side. The higher the number, the more often the
  154. %% server will close a connection after serving the
  155. %% first request, thereby forcing the client to
  156. %% retry. Any number of 10 or higher will disable this
  157. %% chaos mechanism
  158. 10 ->
  159. ets:update_counter(?ibrowse_load_test_counters, one_request_only, 1),
  160. "http://localhost:8081/ibrowse_handle_one_request_only";
  161. _ ->
  162. "http://localhost:8081/blah"
  163. end,
  164. Start_time = os:timestamp(),
  165. Res = ibrowse:send_req(Url, [], get),
  166. End_time = os:timestamp(),
  167. Time_taken = trunc(round(timer:now_diff(End_time, Start_time) / 1000)),
  168. ets:insert(ibrowse_load_timings, {os:timestamp(), Time_taken}),
  169. case Res of
  170. {ok, "200", _, _} ->
  171. ets:update_counter(?ibrowse_load_test_counters, success, 1);
  172. {error, req_timedout} ->
  173. ets:update_counter(?ibrowse_load_test_counters, timeout, 1);
  174. {error, retry_later} ->
  175. ets:update_counter(?ibrowse_load_test_counters, retry_later, 1);
  176. {error, Reason} ->
  177. update_unknown_counter(Reason, 1);
  178. _ ->
  179. io:format("~p -- Res: ~p~n", [self(), Res]),
  180. ets:update_counter(?ibrowse_load_test_counters, failed, 1)
  181. end,
  182. timer:sleep(Delay),
  183. worker_loop(Parent, N - 1).
  184. update_unknown_counter(Counter, Inc_val) ->
  185. case catch ets:update_counter(?ibrowse_load_test_counters, Counter, Inc_val) of
  186. {'EXIT', _} ->
  187. ets:insert_new(?ibrowse_load_test_counters, {Counter, 0}),
  188. update_unknown_counter(Counter, Inc_val);
  189. _ ->
  190. ok
  191. end.