Edited at

[まとめ] Go Conference 2014 autumn

More than 3 years have passed since last update.

写真.JPG


  • 当日の品川シーサイドはミニ四駆大会があった


Keynote1: Rob Pike (@rob_pike) (45min)

Title: Simplicity is Complicated


Go の成功の要因について話したい


  • ちまたで言われているような早さ、容易さ、ツール、ライブラリ、並行性、インターフェースも違う

  • 単純さが要因だと私は思ってる

  • でもそれについて理解してもらうのは難しい


収束しようとしている言語たち



  • Lang.Next カンファレンスにて、様々な言語の新しいバージョンの話をきいた

  • 言語相対性という思想に与える言語の影響という話がある

  • 考え方は様々あるのだから言語も様々あるべき

  • java8, ecmascript6, c++14, c# などは機能追加とともに複雑になりながら収束している


Go はこれらの収束を拒否している


  • Go は言語機能で競わない

  • Go 1.0 のリリースによって、言語機能は固定された


    • 他の言語にある機能要求は、差異をなくして複雑にするもので、拒否された




可読性が最重要事項


  • 一つのことをするのに、一つまたは少なくとも数通りのやり方、それも単純なやり方が望ましい

  • 機能の多さは可読性を損なう

  • 可読性は信頼性につながる


    • 書くのが楽しいよりも保守が楽であることを選択した



  • 表現力の補助のための機能にもバランスが必要


    • やりすぎればパフォーマンスを予測しづらくなるし、可読性も損なう



  • 正しい言語機能は、直交性(≒ やりたいことが過不足なく行える)と予測可能性によって必要か判断される


Go の目的は スケーラブルなクラウドソフトウェアのために設計されたきれいな手続き型言語


  • これを実現するためのすべての構成要素が単純であると私は感じます

  • Gopher のように(スライド参照)


GC は複雑さを隠す単純さの最たる例


  • コードが単純に書けるのは GC のおかげ。free は書かなくていい。

  • 実装はとても難しい


並行性


  • goroutine(実行)、channel(コミュニケーション)、select(調整)の 3 つの要素から成る


goroutines


  • 単純さを示す特徴がある


    • スタックサイズがない

    • return や終了ステータスもない

    • 管理する機構もない

    • ID もない



  • 並行性は他のシステムでも提供しているけれど、Go は最小設計で実現している

  • スタックの管理は GC に依存している


定数


  • 定数は untyped な値として扱われる


    • この特性のおかげで、C における定数のような型の厳密性が適用されないので自由度高く(バグの温床にもなりにくく)扱うことが出来る

    • 例えば、type MyString string という型を作って typed string を代入するとエラーになる

    • untyped string (定数)は代入できる

    • 同じように異なる int 型や float 型など同士の計算式も成立する



  • 詳しくはブログポスト参照


Interface


  • 静的型付け言語に動的型付け言語の性質を持たせる機能

  • もっとも特徴的で強力な機能


    • ライブラリ設計において大きな効果がある

    • その最たる例が io.Reader と io.Writer で Unix のパイプの考え方を一般化している




パッケージ


  • プログラムとライブラリを構成するための設計


    • パッケージのパス("math/big")とパッケージ名(big)を別にした




  • go get とともに単純なパッケージ管理を可能にしている(簡単に使える)

package big

...
import "math/big"


http リクエストを処理する単純な例を見て


  • 日本語の関数名が普通に可能なように、Unicode と UTF-8 をシームレスに扱う

  • パッケージは簡単にインポート出来る

  • Fprintf はネットワーク接続を意識することなく Write 出来る

  • 関数は簡単にメソッドに昇格できる

  • 並行性の機能(goroutines)によって、ブロックしない


質疑応答の一部(Go の Google での使用例)


  • Google の外よりも内での方が普及に成功したと言えるほど


    • Youtube のデータベースアクセス部分(vittess かな)が最も成功例

    • ダウンロードサーバーにも使われている




Keynote2: @fumitoshi_ukai (45min)

Go らしく書くには を伝える


Go Readability Approver に所属してる


  • 社内で Go のよい書き方を教える

  • そこから学んだことを説明する


Readability スキルとはその言語特有のリテラシー


  • Google 社内には言語それぞれで存在する

  • C++ ではプロジェクト(chromium と webkit blink など)ごとに作法が違う、、、

  • C++/Java/Python っぽく書かれても Go ではダメ


  • Want to understand something in google servers? Read the Go implementation !


    • C++ で並行性を実現している部分などは非常に読みにくかったりする

    • C++/Java だとボイラープレートが多いと思う




優れたツール


  • go fmt -- 標準フォーマットに変換

  • go vet -- 間違いやすいコードを指摘

  • golint -- スタイルの問題を指摘

  • godoc -- ドキュメンテーションも自動生成・閲覧


言語特有の作法・慣用表現を理解する必要


  • Go の言語仕様は 50 ページほど


    • C++ では全部理解するなんてありえない




ミス/バグ


error チェック



  • _ で潰したりしない


  • regexp なら MustCompile 使うこと


    • Must を使っていいのは初期化のとき(package の var or init())だけ



  • raw string literal を使うと正規表現が読みやすく成る


write の Close は error チェックをすること


  • buffer の flush 失敗する場合もあるから

  • Read は別にしなくていい

defer func() {

if cerr := out.Close(); err == nil {
err = cerr
}
}


関数の返り値で値と error をまぜない(2 つ返すようにする)


  • 使う側でエラーかそうでないかを判別できなくなる場合がありえるし、使い方もわかりにくくなりがちなため


error の設計


  • エラー処理の区別がない場合


    • error をただ返せばいい



  • エラー処理の区別がしたい場合


    • 複数の error を宣言しておく



  • エラーに情報を含めたい場合


    • 新しい構造体を宣言して、func Error() を生やす



  • 原則論


    • panic は使わない

    • 使う場合には package 内にとどめて、recover でキャッチして最終的にはエラーを返すこと




nil error


  • interface が nil であるためには、型がない && 値が nil の場合だけ

  • 型があって値が nil の場合に nil チェックが false になるから気をつけましょう


interface のチェック



  • var _ scan.Writer = (*ColumnWriter)(nil)


    • ColumnWriter が scan.Writer のメソッドを持つかをコンパイル時にチェックしたい場合に使える

    • 無駄に埋め込んだ定義を書くとかはやめましょう(呼ばれたら panic になるし)




可読性


struct フィールドのレイアウト


  • 関連が深いものをブロックにわける(= 改行する)

  • sync.Mutex を、それが保護しているフィールドの先頭に置くのが良い


長い行


  • 行の長さの制限はない

  • 1 行にした方が grep しやすい

  • もっと短くできないか


    • 名前は短くするのが一般的

    • ローカル変数は短く(そのため func も小さく)

    • 引数も型名からわかるから短く(基本型は少しは説明的に)

    • レシーバー変数((s config) read() {} でいう s のこと)も短く




条件文


  • if-else よりも switch の方がわかりやすい場合も


time.Duration


  • 期間を表すのは int ではなく time.Duration を使う


    • 30 という定数は型を持たないので、time.Duration(30) とかしなくていい

    • 定数は自動的に time.Duration として処理してくれる



var rpcTimeout = 30 * time.Second


sync.Cond と sync.Mutex


  • chan と select を使えば、簡潔にかける


reflect


  • 型がわかっているときは reflect を使わない方が可読性が上がる


テストコード


  • 独自アサート関数は使わない

  • コメントに API の使い方を書くくらいなら Example Test を書くこと


コメント


  • package コメントを書く

  • Export している名前(大文字)にはコメントを書く

  • コメントは対象としているものの名前からはじめる文にする

  • godoc で確認する


    • わかりにくいなら API の設計を見直しましょう




API デザイン


  • package には適切な名前をつける

  • 返り値にはポインタを使わない(複数返せるから必要ない、コピーコストが気になったら使うくらい)

  • slice, map, chan, interface へのポインタは使わない

  • エラーは error を返す、panic しない

  • 一般的な interface (fmt.Stringer, io.Reader)に適合する場合はあわせる

  • 引数は interface にするとテストしやすい


    • ex. ファイルから読む func には *os.File よりも io.Reader を引数にした方が良い



  • chan を package をまたいで使わないようにする

  • 非同期にするのは呼び出し側が goroutine + chan を使うようにする(API は同期 API として書いたほうが良い)


LT 1


  • GoCon T shirt 販売開始しました




Presentation 1

Title: App Engine for Golang Performance


  • Go 早い言われてるけど、早いのか調べた

  • スライド


App Engine とは


  • Good


    • Auto Scaling

    • OS, middleware 周りを気にしなくていい(= カスタマイズも出来ない)

    • Go を使う場合だと net/http 使って init() 内で http.HandleFunc() する感じ



  • Bad


    • v1.2 -- 古い

    • GOMAXPROCS=1

    • ベータ扱い




Managed VMs とは


  • Good


    • Compute Engine 上で App Engine のコンテナが動く

    • App Engine 特有の制限から解放される

    • GOMAXPROCS=X 指定できる



  • Bad


    • deploy が 5 分くらいかかる(App Engine は 1 分くらい)

    • Auto Scaling が 1 必要(App Engine ならリクエストなければ 0 = 無料)




パフォーマンス調査


  • Go と Java で比較

  • Go(App Engine)


    • 1000/s のリクエストまではだいたい 30ms で返してくれる

    • 時間が経ってくると、30ms 以上どんどん上がっていった(原因はわからず)

    • 20 インスタンスで捌けた



  • Java(App Engine)


    • 最初の立ち上がりに時間がかかる

    • 38 インスタンスで捌けた



  • Go(Managed VM)


    • App Engine のときと大体同じ

    • 1 インスタンスで捌けた



  • Java(Managed VM)


    • 503 返して測定不能

    • スパイクをかけるとダメになった



  • 結論、Go のパフォーマンスは良いと思った


Presentation 2

Title: ISUCON で使った話


なんで Go を使ったのか


  • 改善効率が高いため


    • 構文が Simple

    • 並列・並行処理でのメモリ周りの扱いが楽

    • デプロイが楽

    • 標準パッケージが充実




取り組んだこと


  1. プロセスキャッシュ

  2. タスク分散処理


プロセスキャッシュ


  • 永続化は適度なタイミングで Redis に保存したりファイルに dump する


整合性


  • 普通にやると、複数の Goroutines から読み書きされるため整合性に気をつけないといけない

  • Race condition に気をつけないといけない


    • Lock が必要




  • sync -- 各種のロックを扱うパッケージ


    • sync.RWMutex -- Read と Write のロックレベルを分離して使える Lock

    • sync/atomic -- カウンタなどの処理をするために使える




  • atomic.Value -- Go v1.4 から


    • 上記のような処理をより簡潔に書けるようになる




タスク分散処理


  • 並行処理をしつつ、同時実行数に制限をつけたい


Task Queueing


  • capacity 付きの chan を使う


sync.Cond


サーバーの振り分けを適切に行う


  • stathat/consistent を使った


    • コンシステントハッシュライブラリ



  • 重いデータの取得先を振り分ける際に、帯域を使い潰すために使った


Presentation 3

Title: mackerel-agent徹底解説


mackerel アーキテクチャ


  • Web app は Scala

  • agent は Go


mackerel-agent とは


  • メトリクス投稿用の Go 製のプログラム

  • 1 分毎に API サーバーに送る

  • プラグインによる拡張も可能


    • 2 つの要件を満たせば良い



  • ソースコード


    • 常駐処理と定期実行処理とかの参考になるだろう

    • toml の外部ライブラリだけ依存がある




なんで Go


  • マルチプラットフォーム対応が楽

  • フットプリントが小さく監視対象サーバーのパフォーマンスに影響を及ぼさない


ディレクトリ構成


  • command/ -- メイン

  • mackerel/ -- API への投稿処理

  • agent/ -- メトリクス収集処理を束ねる

  • metrics/ -- 各種メトリクス収集処理単位


ソースコード詳解


  • スライド参照


Presentation 4

Title: Why Go program is slow ?


  • スライド

  • ISUCON 5 位


    • CPU 勝負なら今回説明するプロファイラーを使えば勝てるという話をする




CPU profiling w/ pprof



  • runtime/pprof


    • サンプリングプロファイラー


      • モンテカルロ法に似てる

      • パフォーマンスの影響が少ないのでプロダクションで仕込んで置いても良い



    • 対して、ディタミニスティック(決定論的)プロファイラーがある


      • 実行回数が少なくても正確に計算できる

      • パフォーマンスの影響が大きい






  • net/http/pprof が簡単に embed 出来る

  • Mac では動きません


pprof command


  • サンプルとる側は Go で実装している

  • それを解析するのは google perftools の中にあるツールを使っていた


    • ~ Go v1.3 までは bundles CLI (perl)

    • Go v1.4 では Go で再実装された -- より便利に



  • Tips: header で Content-Type を明示的に設定すると早い http サーバー書ける


  • qcachegrind で見ることもできる


  • list, weblist が便利(コードのどこにどれくらい時間使ったか、アセンブラコードレベルで見れる)


Go が遅くなる理由


  • GC

  • memcpy

  • function call


GC


  • GODEBUG=gctrace=1

  • heap profile w/ pprof

  • make 時に capacity hint を渡すようにする

  • GOGC=400 -- デフォルト 100 だが、最初から確保するメモリを大きくしておく


memcpy


  • string と []byte を頻繁に変換するのは避けましょう

  • 最初から []byte で持つとか


function call


  • C よりも遅い

  • ただ、inline 展開はされる


Presentation 5

Title: Golang JP Community


Presentation 6

Title: How we use Go


  • web app で使えるような話をする


    • Gengo というサービスで使ってる




Managing deployments



  • GoShip を作って使っていた


Go API


  • php で 500ms かかっていたのが Go で 10ms に

  • そのため新しく作るエンドポイントは Go で書いている


Managing dependencies


  • simple な shell script に落ち着いた

  • とはいえ、godep がスタンダードになっていると思う


Test Fixtures



  • Gorp を使っている

  • パラレルにできないので go test -p 1


Quick wins (≒ やった方が良いこと)


  • graceful shutdown, exponential back-off が簡単に書ける

  • gofmt -s

  • gofmt -w

  • go test -race

  • goimports

  • go vet


Presentation 7

Title: NSQ-Centric Architecture


Chat サービスを作るなら


  • サーバーは Go を使う

  • WebView なので WebSocket を使う

  • Message Queue には NSQ を使う


NSQ とは


  • デフォルトでは耐久性がない -- 設定で解決できる

  • メッセージは必ず 1 回以上届く

  • メッセージに順番はない


Topics と Channels


  • トピックにメッセージが送られる

  • そのメッセージがチャネルに送られる

  • コンシューマーがそれを取りに来る


nsqlookupd


  • 自動サービスディスカバリ


作りたいのは WebView で iMessage 風のチャットアプリ


  • Protocol


    • JSON をやり取りする

    • server <-> server, client <-> server は同じようにしたい

    • Message Format は自前の json-rpc 形式を使う




Websockets channels


  • サーバー 1 台につき、1 つのチャンネル

  • すべてのメッセージがすべてのサーバーに行くようにして、関係無いものは捨てる

  • 送れなかった場合はトピックが保存しておいてくれる

  • とりあえずはこれでマルチキャストが出来て、チャットが出来る


Archive channels


  • websockets の場合とは役割が違う

  • 保存したいだけなので、1 つのチャンネルに送って複数のコンシューマーから取りに来て、保存する

  • そういう専用のチャネルを用意した


Push channels


  • プッシュ通知するためという役割なので、それ用のチャンネル(サーバー)を用意してそこに送る


Microservices


  • 新しいチャネルを追加することで新しいサービスを追加することが出来る

  • それぞれの機能が独立しているので、停止やデプロイも楽に出来る


Order


  • 順番という概念がない

  • 日付でソートすればいいけど、時計を合わせないといけない


Duplicates


  • 同じメッセージが 2 回以上届く可能性がある

  • UUID などで管理すると良い


React


  • 情報の更新が一元化されて管理される

  • DOM の変更が早い


Presentation 8

Title: Hacking Go Compiler Internals


アーキテクチャ


  • 一般的なシンプルな構造


  1. Lexer

  2. Parser

  3. Escape Analysis

  4. Typegen, GCproggen

  5. Codegen


Lexier


  • テキストのソースコードを抽象的なデータ構造に変換する

  • token と呼ばれる単位が使われる

  • これによって、func とか make とか文法上に使われるキーワードを変えることが出来たりする




Parser


  • tokens から AST(抽象構文木)を作り上げる

  • Parser で一般的に使われている yacc で書かれている


    • これによって、Bracket operator を overload してみるとかが出来る




LT 2

Title: nginx-build


モチベーション


  • nginx のビルドをたくさんする機会があったので、楽するため

  • embedded library とか 3rd library を組み込んで静的にビルドする


使い方


  • go get して binary を手に入れる

  • .ini を用意する



  • ヘビーロードな RTB のエンジンを Go で書いていた


    • それ以来気に入っている




LT 3

Title: Terraform で始める Go 言語


楽天のインフラ as コード


  • サーバー多い

  • インフラのコード化と自動化がないと辛い

  • Packer / Kickstart でブートストラップ、CHEF でコンフィグレーション

  • オーケストレーションは Terraform が良いのではと思ってる


    • 1 service ごとに git (Chef と Terraform) が良いなと




Terraform


  • Go 言語製

  • オーケストレーションツール

  • AWS, Google Cloud とかを公式でサポートしてる


    • 簡単にクラウド上にインスタンス立ち上げられる




Terraform Plugin 開発と Go


  • v2.0 からより開発しやすくなった


LT 4

Title: Goでビルドパイプラインツールを書いた話


  • スライド


  • walter


    • travis や wercker での構成ファイルによるビルドとテスト実行をローカルですることができる




LT 5

Title: go/parser, go/ast の話


LT 6

Title: Unit-testing programs depend on I/O in Go


  • スライド


  • Buffer が io.Reader, io.Writer のインターフェースを実装しているので、ファイルやネットワークのテストでスタブとして使える


LT 7

Title: 分散システムをつくろう


  • consul.io を使って、分散システムによくある課題を解決できる

  • Keynote2: @fumitoshi_ukai (45min)
  • LT 1
  • Presentation 1
  • Presentation 2
  • Presentation 3
  • Presentation 4
  • Presentation 5
  • Presentation 6
  • Presentation 7
  • Presentation 8
  • LT 2
  • LT 3
  • LT 4
  • LT 5
  • LT 6
  • LT 7