イベント駆動で書くと
クラウド破産を回避できたり、自宅サーバを守れるというメリットがあるらしいので、どうせなら epoll を使ってみようということで。
http://d.hatena.ne.jp/odz/20070507/1178558340 と http://alpha.mixi.co.jp/blog/?p=76 と man が参考に。某所経由で id:koizuka 氏にいろいろ教わり助かりました。
nginx 速すぎで大変悲しい結果に。
ソース
http://d.hatena.ne.jp/odz/20070507/1178558340 の勝手 fork です。問題/間違い等あれば連絡いただければありがたいです。エラー処理は省略気味。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* epoll_sample.c | |
* | |
* forked from http://d.hatena.ne.jp/odz/20070507/1178558340 | |
* | |
* compile : gcc -O3 -o epoll_sample epoll_sample.c | |
* bench : ab -n 10000 -c 50 http://localhost:8080/ | |
* | |
* fix me :) | |
* | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <fcntl.h> | |
#include <sys/socket.h> | |
#include <sys/types.h> | |
#include <sys/epoll.h> | |
#include <netinet/in.h> | |
#include <netdb.h> | |
#define SERVER_PORT "8080" /* must be _string_ */ | |
#define MAX_EVENTS 100 | |
#define BACKLOG 10 | |
static int listener; | |
static int epfd; | |
static void die (const char* msg) | |
{ | |
perror(msg); | |
exit(EXIT_FAILURE); | |
} | |
static void setnonblocking(int sock) | |
{ | |
int flag = fcntl(sock, F_GETFL, 0); | |
fcntl(sock, F_SETFL, flag | O_NONBLOCK); | |
} | |
static int setup_socket() | |
{ | |
int sock; | |
struct addrinfo *res, hints; | |
int optval = 1; | |
memset(&hints, 0, sizeof(struct addrinfo)); | |
hints.ai_family = AF_INET; | |
hints.ai_flags = AI_PASSIVE; | |
hints.ai_socktype = SOCK_STREAM; | |
if ((getaddrinfo(NULL, SERVER_PORT, &hints, &res) != 0)) | |
die("getaddrinfo"); | |
if ((sock = socket(res->ai_family, res->ai_socktype, 0)) < 0) | |
die("socket"); | |
if ((setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&optval, sizeof(optval))) < 0) | |
die("setsockopt"); | |
if (bind(sock, res->ai_addr, res->ai_addrlen) < 0) | |
die("bind"); | |
if (listen(sock, BACKLOG) < 0) | |
die("listen"); | |
freeaddrinfo(res); | |
return sock; | |
} | |
int main() | |
{ | |
struct epoll_event ev; | |
struct epoll_event events[MAX_EVENTS]; | |
char buffer[1024]; | |
char res[2048]; | |
snprintf(res, sizeof(res), | |
"HTTP/1.0 200 OK\r\n" | |
"Content-Lnegth: 7\r\n" | |
"Content-Type: text/plain\r\n" | |
"\r\n" | |
"HELLO\r\n"); | |
if ((epfd = epoll_create(MAX_EVENTS)) < 0) | |
die ("epoll_create"); | |
listener = setup_socket(); | |
memset(&ev, 0, sizeof(ev)); | |
ev.events = EPOLLIN; | |
ev.data.fd = listener; | |
if (epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev) < 0) | |
die("epoll set insertion error"); | |
int i, nfd; | |
while (1) { | |
nfd = epoll_wait(epfd, events, MAX_EVENTS, -1); | |
for (i = 0; i < nfd; i++) { | |
if (events[i].data.fd == listener) { | |
struct sockaddr_in client_addr; | |
socklen_t client_addr_len = sizeof(client_addr); | |
int client = accept(listener, (struct sockaddr *)&client_addr, &client_addr_len); | |
if (client < 0) { | |
perror("accept"); | |
continue; | |
} | |
setnonblocking(client); | |
ev.events = EPOLLIN | EPOLLET; | |
ev.data.fd = client; | |
if(epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev) < 0) | |
die("epoll set insertion error"); | |
} else { | |
int client = events[i].data.fd; | |
int n = read(client, buffer, sizeof(buffer)); | |
if (n < 0) { | |
perror("read"); | |
if (epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev) < 0) | |
perror("epoll delete error"); | |
} else if (n == 0) { | |
if (epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev) < 0) | |
perror("epoll delete error"); | |
} else { | |
write(client, res, (int)strlen(res)); | |
if (epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev) < 0) | |
perror("epoll delete error"); | |
} | |
shutdown(client, SHUT_RDWR); | |
close(client); | |
} | |
} | |
} | |
close(epfd); | |
close(listener); | |
return 0; | |
} |
ベンチマーク
それぞれ debian 上で epoll_sample, nginx, Apache2 を実行。nginx, apache2 ともに apt-get install で持ってきて設定はデフォルトのまま。
epoll_sample
# ab -n 10000 -c 50 http://localhost:8080/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Server Hostname: localhost Server Port: 8080 Document Path: / Document Length: 7 bytes Concurrency Level: 50 Time taken for tests: 20.125 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 710142 bytes HTML transferred: 70014 bytes Requests per second: 496.91 [#/sec] (mean) Time per request: 100.623 [ms] (mean) Time per request: 2.012 [ms] (mean, across all concurrent requests) Transfer rate: 34.46 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 3 46 12.6 46 117 Processing: 9 48 13.8 48 241 Waiting: 0 32 15.2 35 239 Total: 32 95 23.2 94 244 Percentage of the requests served within a certain time (ms) 50% 94 66% 96 75% 99 80% 101 90% 109 95% 135 98% 168 99% 178 100% 244 (longest request)
nginx
# ab -n 10000 -c 50 http://localhost:80/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: nginx/0.6.32 Server Hostname: localhost Server Port: 80 Document Path: / Document Length: 151 bytes Concurrency Level: 50 Time taken for tests: 22.493 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 3627602 bytes HTML transferred: 1513171 bytes Requests per second: 444.58 [#/sec] (mean) Time per request: 112.465 [ms] (mean) Time per request: 2.249 [ms] (mean, across all concurrent requests) Transfer rate: 157.50 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 3 54 9.6 53 128 Processing: 33 57 11.1 54 139 Waiting: 7 46 12.1 46 134 Total: 64 112 15.2 107 230 Percentage of the requests served within a certain time (ms) 50% 107 66% 111 75% 113 80% 114 90% 119 95% 146 98% 171 99% 182 100% 230 (longest request)
Apache2
# ab -n 10000 -c 50 http://localhost:80/ This is ApacheBench, Version 2.3 <$Revision: 655654 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking localhost (be patient) Completed 1000 requests Completed 2000 requests Completed 3000 requests Completed 4000 requests Completed 5000 requests Completed 6000 requests Completed 7000 requests Completed 8000 requests Completed 9000 requests Completed 10000 requests Finished 10000 requests Server Software: Apache/2.2.9 Server Hostname: localhost Server Port: 80 Document Path: / Document Length: 45 bytes Concurrency Level: 50 Time taken for tests: 31.275 seconds Complete requests: 10000 Failed requests: 0 Write errors: 0 Total transferred: 3204993 bytes HTML transferred: 452115 bytes Requests per second: 319.74 [#/sec] (mean) Time per request: 156.377 [ms] (mean) Time per request: 3.128 [ms] (mean, across all concurrent requests) Transfer rate: 100.07 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 46 74 16.8 70 194 Processing: 51 81 16.9 81 211 Waiting: 3 52 28.2 53 172 Total: 138 155 20.2 149 401 Percentage of the requests served within a certain time (ms) 50% 149 66% 153 75% 156 80% 158 90% 169 95% 198 98% 230 99% 238 100% 401 (longest request)