fasthttp で作る cowserver

クリスマス？

```
                                        _____________________
                                       < そんなもの、うちにはないよ >
                                        ---------------------
                                              \
                                               \
                         \__\_ :. ___/          \
                        ..\  /--
 :.______ :  .:*  :  . _ .:  :..  .  :   . .  :    ()_ .:
  ((     \. :./(__ :._O_)________:______,____:____/  *\_o
====((    \: (****) (***) :. ...: .. .  ()_______/\\ __-'
 \____((   \ ()oo()_/ /.:  :  ..________/_____ll   -/.: ..
 (      ((  \(())))__/   .  ..  \\.: ..(   )  ll (  l_.:
(       / (( \__*__)___:___ :  : ))   .) /--------\ \ \
(      /    ((_____________) .. //  . / / /..:: .  )_)_\
 (____/_____________________\__// :  /_/_/  :..  :/_/ \_\
 /_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/    /_/_/
```
というわけで明日はクリスマスイブですね！！
この記事は[Go (その2) Advent Calendar 2016](http://qiita.com/advent-calendar/2016/go2) 23日目の記事です！
# 今回作るもの
今回はリクエストが来たら cowsay を行うサーバーを Go で書いてみたいと思います.
とても簡単に作成ができるので、まだサーバー作成したことのない皆さんも挑戦してみてください!!

- GET `/cow` (Hello!! な cowsay を返す)
- GET `/cow/:say` (:say の部分に入力された文字列な cowsay を返す)

その前に...
# cowsay ってなんですか!?
[http://qiita.com/Kei-Kamikawa/items/a5245720524aecfab666](http://qiita.com/Kei-Kamikawa/items/a5245720524aecfab666) を読むとあーこんなものかーと分かるはずです.
今回簡単に cowsay を実現するために [Neo-cowsay](https://github.com/Code-Hex/Neo-cowsay) というパッケージを使っていきます.

# まずは net/http でシュッと作ってみる
net/http でと言ってますが、routing は [httprouter](https://github.com/julienschmidt/httprouter) を利用しています.

```go:net-http.go
package main

import (
	"fmt"
	"log"
	"net/http"

	cowsay "github.com/Code-Hex/Neo-cowsay"
	"github.com/julienschmidt/httprouter"
)

func main() {
	router := httprouter.New()
	router.GET("/cow", cowHello)
	router.GET("/cow/:say", cowSay)

	if err := http.ListenAndServe(":3000", router); err != nil {
		log.Fatalf("Error in ListenAndServe: %s", err.Error())
	}
}

func cowHello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
	say, err := cowsay.Say(&cowsay.Cow{
		Phrase:      "Hello!!",
		Type:        "default",
		BallonWidth: 40,
	})

	if err != nil {
		fmt.Fprintf(w, "Error in cowHello: %s", err.Error())
		return
	}
	fmt.Fprintf(w, say)
}

func cowSay(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {

	say, err := cowsay.Say(&cowsay.Cow{
		Phrase:      ps.ByName("say"),
		Type:        "default",
		BallonWidth: 40,
	})
	if err != nil {
		fmt.Fprintf(w, "Error in cowSay: %s", err.Error())
		return
	}
	fmt.Fprintf(w, say)
}
```

httprouter で作成する場合 `w http.ResponseWriter, r *http.Request, _ httprouter.Params` を 3 つ取るハンドラ(ここでは `cowHello` や `cowSay` の事です)を作成する必要があります. `/cow/:say` のように URL からパラメータを取得する場合は `ps.ByName("say")` という感じで取得することができます.

# 次に fasthttp でサッと作る
ところで [fasthttp](https://github.com/valyala/fasthttp) とはなんぞ？となってる方もいると思います. [net/httpより10倍速いvalyala/fasthttpが面白そうなので調査してみた件](https://developers.eure.jp/tech/survey-on-valyala-fasthttp/) の記事を読むとわかりやすいのではないかと思います. まとめると net/http より高速なんだぜ!! と謳っているパッケージです.
高速なら使ってみたい！ということで, [fasthttprouter](https://github.com/buaazp/fasthttprouter) と組み合わせて作っていきます.

```go:fasthttp.go
package main

import (
	"fmt"
	"log"

	cowsay "github.com/Code-Hex/Neo-cowsay"
	"github.com/buaazp/fasthttprouter"
	"github.com/valyala/fasthttp"
)

func main() {
	router := fasthttprouter.New()
	router.GET("/cow", cowHello)
	router.GET("/cow/:say", cowSay)

	if err := fasthttp.ListenAndServe(":3000", router.Handler); err != nil {
		log.Fatalf("Error in ListenAndServe: %s", err.Error())
	}
}

func cowHello(ctx *fasthttp.RequestCtx) {
	say, err := cowsay.Say(&cowsay.Cow{
		Phrase:      "Hello!!",
		Type:        "default",
		BallonWidth: 40,
	})

	if err != nil {
		fmt.Fprintf(ctx, "Error in cowHello: %s", err.Error())
		return
	}
	fmt.Fprintf(ctx, say)
}

func cowSay(ctx *fasthttp.RequestCtx) {
	say, err := cowsay.Say(&cowsay.Cow{
		Phrase:      ctx.UserValue("say").(string),
		Type:        "default",
		BallonWidth: 40,
	})
	if err != nil {
		fmt.Fprintf(ctx, "Error in cowSay: %s", err.Error())
		return
	}
	fmt.Fprintf(ctx, say)
}
```
先ほどの net/http と httprouter の組み合わせで開発した cowserver よりシンプルに記述することができているのではないかと思います. fasthttprouter を使用するときは `ctx *fasthttp.RequestCtx` の引数を 1 つ取るハンドラを作成すれば完了です.

ただし, `/cow/:say` のような URL パラメーターを受け取るとき, `ctx.UserValue("say")` のようにすると受け取ることができるのですが, 返り値の方が `interface{}` ですので型アサーションを行う必要があります. そういうことからここでは `ctx.UserValue("say").(string)` というように `string` でアサーションを行っています.

# 動かしてみる
早速動かしてみましょう. そして `curl http://localhost:3000/cow` や `curl http://127.0.0.1:3000/cow/サンタさん彼女ください` などを打ち込んで試してみてください！！

```
 __________________
< サンタさん彼女ください >
 ------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
```

おおーー！！クリスマス最高だゼェ！！
# ベンチマーク
ところで fasthttp って本当に早いの？と思って, apache bench を用いて秒間どれくらいのリクエストをさばけるのか見ていきます. 使用したコマンドは `ab -n 10000 -c 1` です.

|          | net/http | fasthttp |
|:--------:|:--------:|:--------:|
|/cow     |2808.56 [#/sec]|2011.16 [#/sec]|
|/cow/:say|2591.93 [#/sec]|2175.72 [#/sec]|

この結果だと net/http の方が早いですね. タイトル的に fasthttp の方が早かったと記述したかったのですが, 結果が結果なので仕方ないですね！

# まとめ

- net/http, fasthttp を使って cowserver を作った
- fasthttprouter を使うとまあまあシンプルに記述できた
- 結果的に net/http の方が早い