$30 off During Our Annual Pro Sale. View Details »

2019-13 実践 Go 言語/2019-13 golang

2019-13 実践 Go 言語/2019-13 golang

Cybozu
PRO

July 04, 2019
Tweet

More Decks by Cybozu

Other Decks in Programming

Transcript

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

  2. 対象者 ▪ A Tour of Go を読んだ ▪ プロダクションコードは書いたことがない ▪

    Go で HTTP client/server を書きたい
  3. アジェンダ ▪ はじめに ▪ 準備 ▪ HTTP Server ▪ HTTP

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

    Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  5. はじめに ▪ この講義では簡単なHTTPサーバを例にGoを書く際のエッセンスを学びます ▪ サードパーティライブラリは扱いません ▪ Goは言語機能が少なく、ライブラリよりもイディオムを駆使する言語です – もっと良い書き方、機能がないかと思っても本当にないです –

    イディオムを覚えて筋力でプログラムしましょう
  6. 準備 ▪ はじめに ▪ 準備 ▪ HTTP Server ▪ HTTP

    Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  7. 準備 ▪ 講義で使用するコンパイラをダウンロードしてください – go (1.12 くらい)

  8. HTTP Server ▪ はじめに ▪ 準備 ▪ HTTP Server ▪

    HTTP Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  9. HTTP Server ▪ Go は HTTP Server に関する機能は標準ライブラリに含んでいる – net/http

    ▪ 重要な型 – Handler – ResponseWriter – Request – Server
  10. Handler型 ▪ net.httpがユーザーリクエストに反応するためのコールバックインターフェース ▪ ユーザーリクエストはReqeust型に入っている ▪ ReponseWriter型を通してレスポンスする type Handler interface

    { ServeHTTP(ResponseWriter, *Request) }
  11. Request型 ▪ https://golang.org/pkg/net/http/#Request – Method – URL – Header –

    Body ▪ この型はClientで使うものと共通 ▪ Bodyがio.ReadCloserインターフェースを満たしているので、ioutil.ReadAllなどで中身 を取り出す ▪ 中身を取り出すのはコードを書く人の責任 ▪ 読んでいる最中にエラーが起きたら、それを処理するのも書く人の責任
  12. 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が便利
  13. ルーティング ▪ リクエストルーティングは簡単なパスベースならhttp.ServeMuxがつかえる – これ自体もHandler ▪ メソッドとか正規表現とか使いたいんですが ▪ 適当なライブラリを探して使う –

    決定版はない印象 ▪ 筋力で自前でルーティング – 細かいバリデーションが必要なエンドユーザー相手でなければあり – ルータライブラリが必要になるようなサーバをGoで書きたいですか?
  14. Server型 ▪ https://golang.org/pkg/net/http/#Server ▪ 実際にコネクションを受け付けてHandlerを起動してくれる型 ▪ フィールドはおおよそオプションだが、Timeoutは設定すべき – 後述する Context

    を使うとさらに細かいTimeoutが設定可能 ▪ ListenAndServerを呼べばサーバが起動する
  15. HTTP Client ▪ はじめに ▪ 準備 ▪ HTTP Server ▪

    HTTP Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  16. HTTP Client ▪ Go は HTTP Client に関する機能は標準ライブラリに含んでいる – net/http

    ▪ 重要な型 – Client – Request – Response
  17. Client型 ▪ https://golang.org/pkg/net/http/#Client ▪ HTTPリクエストを行うための型 ▪ フィールドはオプションでおおよそ何も指定しなくても良い ▪ http.DefaultClientは使わない方が良い –

    設定がグローバル – テストで差し替えれない ▪ 基本的にはDoメソッドだけを使えばよい – Getなどの専用メソッドがあるが、後述するContextが使えない
  18. Request型 ▪ https://golang.org/pkg/net/http/#Request ▪ 見間違いではなくサーバで見たものと同じ ▪ フィールドが多くてびっくりするが、http.NewRequestで生成する

  19. Response型 ▪ https://golang.org/pkg/net/http/#Response ▪ StatusCode にステータスコードが入っている ▪ Body は io.ReadCloser

    で ioutil.ReadAll などを使って自分でコンテンツを読む必 要がある ▪ HTTP keep-aliveを利用するにはプログラマが自前でCloseする必要がある – 忘れがちなので注意
  20. テスト ▪ はじめに ▪ 準備 ▪ HTTP Server ▪ HTTP

    Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  21. テスト ▪ HTTP関連のテストならhttptest.Serverを使うのがらく – 使い終わったらCloseすること ▪ table driven testが便利

  22. json 処理 ▪ はじめに ▪ 準備 ▪ HTTP Server ▪

    HTTP Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  23. JSON処理 ▪ Go は JSONに関する機能は標準ライブラリに含んでいる – encoding/json ▪ 基本型や一部の標準ライブラリに含まれる型を相互変換することができる ▪

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

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

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

  27. タグ付け ▪ タグでフィールドにマッピングされるJSONのキーを指定する ▪ マッピングするフィールドは公開する必要がある ▪ 実はタグがなくても公開メンバが自動的にマップされるがわかりにくいのでつ けた方が良い ▪ JSONにキーが現れない場合はそのフィールドは空になる

    – キーの存在を判定するならポインタを使うと良い type Foo struct { ID int `json:"id"` Data string `json:"data"` }
  28. エラー処理 ▪ はじめに ▪ 準備 ▪ HTTP Server ▪ HTTP

    Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  29. エラー処理 ▪ Goはいわゆる例外を持たない ▪ "エラー"の扱いはコンベンションによる

  30. エラーを返す ▪ エラーを返す関数は戻り値の最後に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 }
  31. エラーを判定する ▪ 関数がコンベンションを満たしているなら次のように書ける ▪ エラーを返す関数ではerror型を最初にifでチェック ▪ nilでないなら上位に返す ▪ nilなら処理を継続する func

    bar() error { n, s, err := foo() if err != nil { return err } // use n and str return nil }
  32. error型 ▪ 組み込みのインターフェース – error ▪ errors.Newやfmt.Errorfを使って生成する ▪ 特定のエラーであることを伝えたり、付加情報をつけるために自前で定義する 場合もある

  33. プラクティス ▪ Goのerrorはいわゆる例外とは異なりスタックトレースを持たない ▪ そのため雑にerrorを上位に伝播するだけだと何が起こったかわかりにくい – 深いロジックで起こったioエラーがmain関数でログされても意味がない ▪ エラーが起きた時は次のことを行うのが良い –

    エラーが起きた箇所でのロギング – エラーをfmt.Errorfなどで付加情報をつけて投げ直す ▪ xerrorsを使うとスタックトレースも手に入る
  34. context ▪ はじめに ▪ 準備 ▪ HTTP Server ▪ HTTP

    Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  35. context ▪ 処理を止めるための仕組み – リクエスト単位のkvsとしても使えるがここでは扱わない ▪ 例えば – HTTPリクエストをタイムアウトさせたい –

    外部コマンド呼び出しをタイムアウトさせたい – goroutineの実行を途中で止めたい
  36. contextパッケージ ▪ contextは標準のcontextパッケージとコンベンションで実現する ▪ contextを扱う関数は第一引数にcontext型を受け取る – 引数名はctx ▪ context.Doneはchannelで、closeされるとそのcontextが終了したことを示す ▪

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

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

    context.WithTimeoutなどと組み合わせて使う
  39. go modules ▪ はじめに ▪ 準備 ▪ HTTP Server ▪

    HTTP Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  40. Go modules ▪ 最近標準で用意されるようになった依存管理方法 – npm, maven, gradle的な ▪ Go

    modulesを使うとGOPATHは不要 – GOPATHを使っているとmodulesが誤作動するので注意 ▪ 依存パッケージのバージョンが指定できるのでvendoringが不要になる
  41. 使い方 ▪ 初期化 – 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
  42. Go 関連のツール ▪ はじめに ▪ 準備 ▪ HTTP Server ▪

    HTTP Client ▪ テスト ▪ json 処理 ▪ エラー処理 ▪ context ▪ go modules ▪ Go 関連のツール
  43. Go 関連のツール ▪ formatter – go fmt – goimports ▪

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