1
0

wrk.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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 "hv.h"
  9. #include "hmain.h" // import parse_opt
  10. #include "hloop.h"
  11. #include "EventLoopThreadPool.h"
  12. #include "HttpMessage.h"
  13. #include "HttpParser.h"
  14. using namespace hv;
  15. static const char options[] = "hvc:d:t:";
  16. static const char detail_options[] = R"(
  17. -h Print help infomation
  18. -v Show verbose infomation
  19. -c <connections> Number of connections, default: 1000
  20. -d <duration> Duration of test, default: 10s
  21. -t <threads> Number of threads, default: 4
  22. )";
  23. static int connections = 1000;
  24. static int duration = 10;
  25. static int threads = 4;
  26. static bool verbose = false;
  27. static const char* url = NULL;
  28. static bool https = false;
  29. static char ip[64] = "127.0.0.1";
  30. static int port = 80;
  31. static bool stop = false;
  32. static HttpRequestPtr request;
  33. static std::string request_msg;
  34. typedef struct connection_s {
  35. hio_t* io;
  36. HttpParserPtr parser;
  37. HttpResponsePtr response;
  38. uint64_t request_cnt;
  39. uint64_t response_cnt;
  40. uint64_t ok_cnt;
  41. uint64_t readbytes;
  42. connection_s()
  43. : parser(HttpParser::New(HTTP_CLIENT, HTTP_V1))
  44. , response(std::make_shared<HttpResponse>())
  45. , request_cnt(0)
  46. , response_cnt(0)
  47. , ok_cnt(0)
  48. , readbytes(0)
  49. {
  50. response->http_cb = [](HttpMessage* res, http_parser_state state, const char* data, size_t size) {
  51. // wrk no need to save data to body
  52. };
  53. }
  54. void SendRequest() {
  55. hio_write(io, request_msg.data(), request_msg.size());
  56. ++request_cnt;
  57. parser->InitResponse(response.get());
  58. }
  59. bool RecvResponse(const char* data, int size) {
  60. readbytes += size;
  61. int nparse = parser->FeedRecvData(data, size);
  62. if (nparse != size) {
  63. fprintf(stderr, "http parse error!\n");
  64. hio_close(io);
  65. return false;
  66. }
  67. if (parser->IsComplete()) {
  68. ++response_cnt;
  69. if (response->status_code == HTTP_STATUS_OK) {
  70. ++ok_cnt;
  71. }
  72. return true;
  73. }
  74. return false;
  75. }
  76. } connection_t;
  77. static connection_t** conns = NULL;
  78. static std::atomic<int> connected_num(0);
  79. static std::atomic<int> disconnected_num(0);
  80. static void print_help() {
  81. printf("Usage: wrk [%s] <url>\n", options);
  82. printf("Options:\n%s\n", detail_options);
  83. }
  84. static void print_cmd() {
  85. printf("Running %ds test @ %s\n", duration, url);
  86. printf("%d threads and %d connections\n", threads, connections);
  87. }
  88. static void print_result() {
  89. uint64_t total_request_cnt = 0;
  90. uint64_t total_response_cnt = 0;
  91. uint64_t total_ok_cnt = 0;
  92. uint64_t total_readbytes = 0;
  93. connection_t* conn = NULL;
  94. for (int i = 0; i < connections; ++i) {
  95. conn = conns[i];
  96. total_request_cnt += conn->request_cnt;
  97. total_response_cnt += conn->response_cnt;
  98. total_ok_cnt += conn->ok_cnt;
  99. total_readbytes += conn->readbytes;
  100. }
  101. printf("%llu requests, %llu OK, %lluMB read in %ds\n",
  102. LLU(total_request_cnt),
  103. LLU(total_ok_cnt),
  104. LLU(total_readbytes >> 20),
  105. duration);
  106. printf("Requests/sec: %8llu\n", LLU(total_response_cnt / duration));
  107. printf("Transfer/sec: %8lluMB\n", LLU((total_readbytes / duration) >> 20));
  108. }
  109. static void start_reconnect(hio_t* io);
  110. static void on_close(hio_t* io) {
  111. if (++disconnected_num == connections) {
  112. if (verbose) {
  113. printf("all disconnected\n");
  114. }
  115. }
  116. if (!stop) {
  117. // NOTE: nginx keepalive_requests = 100
  118. start_reconnect(io);
  119. }
  120. }
  121. static void on_recv(hio_t* io, void* buf, int readbytes) {
  122. connection_t* conn = (connection_t*)hevent_userdata(io);
  123. if (conn->RecvResponse((const char*)buf, readbytes)) {
  124. conn->SendRequest();
  125. }
  126. }
  127. static void on_connect(hio_t* io) {
  128. if (++connected_num == connections) {
  129. if (verbose) {
  130. printf("all connected\n");
  131. }
  132. }
  133. connection_t* conn = (connection_t*)hevent_userdata(io);
  134. conn->SendRequest();
  135. hio_setcb_read(io, on_recv);
  136. hio_read(io);
  137. }
  138. static void start_connect(hloop_t* loop, connection_t* conn) {
  139. hio_t* io = hio_create_socket(loop, ip, port, HIO_TYPE_TCP, HIO_CLIENT_SIDE);
  140. if (io == NULL) {
  141. perror("socket");
  142. exit(1);
  143. }
  144. conn->io = io;
  145. hevent_set_userdata(io, conn);
  146. if (https) {
  147. hio_enable_ssl(io);
  148. }
  149. tcp_nodelay(hio_fd(io), 1);
  150. hio_setcb_connect(io, on_connect);
  151. hio_setcb_close(io, on_close);
  152. hio_connect(io);
  153. }
  154. static void start_reconnect(hio_t* io) {
  155. hloop_t* loop = hevent_loop(io);
  156. connection_t* conn = (connection_t*)hevent_userdata(io);
  157. start_connect(loop, conn);
  158. }
  159. int main(int argc, char** argv) {
  160. // parse cmdline
  161. main_ctx_init(argc, argv);
  162. int ret = parse_opt(argc, argv, options);
  163. if (ret != 0) {
  164. print_help();
  165. exit(ret);
  166. }
  167. if (get_arg("h") || g_main_ctx.arg_list_size != 1) {
  168. print_help();
  169. exit(0);
  170. }
  171. url = g_main_ctx.arg_list[0];
  172. if (get_arg("v")) {
  173. verbose = true;
  174. }
  175. const char* strConnections = get_arg("c");
  176. const char* strDuration = get_arg("d");
  177. const char* strThreads = get_arg("t");
  178. if (strConnections) connections = atoi(strConnections);
  179. if (strDuration) duration = atoi(strDuration);
  180. if (strThreads) threads = atoi(strThreads);
  181. print_cmd();
  182. // ParseUrl
  183. request = std::make_shared<HttpRequest>();
  184. request->url = url;
  185. request->ParseUrl();
  186. https = request->scheme == "https";
  187. const char* host = request->host.c_str();
  188. port = request->port;
  189. // ResolveAddr
  190. if (is_ipaddr(host)) {
  191. strcpy(ip, host);
  192. } else {
  193. sockaddr_u addr;
  194. if (ResolveAddr(host, &addr) != 0) {
  195. fprintf(stderr, "Could not resolve host: %s\n", host);
  196. exit(1);
  197. }
  198. sockaddr_ip(&addr, ip, sizeof(ip));
  199. }
  200. // Test connect
  201. printf("Connect to %s:%d ...\n", ip, port);
  202. int connfd = ConnectTimeout(ip, port);
  203. if (connfd < 0) {
  204. fprintf(stderr, "Could not connect to %s:%d\n", ip, port);
  205. exit(1);
  206. } else {
  207. closesocket(connfd);
  208. }
  209. // Dump request
  210. request->headers["User-Agent"] = std::string("libhv/") + hv_version();
  211. request->headers["Connection"] = "keep-alive";
  212. request_msg = request->Dump(true, true);
  213. printf("%s", request_msg.c_str());
  214. // EventLoopThreadPool
  215. EventLoopThreadPool loop_threads(threads);
  216. loop_threads.start(true);
  217. // connections
  218. conns = (connection_t**)malloc(sizeof(connection_t*) * connections);
  219. for (int i = 0; i < connections; ++i) {
  220. conns[i] = new connection_t;
  221. EventLoopPtr loop = loop_threads.nextLoop();
  222. hloop_t* hloop = loop->loop();
  223. loop->runInLoop(std::bind(start_connect, loop->loop(), conns[i]));
  224. }
  225. // stop after duration
  226. loop_threads.loop()->setTimeout(duration * 1000, [&loop_threads](TimerID timerID){
  227. stop = true;
  228. loop_threads.stop(false);
  229. });
  230. // wait loop_threads exit
  231. loop_threads.join();
  232. print_result();
  233. return 0;
  234. }