Slide 1

Slide 1 text

実践GO言語 2019/07/04 開発本部 深谷敏邦

Slide 2

Slide 2 text

対象者 ■ A Tour of Go を読んだ ■ プロダクションコードは書いたことがない ■ Go で HTTP client/server を書きたい

Slide 3

Slide 3 text

アジェンダ ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 4

Slide 4 text

はじめに ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 5

Slide 5 text

はじめに ■ この講義では簡単なHTTPサーバを例にGoを書く際のエッセンスを学びます ■ サードパーティライブラリは扱いません ■ Goは言語機能が少なく、ライブラリよりもイディオムを駆使する言語です – もっと良い書き方、機能がないかと思っても本当にないです – イディオムを覚えて筋力でプログラムしましょう

Slide 6

Slide 6 text

準備 ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 7

Slide 7 text

準備 ■ 講義で使用するコンパイラをダウンロードしてください – go (1.12 くらい)

Slide 8

Slide 8 text

HTTP Server ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 9

Slide 9 text

HTTP Server ■ Go は HTTP Server に関する機能は標準ライブラリに含んでいる – net/http ■ 重要な型 – Handler – ResponseWriter – Request – Server

Slide 10

Slide 10 text

Handler型 ■ net.httpがユーザーリクエストに反応するためのコールバックインターフェース ■ ユーザーリクエストはReqeust型に入っている ■ ReponseWriter型を通してレスポンスする type Handler interface { ServeHTTP(ResponseWriter, *Request) }

Slide 11

Slide 11 text

Request型 ■ https://golang.org/pkg/net/http/#Request – Method – URL – Header – Body ■ この型はClientで使うものと共通 ■ Bodyがio.ReadCloserインターフェースを満たしているので、ioutil.ReadAllなどで中身 を取り出す ■ 中身を取り出すのはコードを書く人の責任 ■ 読んでいる最中にエラーが起きたら、それを処理するのも書く人の責任

Slide 12

Slide 12 text

ResponseWriter型 ■ https://golang.org/pkg/net/http/#ResponseWriter – Header().Add()/Header().Set()でヘッダを設定する – WriteHeader でステータスコードを設定する – Writeでレスポンスを返す ■ この型はio.Write型をみたすのでfmt.Fprintとかが使える ■ Writeを呼ぶと自動的にWriteHeader(http.StatusOK)がよばれる – エラーステータス返すなら必ずWriteHeaderを先に呼ぶ ■ エラーを返すならhttp.Errorが便利

Slide 13

Slide 13 text

ルーティング ■ リクエストルーティングは簡単なパスベースならhttp.ServeMuxがつかえる – これ自体もHandler ■ メソッドとか正規表現とか使いたいんですが ■ 適当なライブラリを探して使う – 決定版はない印象 ■ 筋力で自前でルーティング – 細かいバリデーションが必要なエンドユーザー相手でなければあり – ルータライブラリが必要になるようなサーバをGoで書きたいですか?

Slide 14

Slide 14 text

Server型 ■ https://golang.org/pkg/net/http/#Server ■ 実際にコネクションを受け付けてHandlerを起動してくれる型 ■ フィールドはおおよそオプションだが、Timeoutは設定すべき – 後述する Context を使うとさらに細かいTimeoutが設定可能 ■ ListenAndServerを呼べばサーバが起動する

Slide 15

Slide 15 text

HTTP Client ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 16

Slide 16 text

HTTP Client ■ Go は HTTP Client に関する機能は標準ライブラリに含んでいる – net/http ■ 重要な型 – Client – Request – Response

Slide 17

Slide 17 text

Client型 ■ https://golang.org/pkg/net/http/#Client ■ HTTPリクエストを行うための型 ■ フィールドはオプションでおおよそ何も指定しなくても良い ■ http.DefaultClientは使わない方が良い – 設定がグローバル – テストで差し替えれない ■ 基本的にはDoメソッドだけを使えばよい – Getなどの専用メソッドがあるが、後述するContextが使えない

Slide 18

Slide 18 text

Request型 ■ https://golang.org/pkg/net/http/#Request ■ 見間違いではなくサーバで見たものと同じ ■ フィールドが多くてびっくりするが、http.NewRequestで生成する

Slide 19

Slide 19 text

Response型 ■ https://golang.org/pkg/net/http/#Response ■ StatusCode にステータスコードが入っている ■ Body は io.ReadCloser で ioutil.ReadAll などを使って自分でコンテンツを読む必 要がある ■ HTTP keep-aliveを利用するにはプログラマが自前でCloseする必要がある – 忘れがちなので注意

Slide 20

Slide 20 text

テスト ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 21

Slide 21 text

テスト ■ HTTP関連のテストならhttptest.Serverを使うのがらく – 使い終わったらCloseすること ■ table driven testが便利

Slide 22

Slide 22 text

json 処理 ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 23

Slide 23 text

JSON処理 ■ Go は JSONに関する機能は標準ライブラリに含んでいる – encoding/json ■ 基本型や一部の標準ライブラリに含まれる型を相互変換することができる ■ 自分で定義した型も変換できるがここでは扱わない

Slide 24

Slide 24 text

Marshal ■ Goの型からJSONバイト列に変換すること ■ json.Marshalかjson.Encoderを使う – json.Encoderはio.Writerを使えるのでhttp.ResponseWriterと相性が良い – と思わせておいて、エラーが起きるとWriteHeaderとの順番で難しいことに なるのでjson.Marshalを使うのが良い気がする

Slide 25

Slide 25 text

Unmarshal ■ バイト列をGoの型に変換すること ■ json.Unmarshalかjson.Decoderを使う – json.Decoderがio.Readerを使えるのでhttp.Responseと相性が良い

Slide 26

Slide 26 text

JSONと型のマッピング ■ Goのstructはフィールドにタグがかける – どう使うかは言語側は規定していないので好きに使える ■ jsonパッケージはこの機能を活用する

Slide 27

Slide 27 text

タグ付け ■ タグでフィールドにマッピングされるJSONのキーを指定する ■ マッピングするフィールドは公開する必要がある ■ 実はタグがなくても公開メンバが自動的にマップされるがわかりにくいのでつ けた方が良い ■ JSONにキーが現れない場合はそのフィールドは空になる – キーの存在を判定するならポインタを使うと良い type Foo struct { ID int `json:"id"` Data string `json:"data"` }

Slide 28

Slide 28 text

エラー処理 ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 29

Slide 29 text

エラー処理 ■ Goはいわゆる例外を持たない ■ "エラー"の扱いはコンベンションによる

Slide 30

Slide 30 text

エラーを返す ■ エラーを返す関数は戻り値の最後にerror型を書く ■ 戻り値は次の性質を満たすようにする – もしエラーが起きていないならerror型はnilにする – もしエラーが起きているならerror型はnon-nilである – もしエラーが起きているならtypeN達は何らかの無効な値である ■ 大抵ゼロ値が使われる func foo() (int, string, error) { if some error is happened { return 0, "", errors.New("error"); } return 1, "ok", nil }

Slide 31

Slide 31 text

エラーを判定する ■ 関数がコンベンションを満たしているなら次のように書ける ■ エラーを返す関数ではerror型を最初にifでチェック ■ nilでないなら上位に返す ■ nilなら処理を継続する func bar() error { n, s, err := foo() if err != nil { return err } // use n and str return nil }

Slide 32

Slide 32 text

error型 ■ 組み込みのインターフェース – error ■ errors.Newやfmt.Errorfを使って生成する ■ 特定のエラーであることを伝えたり、付加情報をつけるために自前で定義する 場合もある

Slide 33

Slide 33 text

プラクティス ■ Goのerrorはいわゆる例外とは異なりスタックトレースを持たない ■ そのため雑にerrorを上位に伝播するだけだと何が起こったかわかりにくい – 深いロジックで起こったioエラーがmain関数でログされても意味がない ■ エラーが起きた時は次のことを行うのが良い – エラーが起きた箇所でのロギング – エラーをfmt.Errorfなどで付加情報をつけて投げ直す ■ xerrorsを使うとスタックトレースも手に入る

Slide 34

Slide 34 text

context ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 35

Slide 35 text

context ■ 処理を止めるための仕組み – リクエスト単位のkvsとしても使えるがここでは扱わない ■ 例えば – HTTPリクエストをタイムアウトさせたい – 外部コマンド呼び出しをタイムアウトさせたい – goroutineの実行を途中で止めたい

Slide 36

Slide 36 text

contextパッケージ ■ contextは標準のcontextパッケージとコンベンションで実現する ■ contextを扱う関数は第一引数にcontext型を受け取る – 引数名はctx ■ context.Doneはchannelで、closeされるとそのcontextが終了したことを示す ■ context生成関数がCancelFuncを返すなら必ずその関数を呼び出す

Slide 37

Slide 37 text

HTTPサーバでのContext ■ HTTPハンドラーのrequest構造体が持つContext()からcontextを取得できる ■ このcontextはユーザーのコネクションがクローズされると終了する – すなわちcontext.Doneがcloseされる ■ クライアントのコネクションが閉じた時、ハンドラーの処理を停止できる

Slide 38

Slide 38 text

HTTPクライアントのContext ■ http.Clientはcontextのコンベンションを守っていない – contextが後で追加されたため ■ 引数で直接contextを渡す代わりに、http.Requestに対してcontextを乗せる – WithContext ■ context.WithTimeoutなどと組み合わせて使う

Slide 39

Slide 39 text

go modules ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 40

Slide 40 text

Go modules ■ 最近標準で用意されるようになった依存管理方法 – npm, maven, gradle的な ■ Go modulesを使うとGOPATHは不要 – GOPATHを使っているとmodulesが誤作動するので注意 ■ 依存パッケージのバージョンが指定できるのでvendoringが不要になる

Slide 41

Slide 41 text

使い方 ■ 初期化 – go.mod が生成される ■ 依存の追加・削除 – import文から依存がgo.modに追加される – ベリファイファイルのgo.sumも生成される ■ ビルド – 自動的に依存がダウンロードされる $ go mod init github.com/foo/bar $ go get github.com/hoge/fuga $ go mod tidy $ go build

Slide 42

Slide 42 text

Go 関連のツール ■ はじめに ■ 準備 ■ HTTP Server ■ HTTP Client ■ テスト ■ json 処理 ■ エラー処理 ■ context ■ go modules ■ Go 関連のツール

Slide 43

Slide 43 text

Go 関連のツール ■ formatter – go fmt – goimports ■ 不要なimportを消してくれるのでgoimportsを使うのがオススメ ■ checker/linter – go vet ■ コーディングミスっぽいものを探してくれる – golint ■ コードスタイルをチェックしてくれる