Open17

実用 Go言語-システム開発の現場...読書メモ

ta.toshiota.toshio

「Goらしさ」に触れる

1.1 変数やパッケージ、メソッドなどに名前を付けるには

変数名

頭文字はUrlやIdなどはせずにURL, IDとするかurl, idのようにする、とのこと。

エラーの変数見栄は接尾辞Errorをつけるのが慣例とのこと。

パッケージ名

priority_queue のようなスネークケースや、 ArrayList のようなキャメルケースを用いたパッケージ名はGoらしくありません。複数の名詞で構成したくなった場合は、encoding/xml, encoding/jsonのようにフォルダを分ける、とのこと

インターフェース

接尾辞にerをつけるのが多い。

1.2 定数の使い方

1.2.1 型のない定数を定義する

1.2.2 型付きconst変数を定義する

関数の返り値をconstキーワードで定数にできない。
constは構造体のインスタンス、マップ、スライスなどの複合型を扱うことはできない

1.2.3 他言語との違い

1.2.4 定数でerror型のインスタンスを提供する

1.2.2にあるように以下はコンパイルエラー

const (
	// New()関数の返り値は実行時まで決まらないのでconstにできない
	ErrDatabase = errors.New("Database Error")
)

1.3 iotaを用いて列挙型を実現する

1.3.5 文字列として出力可能にする

以下のライブラリを紹介
golang.org/x/tools/cmd/stringer
github.com/alvaroloes/enumer

1.4 Goのエラーを扱う

1.5 変数は短縮形式:=とvarのどちらを使うべきか

結論から言うと、使える場所ではどんどん短縮記法を使うべきです。

とのこと。

1.6 関数のオプション引数

1.6.1 別名の関数によるオプション引数

必要な数だけ初期化処理のバリエーションを増やす方法

os.OpenFile()に対するos.Open()とos.Create()、WithContextを紹介

1.6.2 構造体を利用したオプション引数

構造体を利用する方法です。http.Clientの初期化などで使われます。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/optionparams3/optionparams.go

1.6.3 ビルダーを利用したオプション引数

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/optionparams4/optionparams.go

1.6.4 Functional Optionパターンを使ったオプション引数

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/optionparams5/optionparams.go

1.6.5 どの実装方法を選択すべきか

おすすめはコード量の少ない構造体パターンをまず実装して提供することです

1.7 プログラムを制御する引数

1.7.1 コマンドライン引数

Go標準ライブラリflagパッケージの紹介
kingpin.v2を使ったサンプルを紹介

1.7.2 環境変数

1.8 メモリ起因のパフォーマンス低下を解消する

1.8.3 deferの落とし穴

Close()など、エラー処理のメソッドによってはエラーを返す可能性があるケースもあります。この場合、普通にdeferを呼ぶだけではそのエラーを取りこぼしてしまうため、無名関数で括ってそのエラーを名前付きの返り値に代入すると、呼び出し元に返すことができます。

func deferReturnSample(fname string) (err error) {
	var f *os.File
	f, err = os.Create(fname)
	if err != nil {
		return fmt.Errorf("ファイルオープンのエラー %w", err)
	}
	defer func() {
		// Closeのエラーを拾って名前付き返り値に代入
		// すでにerrに別のものが入る可能性があるときは
		// さらに要注意
		err = f.Close()
	}()
	io.WriteString(f, "deferのエラーを拾うサンプル")
	return
}

1.9 文字列の結合方法

大量に文字列を結合するときは、stringsパッケージのstrings.Builderを使うのが良いでしょう

1.10 日時の取り扱い

1.10.1 日時のtime.Timeの取得

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/time.go

1.10.2 時間を表すtime.Duration

time.Durationには、いくつかの作成方法があります。
time.Time同士の差をSub()メソッドで計算
time.Secondなどの既存の時間のインスタンスの積により作成

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/time.go#L40

1.10.3 ウェブフロントエンドとのデータの交換

GoではFormat()メソッドを使って時刻を文字列にし、time.Parse()やtime.ParseInLocation()で文字列からtime.Timeインスタンスを作成します。

既存のフォーマットがいくつか用意されています。timeパッケージの定数として定義されているtime.RFC822Zやtime.RFC3339などが利用可能です。
どれを使うかは悩みどころですが、JavaScriptとのデータ交換を考えると、time.RFC3339Nanoを使うのが良いでしょう

1.10.4 翌月を計算するときのはまりどころ

2021年5月31日の翌月を取得するために AddDate(0, 1, 0) とすると翌月の2021年6月とはなりません。正規化されて2021年7月となります。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch01/adddate.go

ta.toshiota.toshio

2 定義型

2.1 型を定義してデータの不整合を防止する

2.2 既存のデータ型を拡張する

  • メソッドを追加して組み込み型を拡張する
  • 拡張した組み込み型で、元となる基底型の挙動を利用する
  • ファクトリー関数を用意して定義した型を使いやすくする

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch02/extend/main.go

2.3 定義型を作成してアプリケーションドメインに対応する

2.3.1 スライスへの型定義

Goではスライスに対しても定義型を作成できます。

これにより、従来スライスで取得していたデータベースなどへの問い合わせ結果を、レシーバー側に移譲できます。


type Consumers []Consumer

func (c Consumers) ActiveConsumer() Consumers {
    resp := make([]Consumer, 0, len(c))
    for _, v := range c {
        if v.ActiveFlg {
            resp = append(resp, v)
        }
    }
    return resp
}

gets, err := GetConsumers(ctx, key)
activeConsumers := gets.ActiveConsumer()

// 契約が有効で、1ヶ月後に契約が切れる予定で、昇順にソートし、ユーザーを取得
consumers := gets.ActiveConsumer().Expires(time.Now().AddDate(0, 1, 0)).SortByExpiredAt()

2.3.2 値への型定義

https://go.dev/play/p/M743oUEs26F

2.3.3 列挙への型定義

https://go.dev/play/p/sUpjSIrv4Gi

2.3.4 構造体への型定義

引数としている構造体をなるべくレシーバーにすることを検討しよう、という紹介

メソッドにするかどうかは自分の値をつかっているかどうか、という判断をしたらいいよ、というアドバイスをどこかで見た

https://go.dev/play/p/HNZezjlmhDF

2.4 型の変換

Goでキャストに分類される機能には2種類あります。

型変換(type conversion)
型アサーション(type assertion)

型アサーションはインタフェースからのダウンキャストに使います。ダウンキャストというのは、抽象的な型(インタフェース)から具象型への変換です。抽象的な型に入っている実態によって成功したりしなかったりするもので、動的な実行時の型の確認が必要な機能です。これには複数への型とのマッチングを一度に行う型スイッチ(type switch)も含まれます。

2.4.1 型変換(type conversion)によって型をキャストする

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch02/cast/main.go

2.5 機密情報を扱うフィールドを定義して出力書式をカスタマイズする

Goの独自型を利用することで機密情報を含む項目を扱いやすくする方法を紹介します。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch02/confidential/confidential.go

ta.toshiota.toshio

3 構造体

3.1 構造体の基本的な使い方

構造体名の後ろに、フィールドと値のペアが書かれた波かっこを書くと(これを複合リテラル、Composite Literalと呼びます)、メモリが確保されてインスタンスが生成されます。

使い捨て構造体

func main() {
	jst, _ := time.LoadLocation("Asia/Tokyo")
	book := struct {
		Title      string
		Publisher  string
		ISBN       string
		ReleasedAt time.Time
	}{
		Title:      "Some book",
		Publisher:  "オライリー・ジャパン",
		ISBN:       "123456",
		ReleasedAt: time.Date(2017, time.June, 14, 0, 0, 0, 0, jst),
	}

	fmt.Println(book)
}

Goの構造体の裏側ではポインター(値が格納されているメモリのアドレス)が使われています。ピリオドを使った構造体のメンバーアクセスではデリファレンス(ポインターから値の取り出し)を自動でおこなう

https://go.dev/play/p/l4NV4BH0aFQ

3.2 構造体をインスタンス化する3つの方法

	// newで作成(あまり使わない)
	p1 := new(Person)

	// var変数宣言で作成
	var p2 Person

	// 複合リテラルで初期化
	p3 := &Person{
		FirstName: "三成",
		LastName:  "石田",
	}

⭐ それぞれの初期値

3.2.1 ファクトリー関数

Effective Goではコンストラクタと呼ばれています

次の関数のように「New」を最初に付けた名前にするのが一般的です。

⭐メリット

// NewPerson ファクトリー関数
func NewPerson(first, last string) *Person {
	return &Person{
		FirstName: first,
		LastName:  last,
	}
}

3.3 構造体にメソッドを定義する

3.3.1 値レシーバーとポインターレシーバーのどちらを使えば良いか

3.3.2 レシーバーはnilでもメソッドは呼べる

3.3.3 インスタンスからメソッドを取り出して関数型として使う

func register(h *Handler) http.Handler {
	mux := http.NewServeMux()
	mux.HandleFunc("/value", h.Get) // ここのこと
	return mux
}

3.3.4 クロージャを使ってメソッドを再現する

3.3.5 ジェネリクスとメソッド

メソッドではジェネリクス(型パラメータ)は使えない

参考
https://gihyo.jp/article/2023/05/tukinami-go-07

3.4 構造体の埋め込みで共通部分を使いまわす

type OreillyBook struct {
	Book
	ISBN13 string
}

ob := OreillyBook{
	ISBN13: "9784873119038",
	Book: Book{
		Title: "Real World HTTP",
	},
}

https://go.dev/play/p/ckBIBsTApHY

3.5 タグを使って構造体にメタデータを埋め込む

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/structtag/main.go

3.6 構造体を設計するポイント

3.6.1 ポインター型として扱う必要があるケース

内部にスライスやmap、ポインターなど参照型の要素を持っている場合には、基本的にポインター型でのみ扱う構造体にします。

コピーしてもフィールドのポインタを共有していることになるので、影響範囲が大きいため。

構造体をポインター型として扱う場合、フィールドの値を取り出してコピーするという組み込みの文法では問題がおきるため、コピーが必要な場合は明示的なCopy()メソッドを用意すべきです。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/copyguard/copyguard.go#L42

3.6.2 値として扱える場合

ポインター型として扱う必要があるケースの逆になりますが、値として扱う場合には、ポインターやmap、スライスなどをその構造体のメンバーにはできません。文法上エラーにはなりませんが、わかりにくいバグを誘発します。

値として扱える構造体はポインターで扱っても問題ありません。ポインターにnilを入れることで無効な値であることが表現できます。値でも「IsZero()」メソッドを用意することで、同じように表現することもあります。

3.6.3 ミュータブルな構造体とイミュータブルな構造体

Goの場合エンティティと呼ばれるような構造体はミュータブルにするのが良いでしょう。関数型にかぶれると全部イミュータブルにしたくなりますが、time.Timeのようにほぼプリミティブのようなデータやバリューオブジェクトに限って利用した方が、Goの標準ライブラリなどのエコシステムと粒度を合わせやすいと思います。

3.6.4 ゼロ値の動作を保証するかどうか

3.6.5 実装方法を選択するポイント

3.7 空の構造体を使ってゴルーチン間での通知を行う

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/emptystruct/main.go

3.8 構造体のメモリ割り当てを高速化する

sync.Poolの紹介

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/allocate2/main.go#L34

3.9 構造体とオブジェクト指向の違いを知る

3.9.1 構造体の用途

3.9.2 構造体の埋め込みは継承ではない

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/oopng/main.go

3.9.3 テンプレートメソッドパターンではなく、ストラテジーパターン

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch03/oop/main.go#L51

3.9.4 あえてオーバーライドを実装する

https://go.dev/play/p/FRG5pLfaF0W

ta.toshiota.toshio

4 インタフェース

4.1 柔軟なコードを書くインタフェースの利用法

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch04/interfacesyntax/main.go

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch04/interfacebasic/main.go
(NormalizeFileのNormalizeの引数逆な気が)

ジェネリクスとインターフェース

func Stringify[T Stringer](s []T) (ret []string) {
    for _, v := range s {
        ret = append(ret, v.String())
    }
    return ret
}

4.2 あらゆる型のデータを格納するany

any型の変数は、そのままの状態では利用できません。必ず型のキャストが必要です。

4.3 インタフェースのキャスト

4.3.1 型アサーション・型スイッチの基本の書き方

型アサーション

	// ctx.Value()はinterface{]なので変換が必要
	// 必ず、okで成功可否を確認すること
	if s, ok := ctx.Value("favorite").(string); ok {
		// sはstring型
		log.Printf("私の好きなものは%sです\n", s)
	}

型スイッチ

	// 同じvだが、case節ごとにvの型が変わる
	switch v := ctx.Value("favorite").(type) {
	case string:
		log.Printf("好きなものは: %s\n", v)
	case int:
		log.Printf("好きな数値は: %d\n", v)
	case complex128:
		log.Printf("好きな複素数は: %f\n", v)
	default: // どれにもマッチしない場合
		log.Printf("好きなものは: %v\n", v)
	}

4.3.2 他のメソッドを持っているかの問い合わせ

次のコードは、渡されたオブジェクトがio.Closerインタフェースを満たしていて、満たしている場合にはClose()メソッドを呼び出すコードです。

	if c, ok := r.(io.Closer); ok {
		c.Close()
	}

インターフェース定義を直接書くこともできるとのこと

if x, ok := err.(interface{ Is(error) bool }); ok && x.Is(target) {
    return true
}

4.3.3 スライスのキャスト

一括での変換はできない。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch04/interfacecast/main.go#L66

スライスの場合、要素ごとスライスに入れ直す必要がある。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch04/interfacecast/main.go#L46

4.4 インタフェースの合成

type ReadWriter interface {
    Reader
    Writer
}

この場合はio.Readerが満たすべきインタフェース(Read()メソッド)と、io.Writerが満たすべきインタフェース(Write()メソッド)の両方を満たさなければならないインタフェースになります。

4.5 実装を切り替えるためのさまざまな方法

if、インターフェース、DIを紹介

あらかじめ、テストを考えてすべてモック化できるようにインタフェースにしておく、という考えをしているGoユーザーは少数派です。

へー

ta.toshiota.toshio

5 エラーハンドリング

5.1 エラーの書き方

errors.New

var EOF = errors.New("EOF")

fmt.Errorf

fmt.Errorf("length must be greater than 0, lentgh = %d", length)

独自のエラー

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/writing/custom/main.go

生成するときなぜポインターである必要がある?

エラーのラップ、アンラップ

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/writing/wrap/main.go

上記の loadConfigError のような他の詳細なエラーに対して情報を付与して抽象度の高いエラーを扱う構造体などを作りたい場合には、 Unwrap() メソッドを用意するのが、エラー実装者のお作法になります。

エラーの色々な比較

errors.Is(): エラーを値として比較する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/writing/is/main.go

独自にfmt.Errorf(... %w", err)で返してもsql.ErrNoRowsになるの?
%wがwrapすることになるから?

errors.As(): エラーを型として比較する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/writing/wrap/main.go#L49

エラーを文字列として比較する

比較したいエラーが外部パッケージに非公開のエラーとして宣言されている場合があります。非公開のエラーの場合、アプリケーションから利用することができず、やむを得ずエラーを文字列として比較することがあります。

スタックトレースをどう出すのか

golang.org/x/xerrors を使うのが良いでしょう。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/writing/xerrors-sample/main.go#L11

5.2 エラーハンドリングの基本テクニック

5.2.1 呼び出し元に関数の引数などの情報を付与してエラーを返す

上ではなく、下のようにしましょうという紹介。

	user, err := getInvitedUserWithEmail(ctx, email)
	if err != nil {
		// 呼び出し先で発生したエラーをそのまま呼び出し元に返却
		return err
	}
	user, err := getInvitedUserWithEmail(ctx, email)
	if err != nil {
		// 呼び出し先で発生したエラーをラップし、付加情報を付与して呼び出し元に返却
		return fmt.Errorf("fail to get invited user with email(%s): %w", email, err)
	}

5.2.2 ログを出力して処理を継続する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/practice/logcontinue/main.go#L32-L43

5.2.3 リトライを実施する

Songmu/retry を使用したリトライの紹介

5.2.4 リソースをクローズする

return err しても後片付けが必要な場合は defer で後処理しましょうね、という紹介。

log.Fatal() などを呼び出すとプログラムが即座に終了するため defer でリソースがクローズされません。関数の実装の中で log.Fatal() が登場したら8、9割は間違いなので、コードレビューでしっかりと指摘しましょう。

へー

5.2.5 複数のエラーをまとめる方法

https://go.dev/blog/errors-are-values の紹介

func (ew *errWriter) write(buf []byte) {
    if ew.err != nil {
        return
    }
    _, ew.err = ew.w.Write(buf)
}

ew := &errWriter{w: fd}
ew.write(p0[a:b])
ew.write(p1[c:d])
ew.write(p2[e:f])
// and so on
if ew.err != nil {
    return ew.err
}

go.uber.org/multierr の紹介

複数回エラーが発生した場合は何回目のエラーでどのようなエラーが発生したかわかります

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch05/other/merr/main.go#L14-L26

5.3 エラーのチェック忘れを予防する

5.3.1 kisielk/errcheckの利用

エラーのチェック忘れをLinterで検知する紹介

5.3.2 %w利用箇所の制限

fmt.Printf() や fmt.Sprintf()では%wは使用できない
go vet でエラーが検知できる、とのこと

ta.toshiota.toshio

6 パッケージ、モジュール

6.1 プロジェクト構成の事前知識

パッケージの命名規則は変数などと同じで、英数字とアンダースコアを使います。ただし先頭はアルファベットです。

慣習として、パッケージ名とフォルダ名と同一にすることが多いですが、変えることもあります。たとえばSQLite用パッケージのフォルダ名(リポジトリ名)はgo-sqlite3ですが、パッケージ名はsqlite3です

6.1.2 パッケージのimportが循環してはいけない

6.1.3 親パッケージと子パッケージは独立したパッケージ

6.1.4 モジュール

go mod

6.1.5 セマンティックバージョニング

6.1.6 相対パスでのimportはできない

6.1.7 internal

6.1.8 無視されるフォルダ

.と_で始まる名前と、testdataと言う名前のフォルダは、Goの処理系がコンパイル対象から除外します。たとえば静的解析ツールなどを実装していて、goのコードをテストデータとして置いておきたい場合や、テストの失敗ケースに用いる処理系でコンパイルエラーになるコードなどは、testdataフォルダ以下に置きます。

6.1.9 go gettable

6.1.10 GOPATH

「GOPATHは過去のもの」、と考えて問題ありません。

GOPATHを設定したりもされていましたが、Go Modulesが導入されてからはそのような必要はなくなりました。

6.2 Go Modulesで開発環境のパッケージを管理する

6.2.1 Go Modulesの概要

6.2.1.1 モジュールへのファイルの追加

go get

$ go get -u github.com/rs/zerolog/log

-u オプションを付けると取得しようとしているライブラリ(この場合は github.com/rs/zerolog )が依存しているライブラリについても最新のバージョンを取得できます。

更新する場合は go mod tidy

6.2.1.2 ライブラリの削除/更新

go のソースコードで不要になったライブラリ( import に現れなくなったライブラリ)の依存関係は、 go mod tidy を実行することで go.mod と go.sum から削除されます。

同様にソースコードで import して使っているけれど go.mod や go.sum には追加されていない依存関係がある場合は追加されます。

6.3 Goプロジェクトのライフサイクル

6.4 Goプロジェクトのモジュール内のパッケージ構成

1つの参考として

https://github.com/golang-standards/project-layout/

6.4.1 最低限のパッケージ構成

Goのパッケージングにおける最低限のルールは次の2つです。
同一フォルダ内のファイルは同一パッケージとなる
実行ファイルのエントリーポイントはmainパッケージ1となる

6.4.2 パッケージを階層化する

6.4.3 ドメイン/レイヤー vs レイヤー/ドメイン

6.4.4 開発時に使うツールの追加

ソフトウェアのビルドには不要なものの、コード生成にGo製のツールを使うことがあります。特に、go generateで使うようなstringerやgithub.com/alvaroloes/enumer、github.com/Songmu/gocreditsなどがあります。これらのツールはgo installコマンドでインストールできます。

この方法でインストールすると、go.mod に影響を与えることなく、実行バイナリをインストールできます。
実行バイナリのインストール先は環境変数 GOBIN で指定されたディレクトリ、または GOPATH/bin または HOME/go/bin となります。

6.5 Go Modulesの実践的な使い方

6.5.1 1リポジトリ、マルチモジュール構成

内部モジュールを見るための記述

module github.com/myorg/app/entrypoint

require github.com/myorg/app/storage v0.0.0

replace github.com/myorg/app/storage => ../storage

6.5.2 プライベートリポジトリのモジュールを参照する方法

GOSUMDB を off にする代わりに、環境変数 GOPRIVATE を用いて go のコマンドにプライベートなモジュールだと認識させる方法があります。

GitHubのプライベートリポジトリに直接アクセスしてモジュールをダウンロードするには git config でGitHubのアクセストークンを設定しておく必要があります。

git config --global url."https://${access_token}:x-oauth-basic@github.com/oreilly-japan/".insteadOf "https://github.com/oreilly-japan/"

6.5.3 フォークしたモジュールを参照する方法

module forkedsample

go 1.17

require github.com/rs/zerolog v1.26.1

replace github.com/rs/zerolog => github.com/myname/zerolog v1.26.1

6.5.4 モジュールのキャッシュ

GOMODCACHE

6.5.5 依存するモジュールの可用性の考慮

6.6 静的なプラグイン機構を実現する

よく意義が分からなかった

6.7 初期化の順序を制御する

6.7.1 パッケージの読み込み順

6.7.2 パッケージ内部の初期化の順序

上から順々に実行ではなく、

Goは利用順序を考慮して並び替えてから実行する

なお、パッケージレベルの変数を初期化する際に関数呼び出しなどを使うと、暗黙的にinit()関数が作られ、その中で初期化処理が実行されます。

ta.toshiota.toshio

8 さまざまなデータフォーマット

8.1 JSONファイルを扱う

エンコードとは一般に、データをある規則に基づいて別の形式に変換することを指します。逆に変換された形式のデータを元に戻すことをデコードと言います。

Javaの文化だと、Javaのオブジェクトからバイト列に変換して出力することをシリアライズと呼び、シリアライズされた入力をJavaオブジェクトに復元することをデシリアライズと呼びます。

デコード

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/decodesample/a/main.go#L24

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/decodesample/b/main.go#L18

json.Unmarshalとjson.NewDecoderの使いわけ

io.Reader インタフェースを満たしている型のデータを元にデコードできます。io.Reader インタフェースを満たしているストリーミングなJSONデータを扱う場合は Decoder() メソッド 、そうではなく []byte 型を扱う場合は json.Unmarshal() 関数を使う、とおぼえておけば良いでしょう。

エンコード

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/encodesample/c/main.go#L21

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/encodesample/d/main.go#L14

/ "-" とすることでエンコードの対象から除いておくことができる
X func() json:"-"

JSONからGoの構造体を自動作成

https://mholt.github.io/json-to-go/

GoLandにもJSONをエディタに貼り付けることで構造体を自動生成してくれる便利な機能があります。
https://www.jetbrains.com/help/go/working-with-json.html

omitempty: ゼロ値の場合にJSONのフィールドに項目を表示させないようにする

ゼロ値と区別して omitempty する -> ポインター型を使う

デコード時に未知のフィールドがある場合にエラーにする

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/strictdecode/main.go#L14

UnmarshalJSONやMarshalJSONを使ったJSONエンコード/デコードの拡張

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/munm/m/main.go#L16-L25

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/munm/unm/main.go#L16-L32

フィールドの値によってJSONの中身が動的に変化する場合のテクニック

動的に決まるフィールドは json.RawMessage 型としてデコードせずにバイト列のまま保持しておきます

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/json/variable/main.go#L14

8.2 CSVファイルを扱う

Comma-Separated Valuesという意味でのCSVには、RFC 4180(Common Format and MIME Type for Comma-Separated Values (CSV) Files)と呼ばれる仕様があります。 encoding/csv はそれにしたがって実装されています。

8.2.1 CSV形式のファイルを読み込む

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/read.go#L18

8.2.2 CSV形式でファイルに書き込む

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/write.go#L24

8.2.3 BOM付きファイルの扱い

BOM(ボム)とはByte Order Markの略で、通常はテキストの先頭につける数バイトのデータです。BOMを元にどのように符号化されたUnicodeかを判定します。

r := csv.NewReader(bom.NewReader(f)) // BOMの回避

8.2.4 Shift-JIS(Windows-31J)を扱うには

r := csv.NewReader(transform.NewReader(f, japanese.ShiftJIS.NewDecoder()))

8.2.5 コメントアウトされた行をスキップしたい

r := csv.NewReader(f)
r.Comment = '#' // # で始まる行をコメントとみなし、取り込みをスキップ

8.2.6 CSV行を構造体で表現する

gocsv

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/gocsv/read/gocsvread.go#L26

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/gocsv/write/gocsvwrite.go#L29

8.2.7 encoding/csvの設定をgocarina/gocsvに引き継ぐ

8.2.8 CSVのエンコード/デコードを拡張する

8.2.9 巨大なCSVファイルを扱いたい場合(逐次処理で書き込みたい場合)

read

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/channel/read/chanread.go#L23-L39

write

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/channel/write/chanwrite.go#L17-L31

8.2.10 マルチレイアウトCSVを処理したい

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/csv/multilayout/multilayout.go

8.3 Microsoft Excelファイルを扱う

8.3.1 Excelファイルに対する書き込み・読み込み

8.3.2 複数レコードを書き込む方法

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/excel/multirows/main.go

8.3.3 Excelファイルを読み取る

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/excel/read/main.go#L29-L49

8.3.4 構造体へのマッピング付きで読み取る

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch08/excel/structmapper/main.go

8.4 固定長データを扱う

ianlopshire/go-fixedwidth

ta.toshiota.toshio

9 Goとリレーショナルデータベース

9.1 データベースの基本的な利用法

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/intro/user_fetch.go

9.2 トランザクションを扱うには

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/transaction/wrapper/main.go

9.3 コネクションプールのパラメーターをチューニングする

SetMaxOpenConns: 同時にデータベースへ接続できるコネクション数
SetMaxIdleConns: アイドルのコネクションとしてコネクションを保持する最大の数
SetConnMaxLifetime: 下記に説明
SetConnMaxIdleTime: コネクションがアイドル状態でいられる最大時間

新規にコネクションを確立されてからのタイムアウト時間です。アクティブなコネクションは SetConnMaxLifetime の時間が経過しても切断されるわけではありません。

いずれのパラメーターもアプリケーションのワークロードやデータベースの設定に合わせて値を決める必要があります。

9.4 クエリーをキャンセルする

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/cancel/main.go#L20-L21

9.5 アプリケーションでクエリーをロギングする

9.5.1 ドライバーを使ってクエリーをロギング

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/logging/pgxdriver/main.go

9.5.2 ラッパーのドライバーを使用してクエリーをロギング

フック(gchaincl/sqlhooks)を使った例を紹介

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/logging/hook/main.go

9.6 大量のデータをバッチインサート

9.6.1 プリペアードステートメントを使う

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/batchinsert/prepared/main.go

9.6.2 バッチインサートを使う

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/batchinsert/dynamic/main.go#L34-L49

9.6.3 データベース組み込みの関数を使う

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/batchinsert/copy/main.go#L27-L36

9.7 共通カラムをうまく扱うには

9.7.3 共通カラムを構造体の埋め込みで扱う

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/systemcolumns/declare/main.go

9.8 データベースアクセスをともなう実装のテスト

9.9 サードパーティーのライブラリを使ったあれこれ

9.9.2 スキーマドリブンでアプリケーションを開発

スキーマ情報からGoのコードを自動生成する方法

volatiletech/sqlboiler

クエリードリブンでアプリケーションを開発

kyleconroy/sqlc

9.9.4 クエリーのロギング

gorm.v2のロギングを紹介

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/logging/nocustom/main.go

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/logging/custom/main.go

他、アプリケーションロギング

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch09/logging/applog/main.go#L34

ta.toshiota.toshio

10 HTTPサーバー

10.2 ウェブプログラミングの基本

10.2.1 HTTPサーバーを実装する

GoのHTTPサーバーを知るには net/http パッケージの主要な型、インタフェースを押さえると見通しが良くなります。大きく以下の3つです。

Handler インタフェース
HandlerFunc 型
ServeMux 型

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

他参考

10.2.2 JSONデータの読み書き

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/intro/json/main.go

10.2.3 リクエストのバリデーション

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/intro/validation/main.go

10.2.4 必須チェックのハマりどころ

これを避けるためには、必須チェックを行いたいフィールドをポインター型で定義します。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/intro/requiredcheck/pointer/main.go#L13-L16

10.3 HTTPのリクエストのパース

10.3.1 クエリーのパース

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/parse_query/main.go#L17

10.3.2 ファイルのアップロードの処理

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/parse_query/main.go#L42

10.4 ルーター

10.4.1 標準ライブラリのhttp.ServeMux

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/router/main.go

10.4.2 サードパーティー製のルーター

go-chi/chiの紹介

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/router_chi/main.go

10.5 Middlewareパターンを使って処理を分離する

10.5.1 Middlewareの仕組み

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/middleware/logging/main.go#L14

10.5.2 ステータスコードのキャプチャ

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/middleware/transaction/main.go#L44-L50

10.5.3 ハンドラー内部でのpanicの防御

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/middleware/recover/main.go#L9-L20

10.5.4 DBのトランザクション制御

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/middleware/transaction/main.go

10.5.5 タイムアウト設定

	h := MiddlewareLogging(http.HandlerFunc(Healthz))
	http.Handle("/healthz", http.TimeoutHandler(h, 5, "request timeout"))
	http.ListenAndServe(":8888", nil)

10.5.6 レートリミット(速度制限)

1秒間に1回まで

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/ratelimit/normal/main.go#L21-L33

IP制限で回数制限

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/ratelimit/ipaddr/ipratelimiter.go#L9

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/ratelimit/ipaddr/main.go#L13

middleware実装参考

10.6 シングルページアプリケーションの静的ファイルを配信する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch10/spa/main.go#L23

10.7 APIドキュメントを生成する

ta.toshiota.toshio

11 HTTPクライアント

11.1 net/httpを使ったHTTPクライアントの基本

simple get - https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/intro/client/defaultclient/main.go

simple post - https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/intro/client/post/main.go

http.Client
https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/intro/client/makeclient/main.go

11.2 RoundTripperインタフェースによって処理を分離する

リトライなどのリクエストやレスポンスにおける細かい制御は、http.Client の http.RoundTripper インタフェースである Transport フィールドを使って行います。

net/http

type Client struct {
    Transport RoundTripper
    CheckRedirect func(req *Request, via []*Request) error
    Jar CookieJar
    Timeout time.Duration
}

type RoundTripper interface {
    RoundTrip(*Request) (*Response, error)
}

basicな拡張
https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/roundtrip/basic/main.go

11.2.1 RoundTripperでロギングする

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/roundtrip/logging/roundtrip.go

11.2.2 RoundTripperで認証認可

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/roundtrip/basicauth/roundtrip.go

11.2.3 RoundTripperでリトライ

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/roundtrip/retry/roundtrip.go

11.3 リトライ時に考慮するべき点

11.3.1 すべてをリトライしない

11.3.2 Exponential backoff

github.com/hashicorp/go-retryablehttpを使用した例

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/retry/goretryablehttp/client.go

11.3.3 Retry-Afterヘッダーによる待機時間

11.3.4 リトライするための待機処理にtime.Sleepを使わない

Context().Done() と time.After を select で待ち受けるのが良い作法です。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/roundtrip/retry/roundtrip.go#L49-L53

11.4 プロキシサーバーを突破する

11.4.1 net/httpのプロキシサーバー設定

net/http/proxy.go -> FromEnvironment
net/http/transport.go -> ProxyFromEnvironment

11.4.2 SSL証明書エラーがでる場合のプロキシサーバー対応

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch11/httpproxy/ssl.go

ta.toshiota.toshio

12 ログとオブザーバビリティ

12.1 ログをめぐる、出力の仕組みの変化

12.2 ログに出力すべき内容を決めるには

12.3 標準ライブラリでログを出力する

12.3.1 ユニットテストの中のログ出力

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/log_test.go

12.3.2 ログ出力のカスタマイズ

log.SetOutput()は出力先を設定します

	file, _ := os.Create("log.txt")
	log.SetOutput(io.MultiWriter(file, os.Stderr))
	log.Println("ファイルと標準エラー出力に同時に出力します")

SetFlags()とSetPrefix()関数を使うことで、ファイル名を付与したり、目立つような接頭辞を付与できます。

LstdFlags: 日時を出力します(デフォルト)
Lshortfile: ファイル名と行番号を出力します
Llongfile: ファイルのフルパス(ビルドオプションで-trimpath

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/stdlibflag.go

基本的に、logパッケージは開発の途中でのみ利用し、最終的にリポジトリにコミットする段階では削除してからコミットする方針が良いでしょう。情報の収集などに利用する、仕様書に記述するログ出力には構造化ログを使いましょう。

12.4 構造化ログを出力する

rs/zerologを紹介とのこと

12.4.1 zerologの基本とログレベル

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/zerolog.go#L34-L44

12.4.2 zerologの基本: さまざまな情報

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/zerolog.go#L47-L66

12.4.3 ログにエラーコードを埋め込む

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/zerolog_errcode.go#L9-L18

12.4.4 ウェブサービスのミドルウェアでログを出力する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/zerolog.go#L68-L211

12.5 エラーとログ出力

12.5.1 log.Fatal()とpanic()の違い

12.5.2 これらの関数で強制終了をしても良い場所

main()関数
init()関数
Must接頭辞がついた関数
その他のアプリケーションの初期化処理

12.6 net/httpのエラーログをカスタマイズする

zerolog
io.Writeを満たす

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/errlog/custom/z/main.go

zap
io.Writerを満たさない

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/errlog/custom/u/main.go

常にErrorレベルとして出力されるコード例?

12.7 分散システムの動作を確認するには

テレメトリーを行うソフトウェアとして、OSSのOpenTelemetry
OpenTelemetryは次の3分野から構成されています。
複数のシステムにまたがった処理を見える化する「分散トレース」
サービスの負荷や使用しているリソース量を把握する「メトリクス」
分散トレースと連動した、キーと値で複雑な情報も表現可能な「構造化ログ」

12.7.1 Instrumentation/Exporter

12.7.2 分散トレース

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch12/otel/main.go

ta.toshiota.toshio

13 テスト

13.1 Goのテストの書き方の基礎

13.2 Table Driven Testを実装する

TDTでは、まずはテストケースを作成します。主に以下の項目を定義します。
テスト名
関数の引数
関数の戻り値
関数がエラーを返しているかどうか

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/cookbooktest/calc_test.go

13.3 テストに事前事後の処理を追加する

テスト共通の実行前後
TestMain() 関数を利用します。 go test を実行した際、テストファイル内に TestMain() があればこれを経由して各テスト関数が順次 m.Run() で呼び出されます。ここに事前事後の処理を追加します

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/cookbooktest/beforeafter_test.go

13.4 ヘルパー関数

testutil や testonly といった名称でテストコードとは別パッケージを作成し、 _test.go が末尾に付かない通常のGoファイルでヘルパー関数を定義します。この時、テストコードからしかimportしなければ、プロダクションコードのビルドには含まれないため問題にはなりません。

あるパッケージの機能をモックするようなヘルパーの場合は、 パッケージ名test といった名称も良く見かけます。 httptest パッケージが代表的な例でしょう。

13.5 ウェブサーバーのハンドラーをテストする

13.5.1 サーバー全体のテスト

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/handlertest/handler_test.go#L11-L27

13.5.2 ハンドラー単体のテスト

httptestパッケージのNewRequest()とNewRecorder()関数を使い、リクエストとレスポンスのインスタンスを使い、テスト対象のハンドラーを直接起動

ミドルウェアの影響を避けて呼び出すなど、ユニットテストには最適です。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/handlertest/handler_test.go#L32-L44

13.6 テストの落とし穴の回避

13.6.2 テストの順序依存の排除

テストの順番をシャッフル
go test -shuffle=on

13.6.3 キャッシュの削除

-count=1を付与するとキャッシュがクリアされます
go test . -count=1

13.6.4 時間がかかるようになったテストへの対処

「 Model 」が含まれている関数名のテストのみが実行される
go test -run Model

go test -short
https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/skiptest/long_test.go#L8-L11

タイムアウト設定(デフォルト10分)
go test . -timeout 30m

並列実行
https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/parallel/calc_test.go#L87-L91

並列数を変更したい場合は -parallel オプション
これとは別に -p フラグが存在

-parallel は同一パッケージ内のテストに対して、 -p は複数パッケージ単位のテストに対しての並列数を指定するためのオプションです

13.7 testifyを使う

構造体の値は、構造体のすべてのフィールドが比較可能、つまり map 型や slice 型、 func 型の値を含んでいない場合に比較できます。

reflect.DeepEqual() を用いると、単純に等価演算子 == では比較できない複合型のデータも、中身をたどって判定できます

assert.Equalを使うと何が等しくないか視認しやすい、とのこと

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/testify-test/main_test.go

13.8 構造体の比較にgo-cmpを使う

13.8.1 go-cmpを使う理由

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/reason/d/prog_test.go#L21

13.8.2 go-cmpのTips

公開されていないフィールドの取り扱い

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/tips/b/prog_test.go#L19-L23

一方で公開されていないフィールドを比較対象から除くときは

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/tips/c/prog_test.go#L20-L24

構造体のあるフィールドを比較対象から除く

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/tips/d/prog_test.go#L13-L26

複数のオプションを指定する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/tips/multiopts/prog_test.go#L23-L30

go-cmp 側でソートして比較するオプション cmpopts.SortSlices()

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/gocmp/tips/f2/prog_test.go#L17-L27

その他

float64 型の差分を加味して、差分が一定の範囲内であれば等価とみなすような cmpopts.EquateApprox()

math.NaN() と math.NaN() の比較を等価とみなせるような cmpopts.EquateNaNs()

time に関する差分が一定の範囲内であれば等価とみなすような cmpopts.EquateApproxTime()

13.9 テストが書きにくいものをテストする

13.9.1 シンプルな入出力のテスト

コアとなるロジックはなるべく抽象化された入出力のインタフェース(io.Readerとio.Writer)に対して実装するようにしておくと、テストしやすくなります

の紹介

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/helper/simple_test.go#L28

13.9.2 変換が必要な入出力のテスト

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/helper/simple_test.go#L49

13.9.3 さらに複雑な入出力のテスト

13.9.4 時刻をともなうテスト

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/helper/simple_test.go#L90-L116

13.10 品質を保証するテストを実装する

13.10.1 ペアワイズ法を使ったテストパターンの考慮もれ防止

13.10.2 カバレッジ

go test -cover

-coverの代わりに-coverprofileオプションで出力し、 go tool cover でHTMLファイルを生成すれば、どの行が実行されていてどこが実行されていないかが一目瞭然です。

13.10.3 プロパティベーステスト

「入力は文字列である」「整数である」といった入力の特性を与えると、その特性にあった引数の組み合わせを自動的に作り出してエッジケースの問題を見つけてくれるのが「プロパティベーステスト」です。

leanovate/gopter

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/pbt/pbt_test.go

13.11 go testでベンチマークを取る

ベンチマークは、関数名を Benchmark ではじめて、引数に *testing.B を取ります。b.Nはベンチマーク結果が安定するまで自動的に増加する数値です。ベンチマークの試行回数を定義することなく、結果が安定するまで自動で複数回実行してくれます。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/cookbooktest/benchmark_test.go

go test -benchmem -bench Benchmark cookbooktest

ベンチマーク関数名
試行回数( op )
1回の実行にかかる時間( ns/op )
1回の実行で発生するメモリアロケーションサイズ( B/op )
1回の実行で発生するメモリアロケーション回数 ( allocs/op )

ループの前にテストデータのロードなど時間のかかる初期化処理を行う場合、ループの直前で b.ResetTimer() を呼び、経過時間とメモリのアロケーション回数をリセットすることで、ループ外の処理が結果に影響を与えないようにします。

b.ResetTimer()

13.12 ドキュメントに出力するExampleをコードに記述する

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch13/cookbooktest/example_test.go

ta.toshiota.toshio

14 クラウドとGo

14.1 コンテナの起動

https://github.com/oreilly-japan/practical-go-programming/tree/master/ch14/docker_build

14.2 コンテナ用イメージの作成

14.2.1 Dockerを使ったイメージのビルド

golang:1.x-bullseye と golang:1.x-bullseye-slim のように、ビルド用とデプロイ用でイメージの系統をそろえるのが無難です。また情報の多い、標準的なLinuxディストリビューションに基づくイメージの方が、トラブル発生時の問題は少なくなります。DebianとAlpineであれば、各ライブラリの実装など今までテストされてきた回数は圧倒的に前者の方が上でしょう。

latestを選択した結果、いつの間にかメジャーバージョンが変わってビルドに失敗するようになるという話も聞きます。OSのメジャーバージョンやGoのバージョンが勝手に変わると困るので、 1.17-bullseye あたりを選んでおくのが無難でしょう。Goの場合、3桁目のバージョンはセキュリティの修正が入ることが多いため、なるべくアップデートするのがおすすめです。ただし、完全なバージョン固定をしないのであれば、CIテストを設定すべきでしょう。

14.2.2 Cloud Native Buildpacksとkoによるイメージの作成

14.3 クラウドサービスにデプロイする

14.3.1 Amazon ECSへのデプロイ

copilot

$ copilot init --app ecssample --name helloapi \
    --type "Load Balanced Web Service" \
    --dockerfile "./Dockerfile" --deploy

https://github.com/oreilly-japan/practical-go-programming/tree/master/ch14/deploy

14.3.2 AWS LambdaでWebサーバーを動かす

$ CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -trimpath -o lambda ./sampleapp/cmd/main.go
$ zip -j lambda.zip lambda
$ aws lambda update-function-code --profile my_profile_dev --region ap-northeast-1 --function-name dev-example-api --zip-file fileb://lambda.zip

14.3.3 Cloud RunでWebサーバーを動かす

https://github.com/oreilly-japan/practical-go-programming/tree/master/ch14/cloudrun/simple

ta.toshiota.toshio

15 クラウドのストレージ

15.1 AWS S3

15.1.1 AWS SDK for Go v2

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch15/s3crud/main.go

# 15.1.2 Go CDK

Go CDK(Go Cloud Development Kit)は2018年のGoogle Cloud Nextで発表された、各クラウドサービスに対して統一的なコードでアクセスすることを目指し、Goのアプリケーションをクラウド間でポータブルにするためのライブラリです。これに類似するものとして database/sql パッケージがあるでしょう。RDBにもさまざまな製品が存在しますが、 database/sql で抽象化することで開発生産性や移植性を高めています。これをクラウドの世界で、オブジェクトストレージ、ドキュメントストア、Pub/Subなどに適用したのがGo CDKです。

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch15/gocdk/s3bucket/main.go

15.2 Amazon DynamoDB

https://github.com/oreilly-japan/practical-go-programming/tree/master/ch15/dynamodb

ta.toshiota.toshio

16 エンタープライズなGoアプリケーションと並行処理

16.1 並行処理の基本を知る

16.2 同時に動作するスレッドを1つに制限する

16.3 ゴルーチンプールを使って複数のタスクを実行

16.4 チャネルのブロッキングを中断する(何も起きていないことを検知する)

16.5 ゴルーチン間のイベント伝達

16.6 処理の分岐と待ち合わせをしたい(ファンアウト・ファンイン)

16.7 処理の待ち合わせをしたい(Future/Promiseパターン)

https://github.com/oreilly-japan/practical-go-programming/blob/master/ch16/future/main.go

https://go.dev/play/p/0k8H4BnwAio

16.8 ウェブサービスでセッションの情報を共有する