wrk.cpp 6.6 KB

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