Upload
masahiro-nagano
View
10.186
Download
1
Embed Size (px)
Citation preview
GazelleYokohama.pm #12
Masahiro Nagano (kazeburo)https://www.!ickr.com/photos/ckindel/424610604/
Plack Handler for performance freaks
Me
• 長野雅広 (Masahiro Nagano)
• @kazeburo
• CPAN: KAZEBURO / github: kazeburo
• 横浜市西区在住• ISUCON 2013,2014 優勝
Gazelle
Gazelle #とは• Plack Handler / PSGI Server
• HTTP/1.0 Web Server
• Preforking Architecture
• Suitable for running application servers behind a reverse proxy
• Starlet compatible / hot deploy
• Fast Fast Fast
“Hello World”
0
32,500
65,000
97,500
130,000
106,028
62,06933,300
127,462
req/
sec
nginx starman Starlet Gazelle
3x Faster!!than starman
“counter.psgi”
0
27,500
55,000
82,500
110,000
hello world counter.psgi
42,285
106,028
28,292
62,069
20,10033,300
req/
sec
starman Starlet Gazelle
ISUCON4 Quali!er
11,250
22,500
33,750
45,000
44,76442,81339,77637,808Scor
e
予選通過ライン starman Starlet Gazelle
「ISUCON4 予選でアプリケーションを変更せずに予選通過ラインを突破するの術」に若干変更を加えたバージョン
Gazelle はなぜ速い• Only Support HTTP/1.0 and does not
support KeepAlive. It make code very simple
• Mostly written in XS
• Ultra fast HTTP processing using picohttpparser
• Use accept4(2)
• Use writev(2) for output responses
Simple HTTP/1.0 GETaccept4(2)
read(2)
parse_headerpoll(2)
execute app
writev(2)poll(2)
close(2)
complete?
written?
OK
No
OK No
Mostly written in XSaccept4(2)
read(2)
parse_headerpoll(2)
execute app
writev(2)poll(2)
close(2)
complete?
written?
OK
No
OK No
XS
XS
Perl code using XS
while (1) { if ( my ($fd, $buf, $env) = accept_psgi( fileno($listen_sock), $timeout, $listen_sock_is_tcp, $host || 0, $port || 0 ) ) { my $guard = guard { close_client($fd) }; $res = Plack::Util::run_app $app, $env; my $status_code = $res->[0]; my $headers = $res->[1]; my $body = $res->[2]; write_psgi_response($fd, $timeout, $status_code, $headers, $body); }
Perl code using XS
while (1) { if ( my ($fd, $buf, $env) = accept_psgi( fileno($listen_sock), $timeout, $listen_sock_is_tcp, $host || 0, $port || 0 ) ) { my $guard = guard { close_client($fd) }; $res = Plack::Util::run_app $app, $env; my $status_code = $res->[0]; my $headers = $res->[1]; my $body = $res->[2]; write_psgi_response($fd, $timeout, $status_code, $headers, $body); }
picohttpparser
• created by kazuho-san
• used in H2O and HTTP::Parser::XS
必読http://blog.kazuhooku.com/2014/11/the-internals-h2o-or-how-to-write-fast.html
accept4(2)
• Required Linux >= 2.6.28
• Set FD_CLOEXEC and O_NONBLOCK in one system call
accept4(2)
int_accept(int fileno, struct sockaddr *addr, unsigned int addrlen) { int fd;#ifdef SOCK_NONBLOCK fd = accept4(fileno, addr, &addrlen, SOCK_CLOEXEC|SOCK_NONBLOCK);#else fd = accept(fileno, addr, &addrlen); fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);#endif return fd;}
accept4(2)13:51:26.755628 accept(4, {sa_family=AF_INET, sin_port=htons(42828), sin_addr=inet_addr("127.0.0.1")}, [16]) = 5
13:51:27.324951 fcntl(5, F_SETFD, FD_CLOEXEC) = 0
13:51:27.325014 fcntl(5, F_GETFL) = 0x2 (flags O_RDWR)
13:51:27.325067 fcntl(5, F_SETFL, O_RDWR|O_NONBLOCK) = 0
13:51:27.325117 read(5, "GET / HTTP/1.1\r\nUser-Agent:"..., 16384) = 155
13:51:27.325200 setsockopt(5, SOL_TCP, TCP_NODELAY, [1], 4) = 0
13:52:17.946622 accept4(4, {sa_family=AF_INET, sin_port=htons(42835), sin_addr=inet_addr("127.0.0.1")}, [16], SOCK_CLOEXEC|SOCK_NONBLOCK) = 5
13:52:18.505428 read(5, "GET / HTTP/1.1\r\nUser-Agent:”..., 16384) = 155
13:52:18.505519 setsockopt(5, SOL_TCP, TCP_NODELAY, [1], 4) = 0
writev
• write multiple buffer to a fd in one system call
• reduce memory copy or system calls
ex. memory copy#perlmy $header = ["Server"=>"gazelle","Content-Type"=>"text/plain",...];
#xschar buf[512];while ( i < av_len(headers) + 1 ) { key = SvPV_nolen(*av_fetch(headers,i++,0)); strcat(buf, key); strcat(buf, ": "); val = SvPV_nolen(*av_fetch(headers,i++,0)); strcat(buf, val); strcat(buf, "\r\n");}write(fd, buf, sizeof(buf)-1);
Too manymemory copycause system
overhead
ex. write write write
#perlmy $header = ["Server"=>"gazelle","Content-Type"=>"text/plain"];
#xswhile ( i < av_len(headers) + 1 ) { key = SvPV(*av_fetch(headers,i++,0),&len); write(fd, key, len); write(fd, ": ", sizeof(“: ”) - 1); val = SvPV(*av_fetch(headers,i++,0),&len); write(fd, val, len); write(fd, "\r\n", sizeof(“\r\n”) - 1);}
Too manywrite(2)
increase latencyof network
writev(2)#perlmy $header = ["Server"=>"gazelle","Content-Type"=>"text/plain"];#xsstruct iovec v[av_len(headers)+1)*2 + 10];iovcnt = 0;while ( i < av_len(headers) + 1 ) { key = SvPV(*av_fetch(headers,i++,0),&len); iovcnt++; v[iovcnt].iov_base = key; v[iovcnt].iov_len = len; iovcnt++; v[iovcnt].iov_base = “: ”; v[iovcnt].iov_len = sizeof(“: ”) - 1; ...}writev(fd, v, iovcnt);
no memory copyone system call
writev(2)
plackup -s Gazelle -e 'sub{[200,["Content-Type"=>"text/plain"],
["xxx","xxx","yyy","yyy","zzzz","\n"]]}'
writev(5, [{"HTTP/1.0 200 OK\r\nConnection: close\r\nServer: gazelle\r
\n", 53}, {"Content-Type", 12}, {": ", 2}, {"text/plain", 10}, {"\r\n",
2}, {"Date: Fri, 28 Nov 2014 04:38:08 GMT\r\n\r\n", 39}, {"xxx", 3},
{"xxx", 3}, {"yyy", 3}, {"yyy", 3}, {"zzzz", 4}, {"\n", 1}], 12) = 135
高速なサーバを書くには
• write XS, minimize Perl code
• reduce system calls
• Zero Copy
高速なAppサーバって必要なの?
• ISUCON :)
• Social Games, AdTech, SNS
• High optimized applications
• few msec ~ few tens of msec
• Several hundreds of request/sec/host
• 1PVあたりの利益が小さいサービス
Gazelleの実績
• livedoor Blog
• 2500万req/day/host
• Starletからの移行でCPU使用率1%~3%さがった
ぜひお使い下さい
https://www.!ickr.com/photos/superformosa/9057428400/