Woo:
Writing a fast web server
8th European Lisp Symposium @ London, UK
April 21, 2015
Eitaro Fukamachi
Somewrite Co., Ltd.
I’m Eitaro Fukamachi
@nitro_idiot fukamachi
My Products
• Clack
• Caveman2
• CL-DBI
• quickdocs.org
• datafly
• SxQL
...and 25 other libraries
We’re hiring!
Woo
• HTTP server written in Common Lisp
• HTTP/0.9, HTTP/1.x
• Clack compliant
Woo
Clack?
• Abstraction layer of HTTP servers
Web server
Application
Web server Web server
Application Application
Clack?
• Abstraction layer of HTTP servers
Web server
Application
Web server Web server
Application Application
Clack
ex) Caveman2
• built on top of Clack
Web serverWeb server Web server
Caveman2

(Web framework)
Clack
ex) RESTAS (without Clack)
• directly built on top of Hunchentoot
Web server
Depends on Hunchentoot.
Can’t switch the back...
Clack-compliant
(woo:run
(lambda (env)
‘(200 (:content-type “text/plain”) (“Hello, World”))))
(clack:clackup
(lambda (env)...
Fast
https://github.com/fukamachi/woo#benchmarks
Fast — in the real worldreq/sec
0
33
65
98
130
Node.js Woo
127.18
76.73
1.6 times faster!
Let me tell
why I had to write
a fast HTTP server.
Wookie is slower than Node.js
• Wookie is 2 times slower than Node.js
Wookie is slower than Node.js
• Wookie is 2 times slower than Node.js
IS COMMON LISP SLOW???
Wookie is slower than Node.js
• Wookie is 2 times slower than Node.js
IS COMMON LISP SLOW???
NO WAY!
So, I decided to
write a faster one.
How could it be fast?
3 difficulties
in writing an HTTP servers
3 difficulties in HTTP server
• Network I/O is the largest bottleneck
• Need to handle a vast amount of requests
at once
• N...
3 tactics (no silver bullet)
1. Better architecture
2. Fast HTTP parsing
3. The libev event library
Tactic 1:
Better architecture
2 architectures:
Prefork vs Event-driven
Prefork
Worker
process
Worker
process
Worker
process
master process
Requests
accept connections
Responses
• Simple
• Fast ...
Prefork
Worker
process
Worker
process
Worker
process
master process
Requests
accept connections
Responses • Slow client ca...
Event-driven
• Handle many
clients at the same
time
• Asnyc ACCEPT/
READ/WRITE
• ex) Woo, Wookie,
Tornado, nginxServer pro...
Event-driven
• Single-threaded
Problem
Server process
(single-threaded)
Event loop
Woo took another way:
Multithreaded event-driven
Multithreaded event-driven
Server process
Event loop
listen on the same file descriptor
Server process
Event loop
Server pr...
Tactic 2:
Fast HTTP parsing
HTTP parsing can be a bottleneck
• Wookie's largest bottleneck is HTTP parsing
• http-parse (uses regular expression)
• fa...
A brief introduction of HTTP
HTTP request look like…
GET /media HTTP/1.1↵
Host: somewrite.jp↵
Connection: keep-alive↵
Accept: */*↵
↵
HTTP request look like…
GET /media HTTP/1.1↵
Host: somewrite.jp↵
Connection: keep-alive↵
Accept: */*↵
↵
First Line
Headers...
HTTP request look like…
GET /media HTTP/1.1↵
Host: somewrite.jp↵
Connection: keep-alive↵
Accept: */*↵
↵ CR + LF
CRLF * 2 a...
HTTP is…
• Text-based protocol. (not binary)
• Lines terminated with CRLF
• Very lenient.
• Ignore multiple spaces
• Allow...
And,
there’s another difficulty.
HTTP messages are
sent over a network.
Which means,
we need to think about
long & incomplete
HTTP messages.
There’s 2 ways
to resolve this problem.
1. Stateful (http-parser)
http-parser (used in Node.js)
• https://github.com/joyent/http-parser
• Written in C
• Ported from Nginx’s HTTP parser
• W...
http-parser (used in Node.js)
for (p=data; p != data + len; p++) {
…
switch (parser->state) {
case s_dead:
…
case s_start_...
http-parser (used in Node.js)
for (p=data; p != data + len; p++) {
…
switch (parser->state) {
case s_dead:
…
case s_start_...
http-parser (used in Node.js)
for (p=data; p != data + len; p++) {
…
switch (parser->state) {
case s_dead:
…
case s_start_...
2. Stateless (PicoHTTPParser)
PicoHTTPParser (used in H2O)
• https://github.com/h2o/picohttpparser
• Written in C
• Stateless
PicoHTTPParser (used in H2O)
• https://github.com/h2o/picohttpparser
• Written in C
• Stateless
• Reparse when the data is...
And fast-http is…
fast-http is in the middle
• Stateful
• Track state for every line, not every char
• 1.25 times faster than C http-parser.
Tactic 3:
The libev event library
libev
• Wrapper of epoll, kqueue, POSIX select, poll
• Thin
• Fast
libev
• Wrapper of epoll, kqueue, POSIX select, poll
• Thin
• Fast
• Poor Windows support
• Windows isn’t popular for runn...
Goal
Goal: What is fast enough?
• The initial goal was “Beating Node.js”
• Done
• Being the fastest HTTP server in Common
Lisp
...
Got a great Pull Request
See Also
• Clack: clacklisp.org
• fast-http: github.com/fukamachi/fast-http
• libev
• Woo: github.com/fukamachi/woo
You may be interested in
• Dexador: github.com/fukamachi/dexador
• HTTP client library built on top of fast-http
and usock...
Thanks.
EITARO FUKAMACHI
8arrow.org
@nitro_idiot fukamachi
Woo: Writing a fast web server @ ELS2015
Woo: Writing a fast web server @ ELS2015
Nächste SlideShare
Wird geladen in …5
×

Woo: Writing a fast web server @ ELS2015

4.407 Aufrufe

Veröffentlicht am

Demonstration at European Lisp Symposium 2015 in London, UK.

Veröffentlicht in: Technologie
0 Kommentare
2 Gefällt mir
Statistik
Notizen
  • Als Erste(r) kommentieren

Keine Downloads
Aufrufe
Aufrufe insgesamt
4.407
Auf SlideShare
0
Aus Einbettungen
0
Anzahl an Einbettungen
1.397
Aktionen
Geteilt
0
Downloads
14
Kommentare
0
Gefällt mir
2
Einbettungen 0
Keine Einbettungen

Keine Notizen für die Folie

Woo: Writing a fast web server @ ELS2015

  1. 1. Woo: Writing a fast web server 8th European Lisp Symposium @ London, UK April 21, 2015 Eitaro Fukamachi Somewrite Co., Ltd.
  2. 2. I’m Eitaro Fukamachi @nitro_idiot fukamachi
  3. 3. My Products • Clack • Caveman2 • CL-DBI • quickdocs.org • datafly • SxQL ...and 25 other libraries
  4. 4. We’re hiring!
  5. 5. Woo
  6. 6. • HTTP server written in Common Lisp • HTTP/0.9, HTTP/1.x • Clack compliant Woo
  7. 7. Clack? • Abstraction layer of HTTP servers Web server Application Web server Web server Application Application
  8. 8. Clack? • Abstraction layer of HTTP servers Web server Application Web server Web server Application Application Clack
  9. 9. ex) Caveman2 • built on top of Clack Web serverWeb server Web server Caveman2
 (Web framework) Clack
  10. 10. ex) RESTAS (without Clack) • directly built on top of Hunchentoot Web server Depends on Hunchentoot. Can’t switch the backend!! Supports only Hunchentoot Web server Web server RESTAS
 (Web framework)
  11. 11. Clack-compliant (woo:run (lambda (env) ‘(200 (:content-type “text/plain”) (“Hello, World”)))) (clack:clackup (lambda (env) ‘(200 (:content-type “text/plain”) (“Hello, World”))) :server :woo) Run with Clack
  12. 12. Fast https://github.com/fukamachi/woo#benchmarks
  13. 13. Fast — in the real worldreq/sec 0 33 65 98 130 Node.js Woo 127.18 76.73 1.6 times faster!
  14. 14. Let me tell why I had to write a fast HTTP server.
  15. 15. Wookie is slower than Node.js • Wookie is 2 times slower than Node.js
  16. 16. Wookie is slower than Node.js • Wookie is 2 times slower than Node.js IS COMMON LISP SLOW???
  17. 17. Wookie is slower than Node.js • Wookie is 2 times slower than Node.js IS COMMON LISP SLOW??? NO WAY!
  18. 18. So, I decided to write a faster one.
  19. 19. How could it be fast?
  20. 20. 3 difficulties in writing an HTTP servers
  21. 21. 3 difficulties in HTTP server • Network I/O is the largest bottleneck • Need to handle a vast amount of requests at once • Need to handle various HTTP clients
 (fast / slow / unstable)
  22. 22. 3 tactics (no silver bullet) 1. Better architecture 2. Fast HTTP parsing 3. The libev event library
  23. 23. Tactic 1: Better architecture
  24. 24. 2 architectures: Prefork vs Event-driven
  25. 25. Prefork Worker process Worker process Worker process master process Requests accept connections Responses • Simple • Fast for little simultaneous connections • ex) Hunchentoot, Unicorn, Apache
  26. 26. Prefork Worker process Worker process Worker process master process Requests accept connections Responses • Slow client can cause performance issue. • like Mobile users • Slowloris attack blocking! ・・・ Problem
  27. 27. Event-driven • Handle many clients at the same time • Asnyc ACCEPT/ READ/WRITE • ex) Woo, Wookie, Tornado, nginxServer process (single-threaded) Event loop
  28. 28. Event-driven • Single-threaded Problem Server process (single-threaded) Event loop
  29. 29. Woo took another way: Multithreaded event-driven
  30. 30. Multithreaded event-driven Server process Event loop listen on the same file descriptor Server process Event loop Server process Event loop
  31. 31. Tactic 2: Fast HTTP parsing
  32. 32. HTTP parsing can be a bottleneck • Wookie's largest bottleneck is HTTP parsing • http-parse (uses regular expression) • fast-http (byte by byte parser) • 6000 times faster than http-parse
  33. 33. A brief introduction of HTTP
  34. 34. HTTP request look like… GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵ ↵
  35. 35. HTTP request look like… GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵ ↵ First Line Headers Body (empty, in this case)
  36. 36. HTTP request look like… GET /media HTTP/1.1↵ Host: somewrite.jp↵ Connection: keep-alive↵ Accept: */*↵ ↵ CR + LF CRLF * 2 at the end of headers
  37. 37. HTTP is… • Text-based protocol. (not binary) • Lines terminated with CRLF • Very lenient. • Ignore multiple spaces • Allow continuous header values
  38. 38. And, there’s another difficulty.
  39. 39. HTTP messages are sent over a network.
  40. 40. Which means, we need to think about long & incomplete HTTP messages.
  41. 41. There’s 2 ways to resolve this problem.
  42. 42. 1. Stateful (http-parser)
  43. 43. http-parser (used in Node.js) • https://github.com/joyent/http-parser • Written in C • Ported from Nginx’s HTTP parser • Written as Node.js’s HTTP parser • Stateful
  44. 44. http-parser (used in Node.js) for (p=data; p != data + len; p++) { … switch (parser->state) { case s_dead: … case s_start_req_or_res: … case s_res_or_resp_H: … } }
  45. 45. http-parser (used in Node.js) for (p=data; p != data + len; p++) { … switch (parser->state) { case s_dead: … case s_start_req_or_res: … case s_res_or_resp_H: … } } Process char by char Do something for each state
  46. 46. http-parser (used in Node.js) for (p=data; p != data + len; p++) { … switch (parser->state) { case s_dead: … case s_start_req_or_res: … case s_res_or_resp_H: … } } Process char by char Do something for each state Executed for every char!!
  47. 47. 2. Stateless (PicoHTTPParser)
  48. 48. PicoHTTPParser (used in H2O) • https://github.com/h2o/picohttpparser • Written in C • Stateless
  49. 49. PicoHTTPParser (used in H2O) • https://github.com/h2o/picohttpparser • Written in C • Stateless • Reparse when the data is incomplete • Most HTTP request is small
  50. 50. And fast-http is…
  51. 51. fast-http is in the middle • Stateful • Track state for every line, not every char • 1.25 times faster than C http-parser.
  52. 52. Tactic 3: The libev event library
  53. 53. libev • Wrapper of epoll, kqueue, POSIX select, poll • Thin • Fast
  54. 54. libev • Wrapper of epoll, kqueue, POSIX select, poll • Thin • Fast • Poor Windows support • Windows isn’t popular for running an HTTP server
  55. 55. Goal
  56. 56. Goal: What is fast enough? • The initial goal was “Beating Node.js” • Done • Being the fastest HTTP server in Common Lisp • Done
  57. 57. Got a great Pull Request
  58. 58. See Also • Clack: clacklisp.org • fast-http: github.com/fukamachi/fast-http • libev • Woo: github.com/fukamachi/woo
  59. 59. You may be interested in • Dexador: github.com/fukamachi/dexador • HTTP client library built on top of fast-http and usocket.
  60. 60. Thanks.
  61. 61. EITARO FUKAMACHI 8arrow.org @nitro_idiot fukamachi

×