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

181 строка
7.4 KiB

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