wrk.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /*
  2. * @build: make examples
  3. * @server bin/httpd -s restart -d
  4. * @client bin/curl -v http://127.0.0.1:8080/
  5. * @usage: bin/wrk -c 1000 -d 10 -t 4 http://127.0.0.1:8080/
  6. *
  7. */
  8. #include <vector>
  9. #include <algorithm>
  10. #include "hv.h"
  11. #include "hmain.h" // import parse_opt
  12. #include "hloop.h"
  13. #include "EventLoopThreadPool.h"
  14. #include "HttpMessage.h"
  15. #include "HttpParser.h"
  16. using namespace hv;
  17. static const char options[] = "hvc:d:t:b:i:";
  18. static const char detail_options[] = R"(
  19. -h Print help infomation
  20. -v Show verbose infomation
  21. -c <connections> Number of connections, default: 1000
  22. -d <duration> Duration of test, default: 10s
  23. -t <threads> Number of threads, default: 4
  24. -b <bytes> Content-Length, default: 0
  25. -i <interval> Interval of timer, default: 0ms
  26. )";
  27. static int connections = 1000;
  28. static int duration = 10; // s
  29. static int threads = 4;
  30. static int bytes = 0; // byte
  31. static int interval = 0; // ms
  32. static bool verbose = false;
  33. static const char* url = NULL;
  34. static bool https = false;
  35. static char ip[64] = "127.0.0.1";
  36. static int port = 80;
  37. static bool stop = false;
  38. static HttpRequestPtr request;
  39. static std::string request_msg;
  40. typedef struct connection_s {
  41. hio_t* io;
  42. HttpParserPtr parser;
  43. HttpResponsePtr response;
  44. uint64_t request_cnt;
  45. uint64_t response_cnt;
  46. uint64_t ok_cnt;
  47. uint64_t readbytes;
  48. uint64_t start_time;
  49. std::vector<int> delays;
  50. connection_s()
  51. : parser(HttpParser::New(HTTP_CLIENT, HTTP_V1))
  52. , response(std::make_shared<HttpResponse>())
  53. , request_cnt(0)
  54. , response_cnt(0)
  55. , ok_cnt(0)
  56. , readbytes(0)
  57. {
  58. response->http_cb = [](HttpMessage* res, http_parser_state state, const char* data, size_t size) {
  59. // wrk no need to save data to body
  60. };
  61. }
  62. void SendRequest() {
  63. start_time = hloop_now_ms(hevent_loop(io));
  64. hio_write(io, request_msg.data(), request_msg.size());
  65. ++request_cnt;
  66. parser->InitResponse(response.get());
  67. }
  68. bool RecvResponse(const char* data, int size) {
  69. readbytes += size;
  70. int nparse = parser->FeedRecvData(data, size);
  71. if (nparse != size) {
  72. fprintf(stderr, "http parse error!\n");
  73. hio_close(io);
  74. return false;
  75. }
  76. if (parser->IsComplete()) {
  77. int delay = hloop_now_ms(hevent_loop(io)) - start_time;
  78. delays.push_back(delay);
  79. ++response_cnt;
  80. if (response->status_code == HTTP_STATUS_OK) {
  81. ++ok_cnt;
  82. }
  83. return true;
  84. }
  85. return false;
  86. }
  87. } connection_t;
  88. static connection_t** conns = NULL;
  89. static std::atomic<int> connected_num(0);
  90. static std::atomic<int> disconnected_num(0);
  91. static unsigned int connect_start_time = 0;
  92. static void print_help() {
  93. printf("Usage: wrk [%s] <url>\n", options);
  94. printf("Options:\n%s\n", detail_options);
  95. }
  96. static void print_cmd() {
  97. printf("Running %ds test @ %s\n", duration, url);
  98. printf("%d threads and %d connections\n", threads, connections);
  99. }
  100. static void print_result() {
  101. uint64_t total_request_cnt = 0;
  102. uint64_t total_response_cnt = 0;
  103. uint64_t total_ok_cnt = 0;
  104. uint64_t total_readbytes = 0;
  105. uint64_t total_delay = 0;
  106. std::vector<int> delays;
  107. connection_t* conn = NULL;
  108. for (int i = 0; i < connections; ++i) {
  109. conn = conns[i];
  110. total_request_cnt += conn->request_cnt;
  111. total_response_cnt += conn->response_cnt;
  112. total_ok_cnt += conn->ok_cnt;
  113. total_readbytes += conn->readbytes;
  114. delays.insert(delays.end(), conn->delays.begin(), conn->delays.end());
  115. }
  116. std::sort(delays.begin(), delays.end());
  117. for (int i = 0; i < delays.size(); ++i) {
  118. total_delay += delays[i];
  119. }
  120. printf("%llu requests, %llu OK, %lluMB read in %ds\n",
  121. LLU(total_request_cnt),
  122. LLU(total_ok_cnt),
  123. LLU(total_readbytes >> 20),
  124. duration);
  125. printf("Requests/sec: %8llu\n", LLU(total_response_cnt / duration));
  126. printf("Transfer/sec: %8lluMB\n", LLU((total_readbytes / duration) >> 20));
  127. printf("Latency avg: %8llums\n", LLU(total_delay / delays.size()));
  128. printf("Percentage of the requests served within a certain time (ms)\n");
  129. printf(" 50%% %d\n", delays[delays.size() * 0.5]);
  130. printf(" 60%% %d\n", delays[delays.size() * 0.6]);
  131. printf(" 70%% %d\n", delays[delays.size() * 0.7]);
  132. printf(" 80%% %d\n", delays[delays.size() * 0.8]);
  133. printf(" 90%% %d\n", delays[delays.size() * 0.9]);
  134. printf(" 95%% %d\n", delays[delays.size() * 0.95]);
  135. printf(" 96%% %d\n", delays[delays.size() * 0.96]);
  136. printf(" 97%% %d\n", delays[delays.size() * 0.97]);
  137. printf(" 98%% %d\n", delays[delays.size() * 0.98]);
  138. printf(" 99%% %d\n", delays[delays.size() * 0.99]);
  139. printf(" 100%% %d (longest delay)\n", delays.back());
  140. }
  141. static void start_reconnect(hio_t* io);
  142. static void on_close(hio_t* io) {
  143. if (++disconnected_num == connections) {
  144. if (verbose) {
  145. printf("all disconnected\n");
  146. }
  147. }
  148. if (!stop) {
  149. // NOTE: nginx keepalive_requests = 100
  150. start_reconnect(io);
  151. }
  152. }
  153. static void on_recv(hio_t* io, void* buf, int readbytes) {
  154. connection_t* conn = (connection_t*)hevent_userdata(io);
  155. if (conn->RecvResponse((const char*)buf, readbytes)) {
  156. if (interval == 0) {
  157. conn->SendRequest();
  158. }
  159. }
  160. }
  161. static void send_heartbeat(hio_t* io) {
  162. connection_t* conn = (connection_t*)hevent_userdata(io);
  163. conn->SendRequest();
  164. }
  165. static void on_connect(hio_t* io) {
  166. if (++connected_num == connections) {
  167. if (verbose) {
  168. printf("all connected in %ums\n", gettick_ms() - connect_start_time);
  169. }
  170. }
  171. connection_t* conn = (connection_t*)hevent_userdata(io);
  172. if (interval == 0) {
  173. conn->SendRequest();
  174. } else {
  175. hio_set_heartbeat(io, interval, send_heartbeat);
  176. }
  177. hio_setcb_read(io, on_recv);
  178. hio_read(io);
  179. }
  180. static void start_connect(hloop_t* loop, connection_t* conn) {
  181. hio_t* io = hio_create_socket(loop, ip, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
  182. if (io == NULL) {
  183. perror("socket");
  184. exit(1);
  185. }
  186. conn->io = io;
  187. hevent_set_userdata(io, conn);
  188. if (https) {
  189. hio_enable_ssl(io);
  190. }
  191. tcp_nodelay(hio_fd(io), 1);
  192. hio_setcb_connect(io, on_connect);
  193. hio_setcb_close(io, on_close);
  194. hio_connect(io);
  195. }
  196. static void start_reconnect(hio_t* io) {
  197. hloop_t* loop = hevent_loop(io);
  198. connection_t* conn = (connection_t*)hevent_userdata(io);
  199. start_connect(loop, conn);
  200. }
  201. int main(int argc, char** argv) {
  202. // parse cmdline
  203. main_ctx_init(argc, argv);
  204. int ret = parse_opt(argc, argv, options);
  205. if (ret != 0) {
  206. print_help();
  207. exit(ret);
  208. }
  209. if (get_arg("h") || g_main_ctx.arg_list_size != 1) {
  210. print_help();
  211. exit(0);
  212. }
  213. url = g_main_ctx.arg_list[0];
  214. if (get_arg("v")) {
  215. verbose = true;
  216. }
  217. const char* strConnections = get_arg("c");
  218. const char* strDuration = get_arg("d");
  219. const char* strThreads = get_arg("t");
  220. const char* strBytes = get_arg("b");
  221. const char* strInterval = get_arg("i");
  222. if (strConnections) connections = atoi(strConnections);
  223. if (strDuration) duration = atoi(strDuration);
  224. if (strThreads) threads = atoi(strThreads);
  225. if (strBytes) bytes = atoi(strBytes);
  226. if (strInterval) interval = atoi(strInterval);
  227. print_cmd();
  228. // ParseUrl
  229. request = std::make_shared<HttpRequest>();
  230. request->url = url;
  231. request->ParseUrl();
  232. https = request->scheme == "https";
  233. const char* host = request->host.c_str();
  234. port = request->port;
  235. // ResolveAddr
  236. if (is_ipaddr(host)) {
  237. strcpy(ip, host);
  238. } else {
  239. sockaddr_u addr;
  240. if (ResolveAddr(host, &addr) != 0) {
  241. fprintf(stderr, "Could not resolve host: %s\n", host);
  242. exit(1);
  243. }
  244. sockaddr_ip(&addr, ip, sizeof(ip));
  245. }
  246. // Test connect
  247. printf("Connect to %s:%d ...\n", ip, port);
  248. int connfd = ConnectTimeout(ip, port);
  249. if (connfd < 0) {
  250. fprintf(stderr, "Could not connect to %s:%d\n", ip, port);
  251. exit(1);
  252. } else {
  253. closesocket(connfd);
  254. }
  255. // Dump request
  256. request->headers["User-Agent"] = std::string("libhv/") + hv_version();
  257. request->headers["Connection"] = "keep-alive";
  258. if (bytes > 0) {
  259. request->method = HTTP_POST;
  260. request->body = std::string(bytes, 'a');
  261. }
  262. request_msg = request->Dump(true, true);
  263. printf("%.*s", int(request_msg.size() - request->body.size()), request_msg.c_str());
  264. // EventLoopThreadPool
  265. EventLoopThreadPool loop_threads(threads);
  266. loop_threads.start(true);
  267. // connections
  268. connect_start_time = gettick_ms();
  269. conns = (connection_t**)malloc(sizeof(connection_t*) * connections);
  270. for (int i = 0; i < connections; ++i) {
  271. conns[i] = new connection_t;
  272. EventLoopPtr loop = loop_threads.nextLoop();
  273. hloop_t* hloop = loop->loop();
  274. loop->runInLoop(std::bind(start_connect, loop->loop(), conns[i]));
  275. }
  276. // stop after duration
  277. loop_threads.loop()->setTimeout(duration * 1000, [&loop_threads](TimerID timerID){
  278. stop = true;
  279. loop_threads.stop(false);
  280. });
  281. // wait loop_threads exit
  282. loop_threads.join();
  283. print_result();
  284. return 0;
  285. }