2017/01/25


golang にはパッケージマネージャが無数にあります。

PackageManagementTools · golang/go Wiki · GitHub

Home Articles Blogs Books BoundingResourceUse cgo ChromeOS CodeReview CodeReviewComments CodeTools C...

https://github.com/golang/go/wiki/PackageManagementTools

僕もその一つの gom というのを開発している訳ですが、どれもこれも一長一短でなかなか全ての要望を応えられる物がないのが現状だったりします。ただし、開発者がやりたい事は

今 GOPATH にある、うまくビルドできるバージョンに依存したい

ただこれだけなのです。なんで golang はオフィシャルがこの手のツールを出してくれないんだろう、そう思っていた人も多いと思います。が、それは昨日までの話。

GitHub - golang/dep: Go dependency tool

See the help text for much more detailed usage instructions. Note that the manifest and lock file fo...

https://github.com/golang/dep

でたー!みんな待ってた。そしていつもながら他のツールと名前がバッティングしそうなこの名前の短さ!

使い方も簡単です。まずプロジェクトを作ったら

$ dep init

を実行します。すると lock.jsonmanifest.json Gopkg.tomlGopkg.lock が生成されます。この時点では中身はほぼ空っぽです。試しに以下のコードを書いてみます。

package main

import (
    "github.com/mattn/go-runewidth"
)

func main() {
    println(runewidth.StringWidth("あ"))
}

そして以下を実行。

$ dep ensure

すると lock.json が更新されます。

{
    "memo": "e3dcff295c3782f2465033314eee2342535b0a792a61ebf41e0deeab5747a0e7",
    "projects": [
        {
            "name": "github.com/mattn/go-runewidth",
            "version": "v0.0.1",
            "revision": "d6bea18f789704b5f83375793155289da36a3c7f",
            "packages": [
                "."
            ]
        }
    ]
}

dep status を実行すると、go-runewidth の特定のバージョンで stick されているのが分かるかと思います。では試しに

PROJECT                        CONSTRAINT  VERSION  REVISION  LATEST  PKGS USED
github.com/mattn/go-runewidth  *           v0.0.1   d6bea18   v0.0.1  1  

試しに vendor ディレクトリを削除して、go-runewidth のリポジトリで別ブランチに切り替えておきます。そして再度 dep ensure を実行すると、ちゃんと stick したバージョンがよみがえる!

これや!ワイはこれが欲しかったんや!

今のところ、init と status と ensure と remove しかサブコマンドがありませんが、おそらく今後色々と増えてくるんじゃないかなーと思っています。チョー期待してます。

ちなみにいずれ go のサブコマンド(go dep)の様に取り込まれる可能性があるので慎重派の人はしばらくは遊ぶ程度にしておいた方が良さそう。


2017/01/19


go-bindata もいいけど、go-assets もいいよ。

Go でシングルバイナリな Web アプリを開発しているときに webpack --watch をうまいところやる - Diary

Go でシングルバイナリな Web アプリを開発しているときに webpack --watch をうまいところやる 個人的なアプリをつくるとき、だいたい以下のような環境で作業しています WAF は E...

http://diary.app.ssig33.com/166

みんなのGo言語にも書いた気がするのでそういうの興味ある人は買って下さい。

みんなのGo言語【現場で使える実践テクニック】 みんなのGo言語【現場で使える実践テクニック】
松木雅幸, mattn, 藤原俊一郎, 中島大一, 牧 大輔, 鈴木健太
技術評論社 / ¥ 2,138 (2016-09-09)
 
発送可能時間:在庫あり。

バイナリに assets を埋め込む際には go-bindata が有名ですが、実は go-assets も便利です。

GitHub - jessevdk/go-assets: Simple embedding of assets in go

README.md go-assets go-assets is a simple embedding asset generator and consumer library for go. The...

https://github.com/jessevdk/go-assets

何が便利かというと、go-assets はファイルシステムを持っている。go-bindata だと自前で Assets から取り出し ResponseWriter に書き込む必要がありますが go-assets だと

package main

import (
    "net/http"
)

//go:generate go-assets-builder -s="/data" -o bindata.go data

func main() {
    http.Handle("/", http.FileServer(Assets))
    http.ListenAndServe(":3000"nil)
}

こんだけでいい。go-assets を使うには go-assets-builder を使う。

GitHub - jessevdk/go-assets-builder: Simple assets builder program for go-assets

Usage: go-assets-builder [OPTIONS] FILES... Help Options: -h, --help= Show this help message Applica...

https://github.com/jessevdk/go-assets-builder

上記のコードに go:generate が書いてあるので data の中に assets を放り込んで go generate とすると bindata.go が出来上がる。


2017/01/12


この記事には幾らか正しくない部分がありました。後で訂正していきますが、ひとまず shogo82148 さんの解説記事も確認下さい。

http.Client はリクエスト毎に名前を引くので連続したアクセスはあまり速くない。

Go 1.8 からは Resolver が提供されるので、自前で簡単に名前引きのキャッシュを実装出来る。

Go 1.9 だった様です。

Go 1.8 Release Notes - The Go Programming Language

DRAFT RELEASE NOTES - Introduction to Go 1.8 Go 1.8 is not yet released. These are work-in-progress ...

https://beta.golang.org/doc/go1.8#more_context

ただそれまで待てないという人には nett がオススメ。

GitHub - abursavich/nett: Package nett steals from the standard library's net package and provides a dialer with a pluggable host resolver.

Package nett steals from the standard library's net package and provides a dialer with a pluggable host resolver.

https://github.com/abursavich/nett

これを http.ClientDialer として設定すれば、指定期間内の名前引きをキャッシュできる。

dialer := &nett.Dialer{
    // Cache successful DNS lookups for five minutes
    // using DefaultResolver to fill the cache.
    Resolver: &nett.CacheResolver{TTL: 5 * time.Minute},
    // Concurrently dial an IPv4 and an IPv6 address and
    // return the connection that is established first.
    IPFilter: nett.DualStack,
    // Give up after ten seconds including DNS resolution.
    Timeout: 10 * time.Second,
}
client := &http.Client{
    Transport: &http.Transport{
        // Use the Dialer.
        Dial: dialer.Dial,
    },
}
urls := []string{
    "https://www.google.com/search?q=golang",
    "https://www.google.com/search?q=godoc",
    "https://www.google.com/search?q=golang-nuts",
}
for _, url := range urls {
    resp, err := client.Get(url)
    if err != nil {
        panic(err)
    }
    io.Copy(ioutil.Discard, resp.Body)
    resp.Body.Close()
}

※ README から引用

実際にどれくらいパフォーマンスが出るかベンチマークを取ってみました。

package main

import (
    "io"
    "io/ioutil"
    "net/http"
    "testing"
    "time"

    "github.com/abursavich/nett"
)

var (
    urls = []string{
        "https://www.google.com/search?q=golang",
        "https://www.google.com/search?q=godoc",
        "https://www.google.com/search?q=golang-nuts",
    }
)

func BenchmarkNet(b *testing.B) {
    b.ResetTimer()
    client := http.DefaultClient
    for _, url := range urls {
        resp, err := client.Get(url)
        if err != nil {
            panic(err)
        }
        io.Copy(ioutil.Discard, resp.Body)
        resp.Body.Close()
    }
}

func BenchmarkNett(b *testing.B) {
    b.ResetTimer()
    dialer := &nett.Dialer{
        Resolver: &nett.CacheResolver{TTL: 5 * time.Minute},
        IPFilter: nett.DualStack,
        Timeout:  10 * time.Second,
    }
    client := &http.Client{
        Transport: &http.Transport{
            Dial:  dialer.Dial,
            Proxy: http.ProxyFromEnvironment,
        },
    }
    for _, url := range urls {
        resp, err := client.Get(url)
        if err != nil {
            panic(err)
        }
        io.Copy(ioutil.Discard, resp.Body)
        resp.Body.Close()
    }
}

URL 3つしかアクセスしていませんが、如実に結果が出ています。(およそ2倍)

BenchmarkNet-4                 1        1053105300 ns/op
BenchmarkNett-4                2         506050600 ns/op
PASS
ok      _/C_/dev/go-sandbox/slowdns     3.268s

リクエスト毎に毎回設定していられないって人はデフォルトの http.DefaultTransport を書き換えてしまえばシステム全体がこの恩恵を得られます。

http.DefaultTransport.(*http.Transport).Dialer = &nett.Dialer{
    Resolver: &nett.CacheResolver{TTL: 5 * time.Minute},
    IPFilter: nett.DualStack,
    Timeout:  10 * time.Second,
}

追記

この後、他の環境でベンチマークを取り直してみましたが、結果が出ない事もある様です。

$ go test -count=3 -test.bench BenchmarkNetP
BenchmarkNetP-4                1        1116008300 ns/op
BenchmarkNetP-4                2         900440950 ns/op
BenchmarkNetP-4                2         890325100 ns/op
PASS
ok      github.com/mattn/go-sandbox/nett        6.844s

$ go test -count=3 -test.bench BenchmarkNettP
BenchmarkNettP-4               2         952601050 ns/op
BenchmarkNettP-4               2         960558450 ns/op
BenchmarkNettP-4               1        1010136400 ns/op
PASS
ok      github.com/mattn/go-sandbox/nett        6.925s

あまりおすすめできませんね。すみません。。。