YAMAGUCHI::weblog

ジャイトニオ猪場のはからいで、全財産を失いました。トランスマスターケンちゃんこと、剣持です。

「Go言語による並行処理」という本が出版されました #cingo

はじめに

こんにちは、Stackdriver担当者です。このたび私の印刷書籍としては2冊めの翻訳本「Go言語による並行処理」がオライリー・ジャパン社より出版されました。本日より書店ならびに各社オンラインストアでご購入いただけます。

Go言語による並行処理

Go言語による並行処理

電子書籍版についてはオライリー・ジャパンのサイトよりePub、mobi、PDFの各種フォーマットにてご購入いただけます。 www.oreilly.co.jp

誤字脱字等を見つけた場合にはこちらのレポジトリまでご報告ください。 github.com

「Go言語による並行処理」はどのような本か

本書はKatherine Cox-Budayが2017年に執筆した "Concurrency in Go" の日本語訳書籍です。

Concurrency in Go: Tools and Techniques for Developers

Concurrency in Go: Tools and Techniques for Developers

この本はこれまで多くの場所で議論されたり語られたりされてはいたものの、書籍のようなまとまったボリュームで記述されることが少なかったGoでの並行処理についてのあれこれを1冊にまとめた本です。 Goの特徴といえば、並行処理を前提とした言語仕様および標準ライブラリでのサポートが挙げられますが、本書ではそれらの前提にある理論の説明と、実際にそれらを使った並行処理のパターンを記述方法についての説明、さらにはランタイムの内部動作に至るまでを触れています。

ともすると並行処理に関する理論などはそれだけでも非常に多くの説明を要し、このサイズの本には収まりきらない内容になってしまうところですが、本書では説明に必要な最低限の部分だけをうまく切り出し、それらを抑えた上でGoでの実装に多くの分量を費やしています。最低限とはいいつつも、必要な内容に関してはうまく導入しているので、理論的な内容を知りたい方も本文や脚注で触れられている参考文献を参照することで、より深い内容を理解するきっかけにもなることと思います。

また本書ではGoでの並行処理で陥りやすい失敗はなにか、紹介するテクニックがなぜ必要になるのか、といった内容も失敗例も含めて記述されているので、実際にコードを書いてはまったことがある人にはより実感を持ってご理解いただけるのではないでしょうか。

すでに @mattn_jp さんや @lestrrat さんより書評をいただいていますので、そちらもあわせてご覧いただけると、どのような本かよりイメージしやすいかと思います。

mattn.kaoriya.net

medium.com

出版に至るまでの話

私がGoを知ったのは、2010年2月ごろで、まだGoに go tools がなく、Makefileでビルドし、クロスビルドはできていたものの、対象アーキテクチャごとに 6g8g などの専用コンパイラを使う、まるでPlan 9そのままといった様子でした。

その後私がGoogleに転職し日常的にGoに触れるようになり、Goを本格的に使いだしたのはGo 1.0がリリースされる前後でした。その後もGoは今日に至るまで発展を続け、Makefileアーキテクチャごとのコンパイラをいちいち叩きながら使っていたビルド環境も徐々に go tools として一つにまとめ上げられ、またかつては数少なかったサードパーティーパッケージも、いまや実用に耐えうるものが潤沢になりました。そしてGoを本番環境システムの開発用言語として採用する企業もいまや当たり前となりました。はじめてのGoConであるGoCon 2013 spring を開催したときには、まだ採用している企業が日本ではほとんどなかったことを考えると、言語自身だけでなくコミュニティもものすごい勢いで拡大してきたことがおわかりでしょう。

そんなGoですが、オープンソースとしてリリースされた当初から変わらず、それでいてもっともGoをGoたらしめているもの、それが goroutinechannelselect といった並行処理に関するプリミティブです。 Goがこれほどまでに急速な発展を遂げたのも、Goの並行処理のプリミティブが非常に強力であったことと、マルチコアCPU時代かつ分散処理が当たり前になりつつあった時代の要請とが見事に噛み合ったことが大きな要因の一つでしょう。*1

そうしたプリミティブは使い始めるには非常に簡単ではあったのですが、どんな道具でもそうであるように「使いこなす」ためには経験が必要となります。他言語での経験があった開発者も、スレッドプールなどを用いた並行処理のパターンをゴルーチンに当てはめて使うことは容易だったとは思いますが、それでもGo独特の並行処理の記述方法やパターンなどは標準パッケージ内での記述や、 golang-nuts のような開発者メーリングリストでシェアされたことで広まったものも少なくありません。

私自身もそうしたところから学んでいった一方で、2年ほど前に、こうした一箇所にまとまっていない情報が整理された書籍があればいいなと考えていたところでした。そんな折に原著である Concurrency in Go の出版の話を知り、以前よりお世話になっていたオライリー・ジャパンの瀧澤さんにその翻訳ができないか聞いてみました。最初のお返事では訳者と編集のご担当の方がすでにいらっしゃるということでしたので、私もそれを楽しみにしていたのですが、その後紆余曲折あり6年ぶりにオライリージャパンでの翻訳を瀧澤さんとご一緒させてもらえることとなりました。

かねてより瀧澤さんのお仕事は間近に見ていたので、今回も翻訳開始前から非常に頼もしく、実際編集に関しても非常にやりやすい環境を提供していただけました。初校、レビュアーによるレビュー、二校、三校と各段階で間に原稿が進まない時期もあったのですが、瀧澤さんにスケジュールをご調整いただき無事に年内に発刊に至りました。

謝辞

訳者序文でも謝辞として執筆させていただきましたが、本書では多くの方々よりレビューをいただきました。 あらためて本書の出版にあたり、忙しい業務や私生活の合間を縫ってレビューに参加してくださった皆様に感謝いたします。

レビュアーの皆様(五十音順)

  • 伊藤友気さん (@mururururu)
  • 上田拓也さん (@tenntenn)
  • 上西康太さん (@kuenishi)
  • 小泉守義さん (@moriyoshit)
  • 渋川よしきさん (@shibu_jp)
  • 知久翼さん (@_achiku)
  • 中島大一さん (@deeeet)
  • 松木雅幸さん (@Songmu)

オライリー・ジャパン

  • 瀧澤昭広さん (@turky)

特に、上西さん、知久さんには英語の細かなニュアンスの差異などを指摘していただき、大いに参考になりました。また小泉さん、渋川さんには訳注として追加すべき情報や技術的な考慮点などを共有いただいたことで、本書の内容がより充実したものとなりました。@mattn_jp さんには書評をいただいただけでなく、本書の告知にも多分にご協力いただきました。あらためて、ありがとうございました。

参照

*1:他にも多くの開発者が慣れている手続き型指向の言語であったこと、実行速度が速いこと、標準のツールが充実していること、シングルバイナリにビルドされること、メモリフットプリントが小さいこと、ランタイムの起動が速いこと、ビルドが継続開発に耐えうる時間で完了すること、など理由は様々だと思います。

make関数でチャネルを作成する際にキャパシティを指定しない場合の内部動作

はじめに

こんにちは、Stackdriver担当です。Goの make 関数はあらゆるビルトイン型の作成を行う役割を担っているわけですが、諸用でチャネルを作成する際にキャパシティを設定しなかった場合にデフォルト値の0を設定する処理はどこでやってるのかなと思って見てみました。

Go言語による並行処理

Go言語による並行処理

TL;DR

メモ書き

Goのbuiltinは src/builtin/builtin.go にあると見せかけて、それはgodocのために書いてあるだけのダミー。 実際は src/rumtime 以下と src/cmd/compile/internal/gc 以下に隠されている。

src/runtime/chan.go には func makechan(t *chantype, size int) *hchan があり、これが唯一のチャネルを作る関数。したがって、これを呼び出すときにはintの引数が必要になる。

makechan 関数は src/cmd/compile/internal/gc/walk.go 内の func walkexpr(n *Node, init *Nodes) *Node構文解析した結果のノードのオペレーターが OMAKECHAN という識別子になっていた場合に呼ばれている。

OMAKECHAN はどこで呼ばれているかと言うと、src/cmd/compile/internal/gc/typecheck.gofunc typecheck1(n *Node, top int) *Node 内でやはりオペレーターが OMAKE だったときに呼ばれていて、かつその引数が TCHAN (チャネルの識別子)だった場合に呼ばれている。

go/typecheck.go at ae9c822f78d5048aa4290b06a5a38f67aaf23dbe · golang/go · GitHub

case TCHAN 内で i という値は引数の数として i = 1 (=チャネル型を指定しているところ)がまず最低限の数として定義されていて、もしこの他に引数がある(=キャパシティ)の設定がされている場合にはその値を設定している。

で、知りたかった、キャパシティの引数が設定されていない場合は、 n.Left = nodintconst(0) が設定される。 詳細は省くが nodintconst(0) は名前の通り 0 というintの値を持ったノードを作って返すので、ここでデフォルト値のキャパシティの0が設定されている、ということでした。

GoでStackdriver Logging向けのログをお手軽に出力する設定

はじめに

こんにちは、Stackdriverで遊んでいる人です。Stackdriver Loggingは標準出力に出されたJSON形式のログをFluentdベースのエージェント経由でいい感じに表示してくれます。

一方でGoに限らず通常のロギングライブラリは標準エラーにログを吐くという感じになるのですが、Stackdriver Loggingの場合デフォルト設定だと標準エラーに吐かれたログはすべてエラー扱いになりますので(まあ当たり前だよな)、そのあたりのすり合わせ調整が必要。

標準の log パッケージの場合

標準パッケージだけ使うのであれば、とりあえずこれだけやっておけばOK

  • 標準出力に出す(GKEの場合)
  • JSON形式でいくつかの決められたフィールド名(message, time, severity)を満たす

ということなのでこういう log.Loggerインスタンスを作ってあげる感じになりそう。

func sdLog(l *log.Logger, severity, msg string) {
    now := time.Now().Format(time.RFC3339Nano)
    entry := map[string]string{
        "time":     now,
        "severity": severity,
        "message":  msg,
    }
    b, err := json.Marshal(entry)
    if err != nil {
        log.Fatal(err)
    }
    l.Print(string(b))
}

logger := log.New(os.Stdout, "", 0)
sdLog(logger, "info", "hello")

logrus を使う場合

上の条件を満たすだけなんだけども、 logrus の場合はJSONFormatterがあるし、logrus.Loggerに各種ログレベルにあわせたメソッド(InfofWarnf など)があるので楽に設定できる。

log := logrus.New()
log.Level = logrus.DebugLevel
log.Formatter = &logrus.JSONFormatter{
    FieldMap: logrus.FieldMap{
        logrus.FieldKeyTime:  "timestamp",
        logrus.FieldKeyLevel: "severity",
        logrus.FieldKeyMsg:   "message",
    },
    TImestampFormat: time.RFC3339Nano,
}
log.Out = os.Stdout

Stackdriver LoggingのGo用のクライアントライブラリを使う

これを使っていいならはじめから使ったほうが良い。ただ個人的には logrus のほうがseverityに応じたヘルパーメソッドがあるので使い勝手が良いように感じる。

godoc.org

client, _ := logging.NewClient(ctx, projectID)
logger := client.Logger("my-log")
// Text Payload
logger.Log(logging.Entry{Payload: "Hello, world!")

// JSON Payload
type MyEntry struct {
    Name  string
    Count int
}
logger.Log(logging.Entry{
        Payload: MyEntry{Name: "Bob", Count: 3},
        Severity: logging.Critical,
})

ターミナルのデモ用プロファイルで特定の環境変数を反映させる

はじめに

こんにちは、Stackdriver担当の者です。仕事柄デモを大勢の前で行う機会が度々あるのですが、その際にターミナルのプロファイルを普段使っているものと分けたくなりました。とりあえずこういう形が良いかなというところで落ち着いたのでメモしておきます。

やりたいこと

  • 文字サイズの変更(デモ用なのでとても大きく)
  • 起動時に環境変数の読み込み
    • PS1をシンプルなものに
    • GCPAWSなどのcredentialを自動で環境変数に設定し、デモ中に読み込みの必要を無くす。また万が一ターミナルを落としてしまっても、同じプロファイルで起動するだけで良くする。

各種ターミナルエミュレータでの設定

macOSのiTerm2での設定

macOSではiTerm2を使っていますが、Profileの設定でCommandという項目があります。

f:id:ymotongpoo:20180821132428p:plain

ここを "Login shell" ではなく "Command" にチェックを入れ、起動させたいコマンドを書くわけですが、ここにこれから書くシェルスクリプトへのパスを書いておきます。

LinuxでのGNOME Terminalでの設定

マシンによってはLinuxデスクトップ環境で、GNOME Terminalを使っています。その場合も同様に "Run a custom command instead of my shell" にチェックし、そこに同様にシェルスクリプトへのパスを書いておきます。

f:id:ymotongpoo:20180821133550p:plain

.demo.sh の内容

起動時に呼び出すコマンドは単純に通常使っているシェルを呼び出し、その後各種環境変数の設定をexportします。

#!/bin/bash
export DEMO=1
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credential.json
export AWS_ACCESS_KEY=xxxxxxxxxxxx
export AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxx
export AWS_DEFAULT_REGION=us-east-2
source /path/to/venv/bin/activate

exec /bin/zsh

ここで先頭の DEMO という環境変数は、自分の .zshrc 内でプロンプトの切り替えを行うために設定しています。

...
if [ -n "${DEMO}" ] && [ "${DEMO}" = "1" ]; then
  PROMPT="> "
fi
...

これでデモ用プロファイルを指定して立ち上げるだけでいい感じになりました。

深圳に行ってきた(その2)

はじめに

こんにちは、Stackdriver担当になったものです。StackdriverはGoogle Cloud Platform内の製品群で、ロギング、エラーレポート、トレース、モニタリングなどを扱いやすくするものです。

さて、この記事は下の「深圳に行ってきた(その1)」の続きです。

ymotongpoo.hatenablog.com

華強北(华强北)の雰囲気

自分が行ったのはまさに今週の水曜日なので夏が始まったばかりだったのですが、外気温は34度。とてもじゃないけど外にい続けたら暑くてかないません。

歩道の幅が青山通りくらいあるので、人が少なく感じますが、実際歩行者の数自体もそんなにいなかった気がします。平日だから、というのもあると思いますが、多くの人が建物内にいたのでしょう。また近くに昼食を取れるお店が多くなく外に出たくないので、デリバリーを頼む人が多かったのか、電動スクーターが数多く走っていました。深圳に来て驚いたのは、QR決済よりも電動スクーターの数とその馴染み方です。

上のようにデリバリーサービスの電動スクーターが普通に日本の原付スクーターと同じような感覚でバンバン走っていました。広場からちょっと外れた通りではこのような馴染み具合。

どれくらい電動スクーターが多いかは駐輪されている数をみると明らかです。近くに止められていたもののうち半分以上が電動スクーターでした。

一方で、レンタル自転車も健在です。道路の反対なので見づらいですが、黄色いのものがofo、オレンジのものがmobikeです。

ところで、わりときれいめな写真ばかり載せてきましたが、やはり急速に発展したところには必ず光が当たらない場所もあり、ビルの中にはがらんどうになっているブースやビルの中でもゴミが散乱していることもあり、館内禁煙の建物でも普通に廊下で喫煙している人がいたりします。

そうした建物を子供が遊んでいるのを見ると、なかなかに切ない気持ちになったりすることもあります。

京基100(KK100)

さて時間はあっという間に過ぎて3時過ぎ、深圳から香港まで2時間かかることを考えると、そろそろ別のところに移動してもう一つ何かみたいなと思っていたところに良いものを見つけました。

遠くに見える高い先に向かって尖ったビルは深圳で最も高いビルである京基100です。これは行ってみる価値がありそうだということで、タクシーを捕まえられないかとビルの方向に歩いていましたが、結局地下鉄に乗りました。

大劇院(大剧院)駅から直結で建物には入れましたが、京基100には展望台はないと言われます。残念だなあと思っていたところ、中にあるホテルの利用者であれば行けるとのこと。そして、96階にある展望ができるラウンジは一人230元(3800円)払えば「アフタヌーンティー」の利用ができるとのこと。次いつ来るかもわからないし、スカイツリーの450m展望台にはもっと高い料金を払って行ったことがあるので、まあいいか、ということで入りました。

96階からの眺めはなかなかに圧巻で、深圳が中国で3番目に大きな都市だということをあらためて意識させられる眺めでした。ガスで霞んでいるのも、発展している地域であるからこそかなと思いました。ところで230元払った「アフタヌーンティー」ですが、割と良心的なもので、230元までならメニューにある飲み物と食べ物を自由に頼んでも良いというものでした。ビールを数杯とチーズを食べつつ、96階からの眺めを堪能できるのはなかなかの体験です。

面白かったのは国境側の眺めで、川を挟んで向こう側とこちら側で明らかに発展の様子が違う。実際に帰りにMTR羅湖駅から撮った写真でも其の様子は明らかです。

MTR羅湖駅は深圳から見たら川向こうで、先の写真のように非常に牧歌的な風景でした。そして遠くに見える深圳の町並みは急速に発展した近代都市という感じです。

中国を垣間見る

深圳の街中にもプロパガンダ広告をしばしば見かけました。

「人民には信仰があり、民族には希望があり、国家には力がある」これは一昨年の共産党党大会での習近平の言葉ですね。 またほかにも電気街の建物の中に次のような垂れ幕がありました。

見切れていますが、これも去年の共産党党大会で習近平が触れた「習近平による新時代の中国の特色ある社会主義思想」が、そのまま垂れ幕としてかかっています。

さきほどの京基100からの風景を見たり、街中の発展を見ていても感じましたが、国が特区として開発を進めることの利点はトップダウンのスピード感だと思います。深圳がこれほどまでに急激に発展して世界有数の技術特区になったのは、ひとえに国策によるものでしょう。

電気街のビル内に局所的に見られた荒廃も、今後の発展で上書きされ、また新たな発展をしていくのでしょう。実際、華強北から京基100に向かって歩いているときも、古いビルを潰して新しい巨大なビルを建築している工事現場の横を通りました。

巨大な中国がトップダウンで開発を進めているその根底にあるのは、こうした共産的な動きなのだなと、改めて感じました。

深圳は面白い街でした

単純に電気街を歩くというのは自分のような人間には楽しいもので、時間を忘れてすごせるので、またぜひ来てみたいです。 それだけでなく、もともとの深圳がまだ残っている場所は今回の訪問では見ていないので、次回来たときにはぜひ見たいと思います。 また次回来るまでには新たな開発が行われていることでしょう。そうした街の変化もぜひ見てみたいです。