Developers Festa Sapporo 2015 [Go 1.5 & 1.6の新機能のおさらい] レポート #devfesta

2015.12.14

こんにちは、せーのです。 今日は2015/12/01(火)に札幌コンベンションセンターにて行われた「Developers Festa Sapporo 2015」よりB-4トラックの「Go 1.5 & 1.6の新機能のおさらい」のレポートをお送りします。スピーカーはグーグル株式会社のIan Lewisさんです。

devfestab4_1

Ian Lewis

Google Cloud Platformチーム
Developer Advocate
Google Cloud Platformを 利用する開発者を支援しています。アメリカの首都ワシントンDC出身。

(イベントWebサイトより)

レポート

Goの紹介

「Go」とはGoogleで開発した言語である。これは元々Googleで起こる問題を解決するために作られた。"Googleで起こる問題"とは具体的には「開発スピードを上げる必要がある」ということだ。Googleでは大きな、そして沢山の人が開発に携わっているサービスが多く、それらはパフォーマンスを出して効率的に動かす事が大事。開発にはC++などパフォーマンスが出る言語を選んでいたが、複雑で多人数で開発するとコストがかかる。そこでC++をベースとしてC++で多人数が開発する際に問題になる点を改善させた言語を開発しようと考えた。現状のC++では使いにくい型システムの問題があった。IntegerやStringを組み合わせてObjectやClassを作る時に使いにくくめんどくさかった。またC++は並行処理がやりにくかった。PCやサーバーは複数のコアを持っている。スマホでも8コア持っている。ちゃんと並列処理を書かないと8コア全て使えない。Goはその辺を考慮され書きやすくしてある言語なのだ。

Goの特徴

ネイティブコードにコンパイル

ほとんどの言語はVMで動く。だがネイティブではないので実行時に変換させる必要がある。この部分がオーバーヘッドになっていて非効率だったがGoはネイティブコードにコンパイルが可能となっている。

ガベージコレクション

ネイティブコードでコンパイルしている言語は大抵自分でメモリを管理しなくてはいけない。開発スピードがダウンする。メモリリークが起こる。そこでGoにはガベージコレクションがついている。

型付き

大規模チームで開発している場合、型付きじゃないと動的に方がつけられるために実行しないと動くかどうかわからない。できるだけ型をつけてAPIを定義しバグを減らす。GoならString XXX = new String XXのようないちいち書く必要はない。

Structs/Interfaces

Goではできるだけ簡単にアプリケーションの型を組み合わせてObjectを作れるようにしている。JavaのInterfaceはクラスを定義した時にimplementsを書く必要があるが、Goならいらない。またJavaはextendは1つのみ。GoならいくつかのStructsをextendすることができる。

並行処理

ThreadとかprocessとかはLowLevelで使いにくい。Goはこれらの並行処理を表現するのに「Goroutines」と「Channels」という仕組みを使う。

Goroutines

Go1-5 - 1

Goroutinesは沢山作ることができ、立ち上げるのも廃棄するのも早い。

Channels

Go1-5 - 2

ChannelsはGoroutineの間をとりもつ役割を担い、ちょうどキューのような形となる。例えば上の図のようにAが何かの処理をする用のChannelに突っ込み、Bはそれを処理した後別のChannelに突っ込んで返す、というような事が可能となる。

事例

どのようなアプリがGoで作られているかを紹介。

  • Docker: コンテナのランタイムシステム。コンテナのイメージのようなものを持っている。
  • etcd: 分散のkey-value store。サーバーの状態を保存してくれる(IPアドレスやMACなど)。分散しているので一つ壊れてもちゃんと動く。
  • Vitess: youtubeのバックエンドデータベース(mysql)をシャードしてくれる仕組み。オープンソース。
  • Kubernetes: Dockerは一つのサーバーの中でコンテナを実行するもの。Kubernetesは複数のサーバーの中でコンテナを実行するために使用するクラスタマネージャー。どこのサーバーに入れるのかを意識せずにKubernetesを叩けばいい。
  • Gorilla: webのツールキット。Webアプリケーションとして作っている。セッションの管理。URLのルーティング。

Go 1.5

2015/08リリース。1.0が出たのが3年前なので大体半年ごとに新しいバージョンを出していることになる。
Go 1.0から一番大きいリリースとなる。Goは互換性を重視しているので、新しい機能を追加する、というよりパフォーマンスの向上やツールの作成が主で、新たな言語を作ってはいない。

そんな中Go 1.5は新しい並行GCを備え、Goのコンパイラが大きく変わるシリーズとなった。「パッケージベンダリング」という新たなコンパイル手法を取り入れ、依存ライブラリが更新されて自分のアプリが動かなくなる、という悲しい出来事が起こらなくなった。またGoroutineのスケジューラも改善された。

並行GC

Go1-5 - 6

ユーザープログラムと平行にGCが走るためプログラムが停止する時間が短くなる(10ms以下)。

リクエストを受けてレスポンスをかえすと使っていたメモリは要らなくなる。使っているうちにゴミがどんどん溜まる。ゴミが沢山たまるとGCが遅くなる。
そこでアルゴリズムとしては

  • stackをスキャンする。stackはアプリケーションのメモリ。アプリケーションのメモリから使っていないものを見つける
  • 見つけたらMarkフラグを立てる
  • Markフラグを立てたものを削除する(Mark termination)
  • Markフラグを外す
  • 500MBヒープを持っていても3msしか止まらない

という仕組みを使っている。

コンパイラやツールチェインの改善

Goのコンパイラは今までCで書かれていた。そのためGo1.5をコンパイラするためにGo1.4で書いたコンパイラ、つまりCコンパイラが必要だった。そこでC言語をGoで書き換えるようなツールを作ることでCコンパイラが動かない状況がなくなり開発がシンプルになった。今後はGoの修正や改善がコンパイラ自体にも適用されることになる。

ツールチェインとして新しい「go trace tool」を作った。これにより自動テストを実行する時にテストの様子をためておいて後で見ることが出来るようになった。並行処理が走っているアプリはデバッグしにくく、go tool traceは自分のアプリケーションがどう動いているかよくわかるのでデバッキングによく使う。実際に動いているアプリケーションから取ることもできるが、若干パフォーマンスが落ちるので注意。

Goパッケージベンダリング

Go1-5 - 17

[GO15VENDOREXPERIMENT=1]という環境変数を入れる必要がある。この変数を定義することでGoはvendorディレクトリを自分の中に入れてパッケージをインポートする。これによりvendorディレクトリがどこにあるかによってスコープを狭くする。同列にディレクトリがあればインポートでき、特定なバージョンを部分的に使うことができる。
ベンダリングするツールがいくつかあり(Godepsなど)、わかりやすくて便利。

Go 1.6

半年ごとにリリースしているので2016/01リリース予定(?)となる。

主な機能

  • Betaだったパッケージベンダリングがstableへ
  • HTTP/2サポート(net/http): 今まではhttp2のサーバーを呼び出していたがconfigureサーバーが要らなくなった。HTTP2に対応していないブラウザからのアクセスはHTTP1.1にフェイルオーバーするようになっている。
  • Text/HTMLテンプレートのコードブロック: 今まではincludeしかなかったが、1.6よりテンプレートの継承が実現できる。blockを定義して別のテンプレートでオーバーライドする形。ベーステンプレートの変えたい文字をdefineで囲んであげることでそこだけ入れ替えることができる。
  • メモリサニタイザー

まとめ

いかがでしたでしょうか。Goは使いこなすと強力な言語になります。GoogleがバックということもありIoTを始め色々なデバイスにも対応することが期待されます。