Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
36
Help us understand the problem. What is going on with this article?
@shibukawa

Goの準々標準ライブラリの探索

フューチャーアドベントカレンダー21日目のエントリーです。全25話中、神回が25回あったウルトラマンZが終わってしまいましたね。全日本人のうちの1億2000万人ほどがウルトラマンZロスでうちひしがれているころかと思います。

Goでは、Goをインストールしたときに一緒にインストールされるライブラリを「標準ライブラリ」と呼びます。また、golang.org/xなパッケージ群は準標準ライブラリと呼ばれます。準標準ライブラリには多彩なライブラリが含まれます。今ではもう本家に入ってしまって、後方互換性のためだけに残っているパッケージも一部ありますが・・・

これ以外にも https://github.com/golang/ にはたくさんパッケージがあります。一部はgolang/xのものも入っているのですが、それ以外のものをここでは仮に準々標準ライブラリと呼ぶことにします。

golang.org/xのものは、だいたいリポジトリの説明に[mirror]と入っているので、それを見れば識別しやすいかと思います。

スクリーンショット 2020-12-18 22.42.20.png

本エントリーではおすすめの準々標準ライブラリを紹介するとともにそのコードを読んでみます。ちなみに、go.chromium.orgも面白そうなライブラリがいっぱいあって、これのerrorsとかloggingとかモック可能なmath/randとか、標準ライブラリをとことん強くしてみた感が興味深いのですが、キリがないのでやめておきます。

あとは、pkg.go.devのコードのリポジトリにも、TeeProxyとか面白げなものがいろいろあったんですが、internalなのでそのままimportできずにコピーせざるを得ないという。やはりinternalは悪い文明!

github.com/golang/groupcache/consistenthash

github.com/golang/groupcacheはmemcachedのリプレースを狙った、分散キャッシュシステムとのこと。単独のアプリケーションのサーバーと、クライアントライブラリの組み合わせです。で、これは、分散キャッシュでよく利用されるアルゴリズムのコンシステントハッシュです。ワクワクしますね。

引数のkeysは一部peersの方が分かりやすいのではないか、というか、入力が全部keyなのは何かの間違い感があります。これを利用しているコードをみる方が分かりやすいですね。

package main

import (
    "fmt"
    "github.com/golang/groupcache/consistenthash"
)

func main() {
    h := consistenthash.New(3, nil)
    h.Add("host1", "host2", "host3", "host4", "host5")
    self := "host1" // 自分
    peer := h.Get("content's key")
    if self == peer {
        fmt.Println("このファイルはこのノードにあります。")
    } else {
        fmt.Printf("このファイルは%sにあります\n", peer)
    }
}

これを利用している親パッケージのコードをみるだけだと、ノードが落ちた時の復旧とかどうするのか、そのあたりはよくわからなかったです。1つでも落ちたらイミュータブルにクラスタ丸ごと再起動とかやっているんですかね? デフォルトのレプリカ数50というのが強い。斜め読みしかしてないですが、これを紹介するスライドがありました。

github.com/golang/groupcache/lru

上記のキャッシュサーバー内部で使われているLRUキャッシュです。hashicorpの方が強そうですが、まあシンプルで良いですね。わかりやすいです。コードリーディングにもおすすめ。

package main

import (
    "fmt"
    "github.com/golang/groupcache/lru"
)

func main() {
    c := lru.New(10) // 0だと無限
    // 計算結果をキャッシュ
    c.Add(2, 2 * 2)
    c.Add(3, 3 * 3)
    c.Add(4, 4 * 4)
    result, found := c.Get(4)
    fmt.Println(result, found)
}

github.com/golang/gddo/httputil

Webサービスであるgodoc.orgのリポジトリの中のパッケージです。リアルワールドなアプリケーション向けに作られた生々しいライブラリです。これが紹介したくてこのエントリーを書いたようなものです。サーバーとクライアントの間のコンテンツネゴシエーションを行う関数とか、あるいはバッファリングを行うhttp.ResponseWriterなどがあります。

コンテンツネゴシエーションは、ブラウザが欲しい(理解可能)なフォーマットと、サーバーが提供可能なフォーマットの仲介をする仕組みです。もちろん、みなさん、Real World HTTPですでに学習済みですよね。

きちんとq値を見ての選択もしてくれます。最近は画像フォーマットがいろいろ増えてきていています。お互いにより効率の良い形式で返したい、みたいなことが簡単に実現できます。標準ライブラリのhttputilに入れて欲しい。他にもNegotiateContentEncodingもあります。

package main

import (
    "fmt"
    "net/http/httptest"

    "github.com/golang/gddo/httputil"
)

func main() {
    req := httptest.NewRequest("GET", "http://localhost:8080/item", nil)
    req.Header.Set("Accept", "image/avif, image/webp;q=0.9, */*;q=0.8")

    t := httputil.NegotiateContentType(req, []string{"image/avif", "mage/heif", "image/heic", "image/webp", "image/png"}, "image/png")
    fmt.Printf("acceptable content-type: %s\n", t)
}

StaticServerは静的ファイルを返すサーバーです。標準ライブラリにもServeFileあるじゃんと思われた方もいると思いますが、こちらはより積極的なキャッシュ戦略が選択できます。

余談ですが、標準ライブラリのhttp.ServeFileには脆弱性があるんですね。知りませんでした。ServeFileのドキュメントにも、将来的には..はリジェクトされるよ、と書いてありますね。

こちらのStaticServerはディレクトリごと公開もできますし、特定のファイル(単数or複数)のみの公開もできるAPIも別に用意されています。ホワイトリストでできるのは安心ですね。

ResponseBufferは、http.ResponseWriterを満たす構造体です。ミドルウェアでwをこいつに差し替えて、レスポンスの内容をログに出力する、みたいなことが簡単にできますね。

CacheBustersはその名の通り、コンテンツが変わった時にキャッシュを使わせないようにクエリーパラメータを付与するライブラリです。ETagとかLast-Modifiedヘッダーを元に生成します。

package main

import (
    "fmt"
    "net/http"

    "github.com/golang/gddo/httputil"
)

func main() {
    cbs := &httputil.CacheBusters{Handler: http.FileServer(http.Dir("."))}
    path := cbs.AppendQueryParam("main.go", "cache")
    fmt.Printf("path: %s\n", path)
    // path: main.go?cache=Fri-18Dec202022-45-40GMT
}

github.com/golang/gddo/log

ロガーをcontext.Contextに差し込むミドルウェアです。contextの使用例として無駄がない。

その他の公式サービスが使っている気になったサードパーティライブラリ/ツール

公式が使っているのだからきっと質が高いものも多かろう、ということで。自分が知らなかったものをピックアップ。

github.com/client9/misspell/cmd/misspell

github.com/golang/blogやgithub.com/golang/pkgsiteで使われていたものです。名前からしてスペルチェックですよね。

sourcegraph.com/sourcegraph/go-template-lint

github.com/golang/blogやgithub.com/golang/pkgsiteで使われていたものです。テンプレートのLinterのようです。上のツールと同様、にGo製ウェブサイト用の汎用タスクのスクリプトで利用されていて、こちらも興味深いですね。

github.com/inconshreveable/log15

github.com/golang/gddo/logで使われていたライブラリです。シンプルな構造化ログとのこと。logfmt形式でデフォルトで出力されるが、JSONでも出力できるとのこと。

なお、github.com/golang/gddo/logはcontext.Contextにロガーを格納していました。context.Contextにはロガーなどは入れないみたいな説明が日本では見かけたことがありますが、公式がやっているんで、入れちゃってもいいってことですね。

github.com/yuin/goldmark

これも同じく、github.com/golang/websiteで使われていたMarkdownパーサーですね。

github.com/magiconair/properties

Javaのpropertiesファイルを読み込んで構造体にマッピングするライブラリ。エンタープライズな香りがする!

まとめ

最近、github.com/golangを覗いてみたらいろいろ見つけたのでその紹介でした。brotliをサポートしていたら、内部で持っているbrotli形式のデータをダイレクトに返すアセットバンドラー&http.FileServerもどき(インタフェースは満たしてないので別物)を実装したときに、httputil.NegotiateContentEncoding相当のものを雑ですが自前実装していたので、先に知っておけば!!!!!みたいに思った悔しさをエントリーにしました。

それ以外にも、有名どころだと github.com/golang/snappy とか、 github.com/golang/freetype とかありますが、そのあたりは今更説明するまでもないでしょう。

36
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
future
ITを武器とした課題解決型のコンサルティングサービスを提供します

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
36
Help us understand the problem. What is going on with this article?