スッとGoを取り入れる
Go 1.6 Release Party
2016-02-17
Hatena Inc. Tokyo Office
Yusuke Wada a.k.a. yusukebe
自己紹介
• Yusuke Wada
• @yusukebe
• http://blog.yusuke.be/
• Wadit CEO / Omoroki CTO
• Web Application Developer
• Technical Advisor
• Co-founder of bokete.jp
• Perl書いてる...
本日のテーマ
Goを本番投入するまでの
道のりとその他
現在の主なGoプロダクト
2個
その1
bokete/webstamp
bokete.jp
お題画像+テキスト
= ボケ
L○NEスタンプ風画像を

ダウンロードできる
これ
その2
yusukebe/revealgo
Markdown driven presentation tool
revealgo コマンドを実行
立ち上がったサーバをブラウザで開く
まさかの GitHub Trending 入り
\(^o^)/
ところがこのスライドは...
Keynote製
とはいえ...
$ go get github.com/yusukebe/cmd/revealgo
Goを取り入れるため
最初にやったこと
✕コードを書く
https://flic.kr/p/81RqH3
✕コードを読む
https://flic.kr/p/aGjVq8
答え...
⃝bradfitzと話す
http://twitter.com/bradfitz
去年のYAPC::Asiaで
bradfitzがボッチだったので...
ゆ:Hiぶらっど!君が書いたギアマンは最高にクールだね
ぶ:どんな用途でギアマンを使ってるんだい?
...(中略)...
ゆ:ところでGoはどうなんだ?
ぶ:まさに今Goでプログラムを書いてるところだ
ゆ:Goのいい所を教えてくれよ
...(中略)...
ぶ:こうやればAndroid向けのバイナリもコンパイル出来るぜ
ゆ:オーケー!Goを使ってみるよ!
予めGo本体の開発メンバである
bradfitz氏と「サシで」話して
モチベーションが上がった
2番目にやったこと
3番目にやったこと
スクリプト的に
Go言語を利用する
スクリプト的とは?
• main.go に package main のみ
• 単発のバッチスクリプトとか
• 例えば...
• MySQLのレコードを1つずつなめて...
• ちょっとした処理を加えてカラムを変更するetc.
例
for ;; {
var results []Entry
db.Select(&results, db.Where("flag","=","0"), db.Limit(limit));
if len(results) == 0 {
break
}
var wg sync.WaitGroup
for _, entry := range(results) {
wg.Add(1)
go func(e Entry) {
work(e)
wg.Done()
}(entry)
}
wg.Wait()
}
goと書くだけで並行処理が走って
DB接続処理もロックしないで便利!
そして調子に乗って...
goroutine走らせまくって
爆死
https://flic.kr/p/6TorAy
どこからGolang化
していくか?
ザックリな
Boketeアーキテクチャ
Web API サーバ
PC版Web スマホ版Web iOSアプリ
Androidアプリ その他フロント...
コアロジック
各種サービス
APIサーバをGoで書き直す必要も無いので特化したサービスをGoで!
Web API サーバ
PC版Web スマホ版Web iOSアプリ
Androidアプリ その他フロント...
コアロジック
各種サービス
スタンプ画像生成サーバがあった!
スタンプ
http://stamp.bokete.jp/{boke_id}.png
Web API サーバ
スタンプサーバ
TTL付きCDN
クライアント
URLのパスに基づきボケIDをゲット
ボケ情報取得
お題画像も取得しつつ
スタンプ 風に画像を生成しサーブ
ボケが削除されることも考慮しつつ
TTLを設定したCDNが画像を配信
シェア等してもらう
Perl製スタンプサーバを
Goでリプレース
git log -p cpanfile
-requires 'Lingua::JA::Fold';
-requires 'Imager';
-requires 'Imager::DTP::Textbox::Horizontal';
Lingua::JA::Fold - 禁則処理を含む日本語文字列の折り返し
Imager - 画像処理モジュール
Imager::DTP::Horizontal - テキスト画像の配置
じゃあレポジトリつくって
画像生成サーバ書くか
(´・ω・`)
次にやったこと
目的に合わせてそれを検証する
スニペットを書いてく
自信がついたらいよいよ
スタンプサーバの実装
出来た構成
$ tree ./
./
!"" README.md
!"" assets
# !"" font-heavy.ttf
# !"" font-medium.ttf
# !"" stamp_404.png
# !"" stamp_footer.png
# !"" stamp_header.png
# !"" stamp_panel.png
# $"" transparent.png
!"" assets.go
!"" client.go
!"" client_test.go
!"" cmd
# $"" stamp_server
# $"" main.go
!"" stamp.go
!"" stamp_test.go
!"" util.go
$"" util_test.go
go-bindataでassets.goに入れ込む
アセットファイル達
WebAPIから情報とってくる
peco/peco参考にした
画像オブジェクトをゴリゴリ頑張る
utilとはいえ主にテキスト処理
ある程度検証後デプロイ
結果こうなった
• デプロイツールとしてAnsible
• リモートレポジトリはGitHubのPrivate
• Ansibleでgit cloneする時はローカルの をforward
• GOPATHのsrc以下にAnsibleでプロジェクトをgit clone
• go get [とってきたプロジェクトのパス]
• GOPATHのbin以下に出来たバイナリを使って起動
リリースした結果
preforkしないからメモリ使用量少ない!
CPUフルフルに使い切る!
すごい!!
と思ったら...
メモリ膨れてる??
この時少しでも
Goが悪いと考えてしまった
僕をお許し下さい...
実は禁則処理ロジックで
無限ループを生む可能性があった
(*ノω・*)テヘ
さらに改良
yusukebe/go-pngquant
gophper.png
pngquant --speed 10
40.3KB 15.1KB!!
pngquantは標準入力からも
受け取れて標準出力にも
書き出せる
func CompressBytes(input []byte, speed string) (output []byte, err
error) {
cmd := exec.Command("pngquant", "-", "--speed", speed)
cmd.Stdin = strings.NewReader(string(input))
var o bytes.Buffer
cmd.Stdout = &o
err = cmd.Run()
if err != nil {
return
}
output = o.Bytes()
return
}
\(^o^)/
GO引にまとめ
Goを取り入れる
• 段階を追っていけばスッと取り入れられた
• 結果スタンプサーバの画像サイズが縮小した
• リソースの節約
• 最初は特化したサービスのリプレースから
• 次は新アプリをGoで書いてみたい
• たぶん... 悪いのはGoじゃない

スッとGoを取り入れる