dexador rises

83
Dexador Rises Lisp Meet Up #31 August 26, 2015 Eitaro Fukamachi Somewrite Co., Ltd.

Upload: fukamachi

Post on 26-Jan-2017

9.599 views

Category:

Technology


0 download

TRANSCRIPT

Page 1: Dexador Rises

Dexador Rises

Lisp Meet Up #31 August 26, 2015

Eitaro Fukamachi Somewrite Co., Ltd.

Page 2: Dexador Rises

I’m Eitaro Fukamachi @nitro_idiot fukamachi

Page 4: Dexador Rises

Contributing

• Roswell

Page 5: Dexador Rises
Page 6: Dexador Rises

We Work Remotely

Page 7: Dexador Rises

We Work RemotelyWe’re hiring!

Page 8: Dexador Rises

Today, let me talk about HTTP client.

Page 9: Dexador Rises

HTTP client

Client Server

Page 10: Dexador Rises

HTTP client

Client Server

HTTP Request

GET / HTTP/1.1 Host: quickdocs.org User-Agent: curl/7.43.0 Accept: */*

Page 11: Dexador Rises

HTTP client

Client Server

HTTP Request

HTTP Response

GET / HTTP/1.1 Host: quickdocs.org User-Agent: curl/7.43.0 Accept: */*

HTTP/1.1 200 OK Date: Mon, 24 Aug 2015 00:41:36 GMT Content-Type: text/html Content-Length: 3771 Connection: keep-alive

Page 12: Dexador Rises

HTTP client

Client Server

HTTP Request

HTTP Response

GET / HTTP/1.1 Host: quickdocs.org User-Agent: curl/7.43.0 Accept: */*

HTTP/1.1 200 OK Date: Mon, 24 Aug 2015 00:41:36 GMT Content-Type: text/html Content-Length: 3771 Connection: keep-alive

ex) Google Chrome, curl, Drakma ex) Apache, nginx, Woo

Page 13: Dexador Rises

HTTP client libraries

• Drakma

• trivial-http

• Carrier

Page 14: Dexador Rises

HTTP client libraries

• Drakma

• De facto. Full-featured.

• trivial-http

• Simple. A few features. No SSL.

• Carrier

• Asynchronous.

Page 15: Dexador Rises

HTTP client libraries

• Drakma (usocket)

• De facto. Full-featured.

• trivial-http (usocket)

• Simple. A few features. No SSL.

• Carrier (cl-async/libuv)

• Asynchronous.

Page 16: Dexador Rises

HTTP client libraries

• Drakma (usocket)

• De facto. Full-featured.

• trivial-http (usocket)

• Simple. A few features. No SSL.

• Carrier (cl-async/libuv)

• Asynchronous.

75 LIBRARIES

Required by

3 LIBRARIES

None

Page 17: Dexador Rises

Drakma

• Ediware

• Since 2006

• Still maintained at GitHub

Mature??? Easy to use??? Fast???

Page 18: Dexador Rises

No.

Page 19: Dexador Rises

Pitfall 1: Force URL encoding

Page 20: Dexador Rises

• Force URL encoding with PURI

Pitfall (1/3) of Drakma

(drakma:http-request (format nil “http://b.hatena.ne.jp/search/tag?q=~A” tag))

tag = “lisp” => OK tag = “scheme” => OK tag = “clojure” => OK tag = “common lisp” => PURI:URI-PARSE-ERROR

Page 21: Dexador Rises

• Force URL encoding with PURI

Pitfall (1/3) of Drakma

(drakma:http-request (format nil “http://b.hatena.ne.jp/search/tag?q=~A” (drakma:url-encode tag :utf-8)))

tag = “lisp” => OK tag = “scheme” => OK tag = “clojure” => OK tag = “common lisp” => “common+lisp”

Page 22: Dexador Rises

• Force URL encoding with PURI

Pitfall (1/3) of Drakma

(drakma:http-request (format nil “http://b.hatena.ne.jp/search/tag?q=~A” (drakma:url-encode tag :utf-8)))

tag = “lisp” => OK tag = “scheme” => OK tag = “clojure” => OK tag = “common lisp” => “common+lisp”

tag = “AKB48” => OK tag = “乃木坂46” => "%E4%B9%83%E6%9C%A8%E5%9D%8246"

Page 23: Dexador Rises

• Force URL encoding with PURI

Pitfall (1/3) of Drakma

(drakma:http-request (format nil “http://b.hatena.ne.jp/search/tag?q=~A” (drakma:url-encode tag :utf-8)) :preserve-uri t)

tag = “lisp” => OK tag = “scheme” => OK tag = “clojure” => OK tag = “common lisp” => OK

tag = “AKB48” => OK tag = “乃木坂46” => OK

Page 24: Dexador Rises

Pitfall 2: Poor language support

Page 25: Dexador Rises

• Poor language support with flexi-streams

Pitfall (2/3) of Drakma

(drakma:http-request “http://www.hatena.ne.jp/”)

(drakma:http-request “http://www.google.co.jp/”)

Page 26: Dexador Rises

• Poor language support with flexi-streams

Pitfall (2/3) of Drakma

(drakma:http-request “http://www.hatena.ne.jp/”) ;; => body as UTF-8 string

(drakma:http-request “http://www.google.co.jp/”) ;; => body as byte vector

WARNING: Problems determining charset (falling back to binary): :SHIFT_JIS is not known to be a name for an external format.

Page 27: Dexador Rises

Pitfall 3: Error handling

Page 28: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(let* ((body (drakma:http-request “http://cliki.net”)) (parsed-html (plump:parse body))) …)

Page 29: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(let* ((body (drakma:http-request “http://cliki.net”)) (parsed-html (plump:parse body))) …)

It fails if the HTTP response code is 4xx or 5xx.

Page 30: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(multiple-value-bind (body status) (drakma:http-request “http://cliki.net”) (unless (= status 200) (error “An HTTP request failed (Code=~D)” status)) (let ((parsed-html (plump:parse body))) …))

Raise an error unless the status is not 200

Page 31: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(multiple-value-bind (body status) (drakma:http-request “http://cliki.net”) (unless (= status 200) (error “An HTTP request failed (Code=~D)” status)) (let ((parsed-html (plump:parse body))) …))

Raise an error unless the status is not 200

Want to retry???

Page 32: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(block nil (tagbody retry (multiple-value-bind (body status) (drakma:http-request "http://cliki.net/") (unless (= status 200) (go retry)) (return body))))

With Auto-Retrying

Page 33: Dexador Rises

• Tend to forget error handling

Pitfall (3/3) of Drakma

(block nil (let ((times 5)) (tagbody retry (multiple-value-bind (body status) (drakma:http-request "http://cliki.net/") (unless (= status 200) (when (= times 0) (error "An HTTP request failed. (Code=~D)” status)) (decf times) (go retry)) (return body)))))

Retry only 5 times

Page 34: Dexador Rises

Many pitfalls.

Page 35: Dexador Rises

Ridiculous.

Page 36: Dexador Rises

We just wanted to send an HTTP request.

Page 37: Dexador Rises

Dexador changes it.

Page 38: Dexador Rises

• Full-featured. usocket based.

• Use fast-http, QURI, Babel, cl-cookie

Dexador: Another choice

Page 39: Dexador Rises

Dexador: APIs

(dex:get “http://lisp.org/“) (dex:post “http://lisp.org/“) (dex:head “http://lisp.org/“) (dex:put “http://lisp.org/“) (dex:delete “http://lisp.org/“)

Page 40: Dexador Rises

Dexador: Language support

;; Shift_JIS (dex:get “http://www.google.co.jp/“)

;; EUC-JP (dex:get “https://mixi.jp/“)

Page 41: Dexador Rises

Dexador: Error handling

(handler-case (dex:get “http://cliki.net/“) (dex:http-request-failed (e) (warn “An HTTP request failed (Code=~D)” (dex:response-status e))))

Page 42: Dexador Rises

Dexador: Error handling

(handler-case (dex:get “http://cliki.net/“) (dex:http-request-forbidden () ;; for 403 forbidden ) (dex:http-request-service-unavailable () ;; for 503 service unavailable ) (dex:http-request-failed (e) (warn “An HTTP request failed (Code=~D)” (dex:response-status e))))

Page 43: Dexador Rises

Dexador: Error handling

;; Ignore errors and continue (handler-bind ((dex:http-request-failed #'dex:ignore-and-continue)) (dex:get "http://lisp.org"))

Page 44: Dexador Rises

Dexador: Auto-Retrying

;; Auto-retry on 503 error (handler-bind ((dex:http-request-service-unavailable #’dex:retry-request)) (dex:get "http://lisp.org"))

Page 45: Dexador Rises

Dexador: Auto-Retrying

;; Retry only 5 times (handler-bind ((dex:http-request-service-unavailable (dex:retry-request 5))) (dex:get "http://lisp.org"))

Page 46: Dexador Rises

Dexador: Auto-Retrying

;; Retry only 5 times at 3-second intervals (handler-bind ((dex:http-request-service-unavailable (dex:retry-request 5 :interval 3))) (dex:get "http://lisp.org"))

Page 47: Dexador Rises

Dexador: Auto-Retrying

;; Retry only 5 times at 3-second intervals (handler-bind ((dex:http-request-service-unavailable (dex:retry-request 5 :interval 3))) (dex:get "http://lisp.org"))

(block nil (let ((times 5)) (tagbody retry (multiple-value-bind (body status) (drakma:http-request "http://cliki.net/") (unless (= status 200) (when (= times 0) (error "An HTTP request failed. (Code=~D)” status)) (decf times) (sleep 3) (go retry)) (return body)))))

Dexador

Drakma

Page 48: Dexador Rises

You may ask…

Page 49: Dexador Rises

You may ask…

…is it fast?

Page 50: Dexador Rises

BenchmarkSending GET request 30 times to Local Server

(lower is better)

0

0.009

0.018

0.026

0.035

Drakma Dexadorw/out conneciton-pool

0.024s

Page 51: Dexador Rises

BenchmarkSending GET request 30 times to Local Server

(lower is better)

0

0.009

0.018

0.026

0.035

Drakma Dexadorw/out conneciton-pool

0.013s

0.024s

Page 52: Dexador Rises

Sending GET request 30 times to Local Server(lower is better)

0

0.009

0.018

0.026

0.035

Drakma Dexadorw/out conneciton-pool

0.013s

0.024s

Benchmark

x1.8 faster!

Page 53: Dexador Rises

Sending GET request 30 times to Local Server(lower is better)

0

0.009

0.018

0.026

0.035

Drakma Dexadorw/out conneciton-pool

0.013s

0.024s

Benchmark

x1.8 faster!????

Page 54: Dexador Rises

How about requesting over network?

Page 55: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.505s

Page 56: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s

Page 57: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s x1.2 faster

Page 58: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s 0.0036 sec/req

Page 59: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s 0.0036 sec/req

Too trivial improvement…

Page 60: Dexador Rises

How come?

Page 61: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s

Page 62: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s

Network Latency +

Connection establishment

Network Latency +

Connection establishment

Page 63: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

0.396s

0.505s

Network Latency +

Connection establishment

Network Latency +

Connection establishment

The largest bottleneck

Page 64: Dexador Rises

Differences of Servers/ClientsHTTP Server HTTP Client

C

S

C C

C

S

Page 65: Dexador Rises

Can’t help Network Latency and bandwidth.

Page 66: Dexador Rises

Can skip connection establishment…?

Page 67: Dexador Rises

• Reuse connections once established

(Implicit) Connection-pooling

Page 68: Dexador Rises

• Reuse connections once established

(Implicit) Connection-pooling

;; Establish a new connection (dex:get “http://lisp.org/index.html“)

;; Reuse the above connection (dex:get “http://lisp.org/index.html“)

Page 69: Dexador Rises

• Reuse connections once established

(Implicit) Connection-pooling

;; Establish a new connection (dex:get “http://lisp.org/index.html“)

;; Reuse the above connection (dex:get “http://lisp.org/index.html“)

0.727 sec

0.380 sec

Page 70: Dexador Rises

Benchmark (again)

Page 71: Dexador Rises

BenchmarkSending GET request 30 times to Local Server

(lower is better)

0

0.006

0.012

0.018

0.024

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.013s

0.024s

Page 72: Dexador Rises

BenchmarkSending GET request 30 times to Local Server

(lower is better)

0

0.006

0.012

0.018

0.024

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.005s

0.013s

0.024s

Page 73: Dexador Rises

BenchmarkSending GET request 30 times to Local Server

(lower is better)

0

0.006

0.012

0.018

0.024

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.005s

0.013s

0.024s

x4.8 faster!

Page 74: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.396s

0.505s

Page 75: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.219s

0.396s

0.505s

Page 76: Dexador Rises

BenchmarkSending GET request 30 times to Remote Server

(lower is better)

0

0.15

0.3

0.45

0.6

Drakma Dexadorw/out conneciton-pool

Dexadorw/ connection-pool

0.219s

0.396s

0.505s Still x2.3 faster!

Page 77: Dexador Rises

• It’s pretty common to request to the same host multiple times

• Can expect some performance improvements in real applications

In real applications

Page 78: Dexador Rises

Status

Page 79: Dexador Rises

• Still BETA (v0.9.7)

• Stabilizing. More performance improvements are secondary importance.

• Bug reports are welcome

• Tested with SBCL, CCL, ABCL and ECL on Travis CI

Status

Page 80: Dexador Rises

Try the new player and send me a feedback.

Page 81: Dexador Rises

Thanks.

Page 82: Dexador Rises

EITARO FUKAMACHI 8arrow.org @nitro_idiot fukamachi

Page 83: Dexador Rises

See Also

• Dexador: github.com/fukamachi/dexador