2021-08-31 22:58:27
Go Conference 2021 Autumn に向けて CfP をしたためる

CfP の提出は 2021-08-31 が締め切りであり、そして本日は 2021-08-31 である。出すつもりであるのに、どうしていつもギリギリになってしまうのか。

最近の GoCon にはいつも CfP を出すようにしているのであるが、いつも締め切り当日の締め切り間際の時間 (23:50 くらい) に提出しては落選するという展開を繰り返している。いくらか早く (締め切りより一週間くらい前?) に提出すると、運営の方にレビューしてもらえるという話もある。なので、ぜひともギリギリじゃなく提出したいのである。が、今回も今回とてこの体たらくである。すまぬ…。すまぬ…。

CfP 提出を諦めそうな気持ちになりつつ、とはいえこの手のイベントは登壇者なくして成り立たない。Go のコミュニティを盛り上げていくことにちょろっとでも協力できるならば、ぜひとも、微力ながら賑やかしていきたいと思う所存である。なので出すぞ。一個でも出すぞ。

CfP 案

さて、本当は GoCon で過去に採択されたトークの傾向から鑑みて、採択されそうなネタを考えていていきたいところである。しかし残念ながらもう時間がない (現在 2021-08-31 22:55)。話せそうなネタをとにかくぶっぱしていくしかない。話ができそうなネタは、

  • Go で DB をモックせずにテストをする具体的なやり方の例を紹介するぜ
    • 前回はこれで CfP を出したが不採択だった。けどちょっと前回時点より進化したので話せることもありそうな気がする。この話題、実はググっても具体的なやり方の例があんまり出てこない気がするんで、いくらかは需要ある気もする。発表するのも良いけど普通にブログ記事にしとけっていう話かもしれない。
  • 業務で採用している軽量 DDD な Go ウェブアプリケーションの設計を紹介するぜ
    • これも前回か前々回に CfP 出したけど不採択だったような。手垢が付きすぎたネタっていう気はするけど、とはいえそういう登壇あったら、自分なら見に行くだろうなーって気はする。これも多少昔よりも進化しているので話せることはありそう。DDD とかクリーンアーキテクチャとかは用語に気をつけないと刺されそうな話題ではあるよね。センシティブ。
  • 自作 ToDo 管理ツール hashira について紹介するぜ
    • なんだかんだいって何年か使っている自作の TUI ツールである hashira。TUI ツール作るみたいな話もまあ面白いかもしれない。社内の LT とかでは紹介したことがあるけども、詳細に話せば 30 分くらいはいける気がする。

このへんだろうか。面白いかどうかは自分で決めない。とりあえず出しておいて需要があると判断してもらえたら話そう、という考えで行く。

2021-09-01 更新

いまだかつてないギリギリ (2021-08-31 23:58 提出ボタン押下) でなんとか一本だけ提出した…。いい大人がいったい何をやっているのか。夏休みの宿題じゃあるまいし…?(ちなみに papercall のページをよく見てみたら、「お一人様の提出は一本まで」だった。以前は 3 本くらい出せたような?レビューが大変だからっていうことなのかな。とにかく一本が上限なので、別に少ないわけではないことが分かった)

ちなみに暫時悩んで “hashira” の話題で CfP を書いてみた。そういう自作アプリの話みたいのが一個くらいあるのもいいんじゃない、なんて思うがどうかね。ひとまず果報を寝て待つぞ。

2020-07-23 00:54:06
fiddle を使って Ruby から Go を呼ぶ

2020-07-22 に行われた「社内ゆるい Ruby LT 会」にて、「Ruby から Go を呼ぶ」というネタで話をした。

資料を用意しないで、実際にコードを書いて実行する様を見てもらうという感じの発表形式をとったもので「残るもの」がない。だので、資料の代わりと言ってはあれだが何をどう話したかというのを記念に本記事に記しておく。

fiddle

Ruby から Go を呼ぶと言っても色々やり方があり、たとえば、

  • Go で作った実行バイナリを Ruby から system 的なもので呼び出す
  • Go で作った HTTP Server を Ruby の HTTP Client を用いて呼び出す

みたいなのも広い意味では Ruby から Go を呼ぶのに該当しそうな気がする。
が、今回はもうちょっと泥っぽく、「Go で書いたソースコードを共有ライブラリ化したものを用いて、Ruby からそのライブラリに含まれている関数を呼び出す」という形式を披露した。役に立つかはさておき、一番映えそうだったからである。うふふ。

Ruby にはこういう「共有ライブラリを読み込むための機能」が標準で用意されていて、それが fiddle と呼ばれているやつである。
同じような機能を提供するライブラリとして ffi とかそういうのもあるけど、まあ「標準」という言葉に弱い僕であるので、とりあえず fiddle を試していくことにした。

何はともあれ Go で共有ライブラリを作る

まずは呼び出される側の共有ライブラリを作る作業を行った。
ソースコードは以下。lib.go というファイル名で保存した。

package main

import "C"

//export Add
func Add(a, b int) int {
    return a + b
}

func main() {}

何かのチュートリアルに出てきそうな至極シンプルな足し算をする関数。
一応、普通の Go と違う点としては cgo のための諸々 (import "C" しているところと export Add というコメントを付与しているところ) が追記されていることかしら。

これを以下のコマンドでビルドする。

$ go build -buildmode=c-shared -o lib.so lib.go

すると、lib.hlib.so が出力される (今回、ヘッダーは使わない)。

$ ls
go.mod  lib.go  lib.h  lib.so

Ruby から読み込む

出力された lib.so を、Ruby のコード内から読み込む。
このような感じで lib.so をロードし、その中の Add 関数を extern を使って呼び出せる状態にしている。

require 'fiddle/import'

module M
  extend Fiddle::Importer
  dlload 'lib.so'
  extern 'int Add(int, int)'
end

p M.Add(1, 3) # 実際に呼び出しているのはココ

実行結果は以下のようになる。期待通り。

$ ruby ./main.rb
4

晴れて Ruby から Go を呼び出すことができた。便利!

ライトニングトークで触れなかった補足など

ライトニングトークでは、時間の都合上、上記の部分までの話しかしなかった (5 分という縛りだった) のだが、本手法は便利な話ばかりでもなくて不都合だとかやりにくいところだとかが色々ある。そもそも Ruby と Go に限らず、言語間バインディングなんて問題の巣窟になること請け合いであるというのが僕の印象であってだな。

上記のように int を使っている間は特に問題がないのだけれども、もう少し便利に使おうと思ったら他の型 (特に byte array みたいなものとか string) を使いたくなるのが人情かと思う。
実はこのへんを扱うのは割と面倒で、何が面倒って「メモリの確保と解放」をそれなりに意識してやる必要が出てくる。

例えば、Go から文字列を返してやろうと思ったら以下のようなコードになる。

package main

import "C"

//export Fullname
func Fullname(firstname, lastname *C.char) *C.char {
    fullname := C.GoString(firstname) + " " + C.GoString(lastname)
    p := C.CString(fullname)
    return C.CString(firstname + lastname)
}

func main() {}

このコードをざっくり解説すると、

  • Fullname 関数は、lastnamefirstname を受け取って、連結したもの (つまり fullname) を返す関数。
  • 引数、および戻り値の型で string を使うようなところでは、代わりに *C.char を使うようにしている。string を扱おうと思うと Ruby 側では構造体を扱う必要が出てきて少し面倒になってしまうので、生々しいが単なる *char を扱うほうが簡単になる (と思う)。
  • *C.char はそのままでは Go の string として扱えないので、いったん C.GoString に食わせて string に変換している。
  • string に対して一通りやりたい処理をやったあとに、string を戻り値の型である *C.char に変換する必要がある。
  • C.CStringstring を食わせると *C.char 型になって出てきてくれるのだが、C.CString を使って生成した文字列は「メモリ解放」をしなければならない。しないとメモリリークになってしまう。
  • メモリ解放をして良いタイミングは Go 側からはわからないので、Ruby の側からメモリ解放のための関数を呼んであげる必要がある。

そこまでケアすると以下のような感じになる。

Go の側。メモリ解放のための関数を追加した。

package main

import "C"

//export Fullname
func Fullname(firstname, lastname *C.char) *C.char {
    fullname := C.GoString(firstname) + " " + C.GoString(lastname)
    p := C.CString(fullname)

    return p
}

//export Free
func Free(p *C.char) {
    C.free(unsafe.Pointer(p))
}

func main() {}

Ruby の側。メモリ解放のための関数を呼び出すように変更。

require 'fiddle/import'

module M
  extend Fiddle::Importer
  dlload 'lib.so'
  extern 'char* Fullname(char* p0, char* p1)'
  extern 'void Free(char* p0)'
end

ptr = M.Fullname('pan', 'kona')

p ptr.to_s

M.Free(p)

実行結果は以下。

$ ruby ./main.rb
"pan kona"

おそらくこれでメモリリークはしない状態になっている、はず。

つまり

言語間バインディングは用法と用量を守って使うのだ。

参考

2018-01-31 16:30:10
gomobile (build) で作るアプリでファイルを保存する

gomobile という Go 言語で Android/iOS のアプリを作ろうというプロジェクト (準公式) が存在する。
ページはココ → gomobile の公式 wiki ページ - https://github.com/golang/go/wiki/Mobile

gomobile build コマンド一発で Go のソースから APK が生成されるというなかなか豪快なコマンドなのであるが、
いかんせん色々制限があって、いざ何か作ってみようとするとまあまあハマる。
どんな苦行があるかは 手前味噌だが Qiita の記事 に何となく記しておいた。

上記記事には書いてなかったハマりポイントとして、**「ファイルを永続領域に保存する API がない」**というのがある。
ファイルを保存できないということは、ゲーム作ってて何かセーブデータでも保存しよう、とかそういうことができないってことである。
割と致命的である。

ただ、実は gomobile はパッケージ内に JNI を触る実装を持っていて (何故か internal パッケージなので外から触れないが) 、
それを参考にすれば、cgo 経由で JNI に触ることができる。
JNI に触れるということは、Java の API に触れるということで、それはすなわち Android SDK の API に触れるということである。
Go から Android SDK の API に触れるのである。

Android アプリ作成におけるファイル保存領域の取得

ググればさっさと出てくるが、Android アプリを作る際には以下のようなコードを書くと、
ファイルを永続保存できる領域へのディレクトリパスを取得できる (アプリアンインストールと共に削除される) 。

public class MyActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        String dir = this.getCacheDir().getAbsolutePath();
        // dir にはファイル保存可能領域 (internal storage) への絶対パスが入る
    }
}

gomobile (build) アプリ作成時におけるファイル保存領域の取得

つまりは上記と同様の処理を JNI で書いてやれば良い。
以下のようになる。

jni パッケージ (Go言語 + C言語)

指定した関数を JNI 上で実行する関数 RunOnJVM を定義しているパッケージ。

// +build android

package jni

/*
#cgo LDFLAGS: -landroid
#include <jni.h>
#include <stdlib.h>

JavaVM* current_vm;
jobject current_ctx;

static char *
_lockJNI(uintptr_t* envp, int* attachedp) {
	JNIEnv* env;

	if (current_vm == NULL) {
		return "no current JVM";
	}

	*attachedp = 0;
	switch ((*current_vm)->GetEnv(current_vm, (void**)&env, JNI_VERSION_1_6)) {
	case JNI_OK:
		break;
	case JNI_EDETACHED:
		if ((*current_vm)->AttachCurrentThread(current_vm, &env, 0) != 0) {
			return "cannot attach to JVM";
		}
		*attachedp = 1;
		break;
	case JNI_EVERSION:
		return "bad JNI version";
	default:
		return "unknown JNI error from GetEnv";
	}

	*envp = (uintptr_t)env;
	return NULL;
}

static char *
_checkException(uintptr_t jnienv) {
	jthrowable exc;
	JNIEnv* env = (JNIEnv*)jnienv;
	if (!(*env)->ExceptionCheck(env)) {
		return NULL;
	}

	exc = (*env)->ExceptionOccurred(env);
	(*env)->ExceptionClear(env);

	jclass clazz = (*env)->FindClass(env, "java/lang/Throwable");
	jmethodID toString = (*env)->GetMethodID(env, clazz, "toString", "()Ljava/lang/String;");
	jobject msgStr = (*env)->CallObjectMethod(env, exc, toString);
	return (char*)(*env)->GetStringUTFChars(env, msgStr, 0);
}

void
_unlockJNI() {
	(*current_vm)->DetachCurrentThread(current_vm);
}
*/
import "C"

import (
	"errors"
	"runtime"
	"unsafe"
)

func RunOnJVM(fn func(vm, env, ctx uintptr) error) error {
	errch := make(chan error)
	go func() {
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		env := C.uintptr_t(0)
		attached := C.int(0)
		if errStr := C._lockJNI(&env, &attached); errStr != nil {
			errch <- errors.New(C.GoString(errStr))
			return
		}
		if attached != 0 {
			defer C._unlockJNI()
		}

		vm := uintptr(unsafe.Pointer(C.current_vm)) // #nosec
		if err := fn(vm, uintptr(env), uintptr(C.current_ctx)); err != nil {
			errch <- err
			return
		}

		if exc := C._checkException(env); exc != nil {
			errch <- errors.New(C.GoString(exc))
			C.free(unsafe.Pointer(exc)) // #nosec
			return
		}
		errch <- nil
	}()
	return <-errch
}

上記 jni パッケージを使ってファイル保存領域を取得するメソッド (Go言語 + C言語)

上記 jni パッケージを使って、ファイル保存領域を取得するための関数を jni 上で実行する。

// +build android

package storage

/*
#include <jni.h>
#include <stdlib.h>

static const char *
getFilesDir(uintptr_t java_vm, uintptr_t jni_env, uintptr_t jni_ctx) {
	JNIEnv* env = (JNIEnv*)jni_env;
	jobject ctx = (jobject)jni_ctx;

	jclass context = (*env)->FindClass(env, "android/content/Context");
	if(context == NULL) {
		return NULL;
	}
	jmethodID getFilesDir = (*env)->GetMethodID(env, context, "getFilesDir", "()Ljava/io/File;");
	if(getFilesDir == NULL){
		return NULL;
	}

	jobject f = (*env)->CallObjectMethod(env, ctx, getFilesDir);
	if (f == NULL) {
		return NULL;
	}

	jclass file = (*env)->FindClass(env, "java/io/File");
	if (file == NULL) {
		return NULL;
	}

	jmethodID getAbsolutePath = (*env)->GetMethodID(env, file, "getAbsolutePath", "()Ljava/lang/String;");
	if (getAbsolutePath == NULL) {
		return NULL;
	}

	jstring path = (jstring)(*env)->CallObjectMethod(env, f, getAbsolutePath);
	return (*env)->GetStringUTFChars(env, path, 0);
}
*/
import "C"

import (
	"unsafe"

	"github.com/pankona/gomo-simra/simra/internal/jni"
	"github.com/pankona/gomo-simra/simra/simlog"
)

type storageAndroid struct{}

func NewStorage() Storager {
	return &storageAndroid{}
}

var path string

func (s *storageAndroid) DirectoryPath() string {
	if path != "" {
		return path
	}
	jni.RunOnJVM(
		func(vm, env, ctx uintptr) error {
			cpath := C.getFilesDir(C.uintptr_t(vm), C.uintptr_t(env), C.uintptr_t(ctx))
			if cpath == nil {
				simlog.Errorf("failed to get FilesDir!")
			}
			path = C.GoString(cpath)
			C.free(unsafe.Pointer(cpath)) // #nosec
			return nil
		})
	return path
}

DirectoryPath() を呼び出す部分 (Go言語)

上記で準備した関数を呼び出す部分。

func f() {
    dir := storage.NewStorage().DirectoryPath()
    fmt.Printf("path to internal storage: %s", dir)
}

これで、ファイル保存領域へのディレクトリパスを取得できる。
あとは、取得したディレクトリパス配下に保存したいものを保存する。

(余談) キャッシュディレクトリの取得

上記のコードで永続保存が可能な領域へのパスが取得できるので、それはそれで良いのであるが、
実はもっと手軽にファイル保存が可能な領域へのパスを取得できる方法があり、
それは環境変数 TMPDIR を参照することである。

ただし、本値は Android SDK の API で言うところの Context.getCacheDir に相当し、
Android 端末の状況によっては削除され得る領域であることに気をつける必要がある。
が、ちょっと保存したいくらいの用途であれば耐えうるような気がする。
以下のようにして取得する。

func f() {
    dir := os.Getenv("TMPDIR")
    fmt.Printf("path to internal storage (for cache): %s", dir)
}
2017-06-01 19:34:21
golang.tokyo#6@DeNA

2017.06.01 に DeNA さんにて、golang.tokyo #6 が行われました。

golang.tokyo #6 - connpass

開幕は DeNA の方のお話だったんですが、何やら DeNA では Go エンジニアを色々募集しているようです!
サーバーのみならず、色んなフィールドで Go を使っているとのことです。

golang.tokyo-6
今日も大勢の方が参加されております。

golang.tokyo-6
出遅れて私はありつけなかったですが、開幕前からお寿司が振る舞われていました!

golang.tokyo-6
私はおビールいただきました。

Gopher Fest 2017 に参加した話 by tenntenn さん

今日は参加レポートをしてくれました。
主に 2017年8月上旬あたりにリリースされそうな Go 1.9 の話。

スライドはこちら。
https://www.slideshare.net/takuyaueda967/gopher-fest-2017

以下、話の中で気になって点のピックアップ。

言語仕様の拡張 - Alias の追加

型にエイリアスが張れるようになるとのこと。
以下のような書き方ができるようになる。

type Applicant = http.Client

この場合、Applicant と http.Client は等価。

いつこれが役立つかというと、型をリネームするようなリファクタリングを行うとき。
以下の手法だとうまく行かない。

(パターン1) 新しい型を作って順次移行する場合

type Applicant Client
  • Applicant は Client に生えていたメソッドを引き継げない。
  • キャストは可能。

(パターン2) 埋め込みを使う場合

type Applicant struct { Client }
  • Applicant は Client に生えていたメソッドを引き継げる
  • しかしキャストはできない

型エイリアスを用いると、

  • 両者等価なのでキャスト不要で交換可能
  • メソッドもそのまま使える、が、エイリアス側に生やすことはできない

参考

ライブラリへの変更

ビット演算ライブラリ

http://tip.golang.org/pkg/math/bits/
各種ビット演算用の某がそろっております。

sync.Map

スレッドセーフなマップが追加に。
しかも make しないで 0 値のまま使えるらしく便利。

os.Exec 環境変数をコードで上書きできるように

環境変数で指定されている値をコード上で上書きできなかった。
Go 1.9 からは後勝ち。より直感的な動作になる。コードで上書きできるように!

などなど。
スライドには出ていませんでしたが、個人的には mips32 向けの softfloat サポートがどうなったか気になります。Go 1.9 でサポートされるんだったような。

次は DeNA の方。

初めて Golang で大規模 Microservices を作り得た教訓 by Yuichi MURATA

DeNA の方です。
AndApp を作ったときに得られた教訓の話。

Gin と Echo をさまよった話でさまざま苦労なさったそうな。
失敗談をメインに話してくださったので、そういうのは割と貴重と思います。

スライドはこちら。
https://www.slideshare.net/yuichi1004/golangtokyo-6-in-japanese

教訓1. Go でフレームワークに拘ることはない。

  • Gin を使って開発を始める。
  • ちょっと困ったことがあって、一部で別のフレームワーク (Echo) を使い始める
  • 両対応しつつ、また Echo 自身の開発がホットなせいで設計がどんどん変わってしまう

というようなことで、聞いているだけでしんどい気持ちでいっぱいでしたが、
結論としては、フレームワークに頼らずに net/http 使っとくのが一番や、という話でした。
ツールに振り回されるとツライですね。納得。

教訓2. Interface を尊重する - エラーの型の話

独自エラー型と error インターフェースを混在させると起こり得る罠。
独自 error は nil 扱いされない問題。
素直に error インターフェースの利用に統一するべきだという結論。

エラーの種類によって分岐したいとき等、結構ひとによってやり方がまちまちだという気がするので、
なにがしかデファクトスタンダードなやり方が示されていると良い気がしますね。

ちなみに、自分が書くときの「エラーで分岐」については、
deeeet さんの Golangのエラー処理とpkg/errors で記載されているやり方に従っている。

regex compile / reflection の遅さ

JSON Schema でバリデーション。
OSS として扱った二種の速度の話。

  • gojsonschema
    • validation のたびに parse & compile するので遅い
  • go-jsval
    • こちらは速い

regex / reflection 等の処理は遅いので、使うときは意識しないといけない

結論

  • Go の哲学。シンプルなアプローチを。
  • 「コンパイルする言語だから速い」と過信せず、パフォーマンスに気を配ろう。

以下、LT。

ゲーム開発で必要な by @konboi

カヤックの方。
スライドはこちら

CSV の話

データの形によっては非常に見にくいCSV。
いい感じに整形してくれるツールを作った!
csvviewer

スターしておきました。

Go code Review Comments を読もう by knsh14

KLabの方。スライドはこちら。
https://speakerdeck.com/knsh14/go-code-review-comment-wofan-yi-sitahua

こういう記事があって、Effective Go 簡易版というか、Go のお作法初級編みたく書かれている。
Go Code review comments

それを日本語訳した!

初学者のみならず、ある程度経験がある人でも読んで損はないと思いました。
認識を改めるというかね。ちなみに社内勉強会で使わせていただきました、感謝!

Scala から Go にきた話 by James さん

エウレカの方。
スライドは発見できませんでした…。

Scala が好きということは分かりました。
悲しいアリクイかわいい。

Crypto in Go by suzuki kengo さん

マネーフォワードの方。

資料はこちら。アライさんですね。
https://paper.dropbox.com/doc/Crypto-in-Go-cWLX9XxHQm6bAPqrZYkjt

難しい。

まとめ

ということで今回も参加させていただきました、ありがとうございました。
失敗談というかアンチパターンというか、そういう話が聞けたのは大きな収穫でした。

2017.06.15 現在、既に次の golang.tokyo が企画されています。
golang.tokyo #7
気になる方は要チェックです!

2017-03-01 19:31:27
golang.tokyo#4@eureka

2017.03.01 に eureka さんにて、golang.tokyo #4 が行われました。

golang.tokyo #4 - connpass

今回もまた大盛況で一般参加枠は倍率3倍くらいの抽選となっていましたが、
たまたまブログ枠が空いているところに遭遇してしまったため、またしてもブログ枠として
参加させていただきました。内容をレポートしていきます。

発表内容の詳細は、実際発表に用いられたスライド (上記 connpass のページから辿れます) を参照いただくのが一番良いと思いますので、
本記事ではその他、イベントの雰囲気や私の感想を主にお伝えしていくような体になります。
それではいきます。今回のテーマは 「concurrency」


Concurrency for distributed Web crawlers by puhitaku さん

Concurrency for distributed Web crawlers

(↑のカードをクリックでスライドに飛びます。)

AppStore と Google Play をクロールして欲しい情報を収集するやつ

  • クロール対象はシングルドメイン。だが対象となるアプリが多い (100k以上) 。

    • 同一IPからあんまりひどいことするとバンされたりする…。
    • とはいえあんまりちんたらやってるわけにもいかない。24時間以内にクロールし終わる必要がある。
  • Commander と Crawler

    • AWS EC2 Container Service を使っている。
    • 一日30弱くらいのインスタンスを立ち上げて並列でクローリングする。
  • 1 アプリあたりのクロール時間が見積もれないと困る。

    • 1 アプリあたりのクロール速度は割とまちまち。終わるの待ってたらクロール量が安定しない。
    • 一個あたりのクロールを goroutine で行う。
    • 規定時間を設ける。規定時間より超過した場合、終わってなくても次のタスクをスタートさせる。
    • という戦略で、時間あたりのクロール数を見積もれるという寸法。
  • 落とし穴シリーズ

    • TCPコネクションを使い果たす問題。並列に行われるクロールの数が多すぎると TCP コネクションを使い果たしてしまう…。
    • この問題は、make(chan int, 100) みたくして、Channel が保持する値の数を制限することで対応。

Goのスケジューラー実装とハマりポイント by niconegoto さん

Goのスケジューラー実装とハマりポイント

Goroutine の内部実装について。

Goroutine の実装デザイン

  • runtime を読む。

  • https://golang.org/pkg/runtime

  • M、G、P という文字が頻繁に出てくる。それらの意味は、

    • M … Machine
    • G … Goroutine
    • P … Processor

スケジューラーのハマりどころ

  • C 言語の pthread なんかと同じで、goroutine もコンテキストスイッチを考慮する必要がある。
  • Morsing’s Blog に詳しいこと書いてある。

Ridge a framework like GAE/Go on AWS by fujiwara さん

Ridge - A framework like GAE/Go on AWS

Go で書いた HTTP Server を AWS Lambda で動かす…!

Ridge の紹介

  • Ridge

  • 実質、GAE/Go みたいなことを AWS Lambda で実現できる

  • 裏で goroutine を延々動かしておくみたいなことはできない。Lambda はレスポンス返し終わると寝てしまう。

    • 次のリクエストが来たら起きて続きの処理が行われていく
  • 頻繁にアクセスがなく、レイテンシ要求がシビアでないようなものに向く

    • サービスが終了したゲームの告知 API とか。POST を受けて JSON を返す、という処理を EC2 使わないで行う。
  • EC2 使わず、それでいてリクエスト数が多ければスケールする、ということで用途が合えば非常にリーズナブルにできる印象。


ライブコーディング by kaneshin さん

golang.tokyo-4
写真1. ライブコーディング直前の kaneshin さん

ポイント (と思ったところ)

  • kaneshin さんは vim + vim-go プラグインを使って Go を書いている模様。
  • channel をバッファのように扱っている。
  • 入力を待ち受ける goroutine と、出力を担当する goroutine とで 2 並列。
    今回のテーマにあった良い題材であると感じた。

嫁に怒られずに Go を書く技術 by teitei_tk

嫁に怒られずにGoを書く技術

「嫁のため」という免罪符を得て開発していくスタイル

  • LINE に天気予報だったりを投稿する。
  • つまり生活に役立つというか家内に益があれば良いということ。
  • 夫婦円満を願ってやまない。

Gogland by Sergey Ignatov さん

golang.tokyo-4
写真2. Gogland の開発者 Sergey Ignatov さん

JetBrains から Sergey Ignatov さんが来てくれて、Gogland の紹介をしてくれたぞ!

  • function の定義に飛んで実装を確認する必要はなく、小窓で出せるような機能がある。便利そう。
  • 引数のサジェストが賢い。ファジーサーチ的に動きつつ、型が合わないものはサジェストされない、等。
  • 保存時に go fmt、 go import する機能も最近対応された。
  • 2017年3月現在は EAP 版だが、年末くらいには EAP が取れて正式版になるような予定らしい。
  • 意見・要望があったら issue tracker へ!

ざっくりですが、かいつまんで golang.tokyo 4 回目の様子を紹介いたしました。
休憩時間にはビールも振る舞われたりして、無料でいいんですかという気持ちになります。
いつもありがとうございます。

次回もまた 4 月くらいに実施されるようなので、都合が合えば参加させていただこうかと思います。

2016-12-12 19:24:17
golang.tokyo#2@はてな

2016.12.12 に表参道のはてなさんにて、golang.tokyo #2 が行われました。

golang.tokyo #2 - connpass

今回もまたブログ枠にて参加させていただきましたので、
その内容をレポートしていきます。

発表内容の詳細はスライド (上記 connpass のページから辿れます) を参照いただくのが一番良いと思います。
本記事ではその他、イベントの雰囲気や私の感想を主にお伝えできればいいかなと思っています。
それでは行きます。


今回の golang.tokyo は 2 回目の開催。
今後も色々目論まれているようです。楽しみ。

golang.tokyo-2
図1. golang.tokyo について

golang.tokyo 2 回目のテーマは 「テスト」 について。


テストしやすいGoコードのデザイン by deeeet さん

テストしやすいGoコードのデザイン

(↑のカードをクリックでスライドに飛びます。)

golang.tokyo-2
図2. 発表中の deeeet さん。

以下、印象に残ったところを抜粋。

deeeet さんはテストフレームワークを使わない派

テストのフレームワークは使わず、testing パッケージだけで十分であろうとの意見。
これは、フレームワークは「ミニDSL」であって、導入するひとはまだしも、
あとからプロジェクトに入ってくる人は学習する部分が増えてしんどくなってしまう、という一面があるからとのこと。
納得。

テストしやすいコードとは

「Table Driven Test」 がおすすめ。

  • 入出力が理解しやすい
  • テストケース追加が容易
  • 「Table Driven Test に落とし込めるコード」は入出力が明確でテストしやすいコード

テストしにくくなる要素とその対策

テストしにくくなるというのは「Table Driven Test」がやりにくくなる状況を指す。
→ 入力以外の要素が出力影響を及ぼしてしまう状況。

  • グローバル変数 (暗黙の入力)
    • なるべく関数の引数に入れるようにしてテストしやすくする
    • もしくは「デフォルトの値」として のみ 使う
    • 変わらないかもしれない定数っぽい値もなるべく設定可能にする
    • 環境変数もグローバル変数と同じ
  • ユーザーの入力 (コマンドを入力→期待通りに動いたか、のテスト)
    • 入力の受取に os.Stdin を暗黙的に使わず io.Reader を使い、テスト時に仮想的な入力を行えるようにする。
    • 入力に対する出力で、Table Driven にすることができるようになる。
  • ファイル出力 (ファイル出力された内容が正しいかどうか、のテスト)
    • 実際に書いたあとに開き直して中身を確認するのでもテストは可能だが、大量にやろうと思うと遅くなってしまう。
    • 入力のときと考え方は同じで、io.Writer を出力先とし、テスト時はオンメモリのバッファに出力できるようにする。
    • バッファに出力された内容とその期待結果で、Table Driven することができるようになる。

MacherelにおけるGoのエコシステムとかテストとか by Songmu さん

MackerelにおけるGoのエコシステムとかテストとか

(↑のカードをクリックでスライドに飛びます。)

golang.tokyo-2
図3. 発表中の songmu さん

Mackerel のエコシステム周りの話

  • ソースをオープンにしてパッチ受け入れるようにした。
    • ホスト先は GitHub。contribute してもらいやすい。
    • pull request に対するレビュー体制、CI が必要。
    • Travis CI、Circle CI を使っている。CI の内容は以下のようなもの。
      • go vet、 golint go test
      • coverage 計測
      • cross build 可能か
  • ちなみに Changelog はプルリクエストの情報から自動生成している。

ミドルウェアのテスト

  • 実際にテスト時に実行する
    • DB ならばモックせずに実際に DB を立ててデータを入れて確認をする、のような。
    • モックや interface でのテストでは気づけない部分もあるので、実際にやってテストする。

休憩

deeeet さん、songmu さんの発表のあと、いったん休憩に。
休憩ではピザとビールが振る舞われまして、はてなさんにスポンサーしていただいたとのこと。
ありがたくいただきました。

golang.tokyo-2
図4. ピザとビールをはてなさんから振る舞っていただく。

golang.tokyo-2
図5. 会場遠景。芝生です。

golang.tokyo-2
図6. deeeet さんにむらがる Gophers (私もこのあとむらがりました) 。

勉強会の真ん中にこういう親睦会的なノリの時間が設けられるのは珍しいかな?
なんだか新鮮でした。参加者の方とも少しだけお話できたりしました。
勉強会終わってからの親睦会だと参加できないケースが多い私のようなものにとっては、会の真ん中にこういうのやってもらうのもいいかもしれない。


ここから LT コーナー

ピザとビールで温まってきたところで LT 開始。

timakin さん

Plain db import with Go

  • timakin/gopli
    • 開発環境を本番環境に近づけるやつ。本番データをローカルに簡単にもってくる。

osamingo さん

Go で始める JSON-RPC 入門

  • JSON-RPC!

KazuhiraTogo さん

Continuous Deployment with Go on AWS ECS

  • デプロイをとことん自動化した話。
  • 本番とローカルで同じ環境を → Docker を使う。
  • Circle CI 上の docker は Ubuntu。本番は Alpine。環境の違いが問題に。 → Docker on Docker にして解決した。

以上の内容でした。せめて雰囲気くらい伝わればいいですが。
いずれの発表もとても内容が濃くて、勉強になりっぱなしでした。感謝。
ひとまず Table Driven Test ですかね。取り入れてなかったのでやってみようか等と思い。

感謝といえば、運営サイドのこと。
ほぼトラブルなしでスムーズに進んだのは、十分に準備してくれていたということだと思います。
だいたいマイクの電池が切れたりスライドがうまく映らなかったり、そういうの対策しようがなくてしょうがないところもあるんですが、
今回に関してはそういうのほぼほぼなくて、というかマイクの音量とか超ちょうど良くて、本当に細かい配慮を感じました。多謝。
他にも、アンケートを集めて次回のネタにしたりしていて (今回のテーマも、前回のアンケートでテストに関することを聞きたいという要望が多かったから、という理由で選んだとのこと)、golang.tokyo 運営すげーなという印象です。すげーな!

次回もまた来年に予定されているようです。楽しみにしています!

2016-10-25 19:38:13
golang.tokyo #1@mercari

2016.10.25 に六本木の森タワーメルカリさんにて、golang.tokyo #1 が行われました。

golang.tokyo #1 - connpass

以下に togetter まとまってます。
メルカリ本社で開催されたGo言語勉強会 golang.tokyo #1 #golangtokyo - Togetterまとめ

参加者の方のまとめです。きれいにまとまっていて読み応えあります。
Hello Gophers, Hello Golang.tokyo #1 - 365 simple life - g.o.a.t

本イベントにブログ枠にして参加させていただきましたので、
その内容をレポートしていきます。

golang.tokyo-1
図1. 入り口にていただいたステッカー。にゃってとごっふぁーくん。かわゆし。

どういう主旨のイベントかと言うと

いわゆる一般的な勉強会(?)でやるような、「数人の発表者が順繰りにスライドを用いて発表を行っていく」という体ではなく、パネルディスカッション形式。
事前に収集された質問に対して、パネラーの方々が体験談を踏まえて回答をしていく、というのが主な内容。
名だたる5名のパネラーさん達の詳細については、connpass のイベントページを参照いただきたい。

golang.tokyo-1
図2. 司会の tenntenn さん。

golang.tokyo-1
図3. 向かって左正面。Songmu さん、大谷さん、kaneshin さん。

golang.tokyo-1
図4. 向かって右正面。y_matsuwitter さん、辻さん。

写真遠いし真っ黒やんけ。しかし顔で勉強会するわけじゃないということでご容赦いただければと思います。

ちなみに開始当初から軽食、おビールなんかも振る舞われており、20時前開始という、
おそらく夕飯食べてないであろう我々には、とても良い環境を提供いただいておりました。
メルカリさん、いつもありがとうございます。

本番中も質問が募集されていた

Google Apps のどの機能なのかちょっと失念してしまったのですが、
本番中も質問を次々ポストできる形式になっていて+既存の質問に対してイイねが可能になっていて、
みんなが聞きたい質問が優先的に採用されてディスカッションされるというスキームでした。

あんまり見かけないやり方だなぁと思いつつ、視聴者参加型で面白い仕組みだと思いましたので、
今後もやってくれたらいいなー、と感想を残しておきます。

そして本編 - 質問と回答集

前置きが長くなってしまいましたが、
ここからは実際どのような質問がされ、どのように回答がなされたのかを載せていきます。
※ 全部載せるとだだ長くなってしまうので、独断と偏見で端折りつつ、お送ります。

Q. メンバーの Go の教育はどうしてますか?

Go に馴染みのないメンバーにどうやって Go を学んでもらうか?というトピック。

golang.tokyo-1
図5. そしてディスカッションが始まった

Q. IDEやデバッグはどうしているか

Go でデバッガといえば delve かと思ったが、思ったより使われていないのかなという印象。
ちょこちょこテスト書いて動かしていけば、それほど難儀なデバッグが必要になることも少ないっていうことかしら?

  • songmu さん >
    • vim 使っている。メンバーが使っているのは結構バラバラ。
    • デバッグは主に print デバッグ。ちょっとコード書いてちょっとテスト書いて、みたいな。
  • 大谷さん >
    • intellij idea。何人か vim。
    • デバッグは主に print デバッグ。実際に動かしながら。

Q. コーディオング規約、レビューの指針、golint に従うか、など。

個人的には、従うと腹を決めて一度クリーンな状態になれば、あとはさほど苦ではないと思うが、はたして。

  • 辻さん >
    • コーディング規約は CodeReviewComment を基準にしている。
    • 発火させるだけの単純なチャンネルには 空 struct を使う。
    • golint はベストエフォート。というのも、3rd party ツールがが生成するコードが golint に従ってない場合もあったりするようで…。
      • golint の対象を除外は grep -v で頑張る。
  • songmu さん >
    • glint には従っている。従えば Go っぽい書き方ができてくると思う。

golang.tokyo-1
図6.songmu さん回答中。

Q. Webフレームワークとテンプレートエンジンは?ORMは?

  • 辻さん >
    • Echo。パフォーマンス重視の選択。
    • テンプレートエンジンは…標準のを使うのは結構ツラかった。フロントエンドは Go では書いてない。
    • DB のラッパーは squirrel を使っている。
  • kaneshin さん >
    • 当初は Revel を使っていたが、重量級な感じだったのでとっぱらいたかった。
      • 後に Gin で置き換えた。
      • router に gorrila/mux。もしくは標準の http。
    • テンプレートエンジンは標準のがツライ。フロントエンドは SPA を JS で作っている。
    • ORM は XORM。 一部では gorm

ところでフロントエンド事情は…

  • Go のテンプレートはツライ。
  • react とか angular とか使っちゃう。

みな口々に「Go のテンプレートはツライ」と言っていたのが印象的でした…。

Q. エラー処理どうしてますか?pkg/errors? panic は?

  • tenntenn さん >
    • pkg/errors を主に使う。
  • songmu さん >
    • panic はなるべくしないように作る。
    • goroutine の中でのエラーは、sync.ErrorGroup

sync.ErrorGroup の使い方について、
ちょうど近頃類似トピックの記事 - sync.ErrGroupで複数のgoroutineを制御するをポストされていた deeeet さん (※ e は 4つ) からのコメント。

  • deeeet さん >
    • ErrorGroup は便利。たくさんの処理があって、一個でも失敗したらご破産にしたいときに使う。
  • kaneshin さん >
    • error はエラーを上位レイヤーに伝搬させていく思想。
    • panic はしっかり使っていく派。起動直後に実行されて失敗したらどうにもならないものとかに対して。

golang.tokyo-1
図7. deeeet さん (一番奥)。

Q. Git に上がっているオススメの Go で書かれたものは?

  • kaneshin さん >
    • aws-sdk-go。コードジェネレーション部が参考になる。
    • リクエストの作り方、リクエストのリトライの仕方。パッケージの構造なども。
    • google-cloud-go も参考になる。
    • go-github も。
    • kaneshin/gate。Makefile の使い方がポイント。rake task 的な。

Q. ロガーどうしている?

ロガーは logrus が定番といった空気でした。

  • 辻さん >
    • logrus
    • zap というのもある。使い勝手が特殊な感じだが高速らしい。
  • 大谷さん >
    • Web アプリではフレームワークのロガーをそのまま使う。
    • fluentd でひっかけて Big Query に投げる、等。

Q. パッケージ分けどうしているか?パッケージ名、循環 import 問題は?

パッケージ分けは割と悩むポイントかと思いますが、はたして。

  • 松本さん >
    • ひとつのサービス内のサブパッケージは2つか3つくらい。
      • 設計上のドメイン軸で切っていく。ニュース記事・ユーザー・…
      • サブパッケージのサブパッケージ、みたいにこまかく切っていくことはあまりない。
      • リポジトリ一個一個を小さく保つようにしている。
  • tenntenn さん >
    • internal package というのもあるが、使うのをやめた。
    • 別にそこまでしなくても、例えば private とかつけておいて区別さえできればよく、またこうやっとくといざというときにも使える。

あとは、以下のような意見も。

  • Go っぽい感じを意識すると、あんまりパッケージを分けない?
  • microservice だと、microservice 同士で重複した処理が出てきたりする。パッケージ化して、service 同士で共有するか?
    • ロジックを共有すると、変更がお互いに影響してしまうので留意が必要。

Q. テスト周り

  • songmu さん >
    • 最初は標準を使っていたが、そのうち testify を使うように。
    • lestrrat さんの mysql のテストに使うフレームワークを使ったりもしている。lestrrat/go-test-mysqld
  • kaneshin さん >
    • CI 周りはツラくて常に戦っている。テスト全消化で 30 分かかったりしている。ツラミ。
      • 今の気持ちとしては、DB 周りのテストは消したい。モック使いたい。
      • GAE にデプロイするものは、全て Pure Go で動くように設計している。テストしやすいように。
  • deeeet さん >
    • フレームワーク使わない派。フレームワークは mini DSL みたいなものだと思っていて、それを覚えるのはつらい。新規メンバーをげんなりさせる原因。
    • DB 周りのテストは、interface を使ってモックする。依存している部分を interface で分ける。
    • 詳しくはここ → Golangにおけるinterfaceをつかったテスト技法 | SOTA
  • songmu さん >
    • DB のテストはモックせずに実際に DB を立ててやるべき派。
    • ロジック外の部分の問題も留意するべき。設定ファイルの関係で実際に DB にデータが入らなかったりすることも起こる。

Q. デプロイまでのフロート工夫している点。CIとか。

  • kaneshin さん >
    • ansible。dynamic inventry を使っている。

ところで、Go のビルドが遅くなる理由

Go のビルドは一般的には早いと言われているが、遅いとしたらそれは何故かと言うと…?

  • import が煩雑である。依存が連なっていて、フルビルドがかかってしまう場合。

これは例えば、A が B に依存していて、B が C に依存している、なんていうシチュエーションのとき、C を変更したら依存を遡って B も A もビルドが走ってしまう、ということが起こる模様。
ちょうど C言語の include と同じような感じかな。ヘッダー変えたら全ビルド走っちゃうみたいな。
個人的には Go でビルド速度にそれほど苦を感じたことはないが、とはいえ、留意されたしである。

Q. pprof を本番で使っている?モニタリングやチューニングは?

  • 大谷さん >
    • pprof は本番では使ってない
    • モニタリングは zabbix で監視。プロセスが落ちたら復活させたり。
    • チューニング面では、文字列を + で繋がない。とか。
  • 松本さん >
    • pprof ではなく、golang-stats-api-handler を使っている。
    • datadog でリソース監視。プロセスが落ちたらすぐ再起動するようになっている。
    • 個のチューニングではなく、横に並べられる設計でスケールできるようにしておく。札束で殴る。金の弾丸!

その他の質問

概ね、以上のトピックスでディスカッションが行われました。
他にも、回答は得られませんでしたが、

golang.tokyo-1
図8. Go言語プログラマの給料はいかほどか…

なんていう質問も出たり。
ネタも挟みつつ、終始知見に溢れた有意義なディスカッションでありました。

おわりに

運営の方々、登壇の方々、お疲れ様でした!

golang.tokyo #2 も計画されていると噂を聞きますので、
興味を持たれた方、チェックしてみてはいかがでしょうか。
Go 言語に興味さえあれば、参加して楽しいかと思います。

golang.tokyo-1
図9. 戦利品としてTシャツいただきました!