- 投稿日:2020-03-27T19:49:25+09:00
Golang 入門する
個人的なメモとして用いる。
GOの特徴
- コンパイラ言語
- 静的型付け言語
- 文法がシンプル
- クロスコンパイルが可能
- 標準パッケージが豊富
ソースコードの構成
main.go// パッケージの定義 package main // 標準パッケージや外部ライブラリのインポート import "fmt" // main関数の定義 func main { fmt.Println("Hello World") }プログラムはどこから実行されるのか
- main関数
- mainパッケージに存在する
- ソースコードを追う時はここから
- プログラムのエントリポイントで、初期化後はここから実行される
- webサーバの場合は常に実行されているので代わりにhttpハンドラを読む
Goプログラムの構成要素
- パッケージ
- 型
- 関数
- 変数
- 定数
パッケージ
- 関数や定数・変数・型を意味のある単位でまとめたもの
- Goプログラムはパッケージの組み合わせでできあがる
- mainパッケージから別のパッケージをimportする
- importすることで様々な機能が使えるようになる
標準パッケージ:https://golang.org/pkg/
- fmt
- 書式に関する処理など
- net/http
- webサーバなど
- archive, comress
- zipなどの圧縮形式
- encording
- JSON, XML, CSV, TSVなどのファイル形式
- html/template
- web用のHTMLを生成する機能
- os, path/filepath
- osへのアクセスや操作、ファイル操作など
型
組み込み型(最初から使える)
種類 型 ゼロ値 整数 int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
uintptr, byte, rune0 浮動小数点数 float32, float64 0 複素数 complex64, complex128 0 文字列 string "" 真偽値 bool false エラー error nil 型のキャスト
下の
sample.go
はコンパイルエラーになる。
原因はif文で用いる変数と浮動小数点数の不一致。sample.govar sum int sum = 5 + 6 + 3 avg := sum / 3 // float64(avg)とキャストすればエラーではなくなる if avg < 4.5 { fmt.Println(avg) }キャストできない場合には、エラーとなる
例)int("hello world")
などコンポジット型
複数のデータ型が集まって1つの型となっている
型の種類 説明 ゼロ値 構造体 型の異なるデータ型を集めたデータ型 全フィールドがゼロ値 配列 同じ型のデータを集めて並べたデータ型 要素が全てゼロ値 スライス 配列の一部を切り出したデータ型 nil マップ キーと値をマッピングさせたデータ型 nil 構造体
- 型のデータ型の変数を集めたデータ構造
- 各変数はフィールドと呼ばれる
- フィールドの方は自由
- フィールドの方にはコンポジット型やユーザ定義型も使用可能
sample.gotype Person struct { Name string age int } bob := Person{"Bob", 30} fmt.Println(bob.Name, bob.Age) // Bob 30また以下のように初期化関数を作成することで初期化をすることも一般的に行われます。
sample.gofunc newPerson(name string, age int) *Person { person := new(Person) person.Name = name person.Age = age return person } func main(){ var jen *Person = newPerson("Jennifer", 40) fmt.Println(jen.Name, jen.Age) //=>Jennifer 40 }Goではクラスの概念がないが、構造体に対してメソッドを定義することができる
sample.gofunc (<レシーバ引数>) <関数名>([引数]) [戻り値の型] { [関数の本体] }sample.gofunc (p Person) intro(greetings string) string{ return greetings + " I am " + p.Name } func main(){ bob := Person{"Bob", 30} fmt.Println(bob.intro("Hello")) //=> Hello I am Bob }配列
- 同じ型のデータを集めて並べたデータ構造
- 要素の型は全て同じ
- 要素数が違えば違う型
- 要素数は変更できない
- 型は型リテラルで記述することが多い
sample.go// 初期化 var n [5]int var n [5]int{1, 2, 3, 4, 5} n := [...]{1, 2, 3, 4} //lengthを値から推論 n := [...]{3: 20, 4: 11} //要素数: 5, n[3] = 20, n[4] = 11 // アクセス n[1] // 添字は変数可 n[2:3] //スライス演算スライス
- 配列の一部を切り出したデータ構造
- 要素の型は全て同じ
- 要素数は型情報に含まない
- 背後に配列が存在する
sample.go// 初期化 // アクセスは配列と同様 var n []int // 変数にゼロ値を代入 n := make([]int, 3, 10) // 長さと容量を指定して初期化, 各要素䛿ゼロ値で初期化される var n = []int{1, 2, 3, 4, 5} // スライスリテラルで初期化, 要素数は指定しない, 自動で配列が作られる n = append(n, 10, 30) // 要素の追加 // 容量が足りない場合は背後の配列が再確保される len(n) // 長さ cap(n) // 容量マップ
- キーと値をマッピングさせるデータ構造
- キーと値の型を指定できる
- キーには「==」で比較できる型しかNG
型リテラル
var n map[string]int
sample.go// 初期化 var n map[string]int var m = make(map[string]int) n := make(map[string]int, 10) // 容量の指定 m = map([string]int){"a": 2, "b": 4} // 操作 n["a"] // アクセス n["a"] = 20 // キーの値を上書き n, ok := m["a"] // キー存在の確認, ok << true, false if _, ok := m["no"]; !ok { fmt.Println("そのキーはありません") } delete(m, "z") // 要素の削除sample.go// マップのバリューがスライス var a map[string][]int a = map[string][]int{ "a": []int{1, 2, 3}, "b": []int{4, 5, 6}, "c": []int{7, 8, 9}, } fmt.Println(a["a"][0])ユーザ定義型
- typeで名前をつけて新しい型を定義する
type 型名 基底型
sample.go// 組み込み型を基にする type MyInt int // 他のパッケージを基にする type MyWriter io.Writer // 型リテラルを基にする type Person struct { Name string }型リテラル
- 型リテラルとは
- 型の具体的な定義を書き崩した型の表現方法
- コンポジット型などを表現するために使う
- 変数定義やユーザ定義型などで使用する
sample.go// intのスライスを用いた変数定義 var n []int // mapの型リテラルを用いた変数定義 var m map[string]intリテラル = 識別子(名前)が付与されていないもの
関数
- 一連の処理をまとめたもの
- 引数で受け取った値を基に処理を行い戻り値として結果を返す機能
- 引数:関数䛾入力となるものf(x)の場合x
- 戻り値(返り値): 関数の出力となるもの
- 戻り値がある場合䛿変数に代入したり式中で使う
x := f(12, 1+1, y)
組み込み関数
関数名 機能 print, println 表示を行う make コンポジット型の初期化 new 指定した型のメモリ len/cap スライスの長さ/容量を返す copy スライスのコピー delete マップから指定したキーのエントリ complex 複素数型を作成 imag/real 複素数の虚部/実数部を取得 panic/revocer パニックを起こす/回復する 関数の定義
sample.go// 関数の定義方法 func add(x, y int) int { } // 複数の戻り値を返す func swap(x int, y int) (int, int) { return x, y } // 名前付き戻り値 func swap(x y int) (x2, y2 int) { x2, y2 := x, y return } func main() { x, y := swap(1, 2) // 返り値はカンマで区切って取得する }クロージャ
- 無名関数とも呼ばれる
- 定義と実行のタイミングを気をつける
- 関数外の変数を参照している場合
- 実行のタイミングでは値が変わっている可能性がある
sample.go// 「引数に文字列を取り、戻り値に文字列を返す関数」を返す関数 func later() func(string) string { var store string // 前の関数の呼び出し時に格納されている変数の値を返す return func(next string) string { s := store store = next return s } } func main() { f := later() fmt.Println("a: " + f("Golang")) fmt.Println("b: " + f("is")) fmt.Println("c: " + f("awesome!")) fmt.Println("d: " + f("")) }変数
- メモリに名前をつけて値を格納する
- 変数は型を持つ
- 変数に違う型の値を代入することはできない
- 暗黙の型演算がない
- 型推論がある
変数定義
sample.go// 変数定義と代入を同時に var n int = 100 // 変数代入時に型推論 var n = 100 // 変数に後から代入 var n int n = 100 // varを省略, 型推論 n := 100 // まとめて定義 var ( n = 100 b = 1000 )定数
- 値の変わらないもの
- リテラルで記述される場合が多い
- 型推論ではデフォルトの型を使用
- 桁区切りには
_
を用いる
- 例)100_000_000
定数定義
sample.goconst n int = 100 const n = 100 const n = "hello" + "world" // 定数式, コンパイル時に計算される const ( n = 100 m = 200 )制御構文
条件分岐: if
sample.go// 通常のif文 if x == 0 { fmt.Println("xは0です") } else if x == 1 { fmt.Println("xは1です") } else { fmt.Println("xは2以上です") } // 代入if文 if a := f(); a < 1 { fmt.Println(a) } else { fmt.Println(a*2) }条件分岐: switch
sample.goswitch a { case 1, 2: fmt.Println("a is 1 or 2") case a == 3: fmt.Println("a is 3") default fmt.Println("default") }繰り返し: for
Golangの繰り返しはfor文のみ
sample.go// 初期化; 継続条件; 更新 for i := 0; i <= 10; i++ { fmt.Println(i) } // 継続のみ for i <= 100 { } // rangeを使った繰り返し for i, v := range []int{1, 2, 4, 8} { fmt.Println(i, v) }ひたすら簡単なおみくじプログラム
sample.gopackage main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().Unix()) results := map[int]string{ 0: "凶", 1: "小吉", 2: "小吉", 3: "吉", 4: "吉", 5: "中吉", 6: "大吉", } for i := 0; i <= 10; i++ { num := rand.Intn(8) if a := results[num]; a == "" { fmt.Printf("サイコロに%vの目はありません\n", num) } fmt.Printf("あなたの今年の運命は%vです\n", results[num]) } }ポインタ
- ポインタ =
0xc000016068
的なもの- 変数の格納先を表す値
- 値で渡される型の値に対して破壊的な操作を加える際に利用する
- 破壊的な操作 = 関数を出てもその影響が残る
sample.gofunc f(xp *int) { // intのポインタ型 fmt.Println("xp", xp) // xp 0xc000016068 fmt.Println("*xp", *xp) // *xp 0 fmt.Println("&xp", &xp) // &xp 0xc00000e028 *xp = 100 // *でポインタの指す先に値を入れる } func main() { var x int // ゼロ値 = 0 f(&x) // &でポインタを取得する fmt.Println(x) // x == 100 }
- 以下の型では内部でポインタが用いられているためポインタを使用する必要がない
- スライス
- マップ
- チャネル
sample.gons := []int{10, 20, 30} ns2 := ns ns[1] = 200 fmt.Println(ns[0], ns[1], ns[2]) // 10 200 30 fmt.Println(ns2[0], ns2[1], ns2[2]) // 10 200 30
- 投稿日:2020-03-27T19:49:25+09:00
Golang 入門
個人的なメモとして用いる。
GOの特徴
- コンパイラ言語
- 静的型付け言語
- 文法がシンプル
- 並行処理プログラミング
- クロスコンパイルが可能
- 標準パッケージが豊富
ソースコードの構成
main.go// パッケージの定義 package main // 標準パッケージや外部ライブラリのインポート import "fmt" // main関数の定義 func main { fmt.Println("Hello World") }プログラムはどこから実行されるのか
- main関数
- mainパッケージに存在する
- ソースコードを追う時はここから
- プログラムのエントリポイントで、初期化後はここから実行される
- webサーバの場合は常に実行されているので代わりにhttpハンドラを読む
GOPATH
- GOPATHとは?
- Goのソースコードやビルドされたファイルが入るパスが設定されている
- インポートされるパッケージもここから検索される
- 環境変数として設定される
- デフォルトが決まっている
- Unix系:$HOME/go
- Windows:%USERPROFILE%\go
- 複数設定できる
go env GOPATH
コマンドで取得可能実際のGOPATH
- $GOPATH
- bin
- fuga
ビルドされた実行可能ファイルが入る
- pkg
- darwin_amd64
ビルドされたパッケージが入る
- hoge.a
- src
- github.com
- Kanai-Yuki
- sample
project
- main.go
実行ファイル
- sample2
project
- main.go
実行ファイル
Goプログラムの構成要素
- 型
- 関数
- 変数
- 定数
- パッケージ
型
組み込み型(最初から使える)
種類 型 ゼロ値 整数 int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
uintptr, byte, rune0 浮動小数点数 float32, float64 0 複素数 complex64, complex128 0 文字列 string "" 真偽値 bool false エラー error nil 型のキャスト
下の
sample.go
はコンパイルエラーになる。
原因はif文で用いる変数と浮動小数点数の不一致。sample.govar sum int sum = 5 + 6 + 3 avg := sum / 3 // float64(avg)とキャストすればエラーではなくなる if avg < 4.5 { fmt.Println(avg) }キャストできない場合には、エラーとなる
例)int("hello world")
などコンポジット型
複数のデータ型が集まって1つの型となっている
型の種類 説明 ゼロ値 構造体 型の異なるデータ型を集めたデータ型 全フィールドがゼロ値 配列 同じ型のデータを集めて並べたデータ型 要素が全てゼロ値 スライス 配列の一部を切り出したデータ型 nil マップ キーと値をマッピングさせたデータ型 nil 構造体
- 型のデータ型の変数を集めたデータ構造
- 各変数はフィールドと呼ばれる
- フィールドの方は自由
- フィールドの方にはコンポジット型やユーザ定義型も使用可能
sample.gotype Person struct { Name string age int } bob := Person{"Bob", 30} fmt.Println(bob.Name, bob.Age) // Bob 30また以下のように初期化関数を作成することで初期化をすることも一般的に行われます。
sample.gofunc newPerson(name string, age int) *Person { person := new(Person) person.Name = name person.Age = age return person } func main(){ var jen *Person = newPerson("Jennifer", 40) fmt.Println(jen.Name, jen.Age) //=>Jennifer 40 }配列
- 同じ型のデータを集めて並べたデータ構造
- 要素の型は全て同じ
- 要素数が違えば違う型
- 要素数は変更できない
- 型は型リテラルで記述することが多い
sample.go// 初期化 var n [5]int var n [5]int{1, 2, 3, 4, 5} n := [...]{1, 2, 3, 4} //lengthを値から推論 n := [...]{3: 20, 4: 11} //要素数: 5, n[3] = 20, n[4] = 11 // アクセス n[1] // 添字は変数可 n[2:3] //スライス演算スライス
- 配列の一部を切り出したデータ構造
- 要素の型は全て同じ
- 要素数は型情報に含まない
- 背後に配列が存在する
sample.go// 初期化 // アクセスは配列と同様 var n []int // 変数にゼロ値を代入 n := make([]int, 3, 10) // 長さと容量を指定して初期化, 各要素䛿ゼロ値で初期化される var n = []int{1, 2, 3, 4, 5} // スライスリテラルで初期化, 要素数は指定しない, 自動で配列が作られる n = append(n, 10, 30) // 要素の追加 // 容量が足りない場合は背後の配列が再確保される len(n) // 長さ cap(n) // 容量マップ
- キーと値をマッピングさせるデータ構造
- キーと値の型を指定できる
- キーには「==」で比較できる型しかNG
型リテラル
var n map[string]int
sample.go// 初期化 var n map[string]int var m = make(map[string]int) n := make(map[string]int, 10) // 容量の指定 m = map([string]int){"a": 2, "b": 4} // 操作 n["a"] // アクセス n["a"] = 20 // キーの値を上書き n, ok := m["a"] // キー存在の確認, ok << true, false if _, ok := m["no"]; !ok { fmt.Println("そのキーはありません") } delete(m, "z") // 要素の削除sample.go// マップのバリューがスライス var a map[string][]int a = map[string][]int{ "a": []int{1, 2, 3}, "b": []int{4, 5, 6}, "c": []int{7, 8, 9}, } fmt.Println(a["a"][0])ユーザ定義型
- typeで名前をつけて新しい型を定義する
type 型名 基底型
sample.go// 組み込み型を基にする type MyInt int // 他のパッケージを基にする type MyWriter io.Writer // 型リテラルを基にする type Person struct { Name string }型リテラル
- 型リテラルとは
- 型の具体的な定義を書き崩した型の表現方法
- コンポジット型などを表現するために使う
- 変数定義やユーザ定義型などで使用する
sample.go// intのスライスを用いた変数定義 var n []int // mapの型リテラルを用いた変数定義 var m map[string]intリテラル = 識別子(名前)が付与されていないもの
関数
- 一連の処理をまとめたもの
- 引数で受け取った値を基に処理を行い戻り値として結果を返す機能
- 引数:関数䛾入力となるものf(x)の場合x
- 戻り値(返り値): 関数の出力となるもの
- 戻り値がある場合䛿変数に代入したり式中で使う
x := f(12, 1+1, y)
組み込み関数
関数名 機能 print, println 表示を行う make コンポジット型の初期化 new 指定した型のメモリ len/cap スライスの長さ/容量を返す copy スライスのコピー delete マップから指定したキーのエントリ complex 複素数型を作成 imag/real 複素数の虚部/実数部を取得 panic/revocer パニックを起こす/回復する 関数の定義
sample.go// 関数の定義方法 func add(x, y int) int { } // 複数の戻り値を返す func swap(x int, y int) (int, int) { return x, y } // 名前付き戻り値 func swap(x y int) (x2, y2 int) { x2, y2 := x, y return } func main() { x, y := swap(1, 2) // 返り値はカンマで区切って取得する }クロージャ
- 無名関数とも呼ばれる
- 定義と実行のタイミングを気をつける
- 関数外の変数を参照している場合
- 実行のタイミングでは値が変わっている可能性がある
sample.go// 「引数に文字列を取り、戻り値に文字列を返す関数」を返す関数 func later() func(string) string { var store string // 前の関数の呼び出し時に格納されている変数の値を返す return func(next string) string { s := store store = next return s } } func main() { f := later() fmt.Println("a: " + f("Golang")) fmt.Println("b: " + f("is")) fmt.Println("c: " + f("awesome!")) fmt.Println("d: " + f("")) }メソッドとレシーバ
- Goではクラスの概念がないが、ユーザ定義型に対してメソッドを定義することができる
type
で型を定義し、レシーバ・ユーザ定義型を指定することでメソッドを定義することができる- レシーバにできる型
type
で定義した型
- ユーザ定義型をレシーバにできる
- ポインタ型
- レシーバに変更を与えたい的
- 内部にポインタを持つ型
- マップやスライスなどもレシーバにできる
sample.gofunc (<レシーバ引数>) <関数名>([引数]) [戻り値の型] { [関数の本体] }sample.gofunc (p Person) intro(greetings string) string{ return greetings + " I am " + p.Name } type T int func (t *T) M() { fmt.Println("method") } func main(){ bob := Person{"Bob", 30} fmt.Println(bob.intro("Hello")) // Hello I am Bob var t T p := &t p.M() // method }変数
- メモリに名前をつけて値を格納する
- 変数は型を持つ
- 変数に違う型の値を代入することはできない
- 暗黙の型演算がない
- 型推論がある
変数定義
sample.go// 変数定義と代入を同時に var n int = 100 // 変数代入時に型推論 var n = 100 // 変数に後から代入 var n int n = 100 // varを省略, 型推論 n := 100 // まとめて定義 var ( n = 100 b = 1000 )定数
- 値の変わらないもの
- リテラルで記述される場合が多い
- 型推論ではデフォルトの型を使用
- 桁区切りには
_
を用いる
- 例)100_000_000
定数定義
sample.goconst n int = 100 const n = 100 const n = "hello" + "world" // 定数式, コンパイル時に計算される const ( n = 100 m = 200 )制御構文
条件分岐: if
sample.go// 通常のif文 if x == 0 { fmt.Println("xは0です") } else if x == 1 { fmt.Println("xは1です") } else { fmt.Println("xは2以上です") } // 代入if文 if a := f(); a < 1 { fmt.Println(a) } else { fmt.Println(a*2) }条件分岐: switch
sample.goswitch a { case 1, 2: fmt.Println("a is 1 or 2") case a == 3: fmt.Println("a is 3") default fmt.Println("default") }繰り返し: for
Golangの繰り返しはfor文のみ
sample.go// 初期化; 継続条件; 更新 for i := 0; i <= 10; i++ { fmt.Println(i) } // 継続のみ for i <= 100 { } // rangeを使った繰り返し for i, v := range []int{1, 2, 4, 8} { fmt.Println(i, v) }ひたすら簡単なおみくじプログラム
sample.gopackage main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().Unix()) results := map[int]string{ 0: "凶", 1: "小吉", 2: "小吉", 3: "吉", 4: "吉", 5: "中吉", 6: "大吉", } for i := 0; i <= 10; i++ { num := rand.Intn(8) if a := results[num]; a == "" { fmt.Printf("サイコロに%vの目はありません\n", num) } fmt.Printf("あなたの今年の運命は%vです\n", results[num]) } }ポインタ
- ポインタ =
0xc000016068
的なもの- 変数の格納先を表す値
- 値で渡される型の値に対して破壊的な操作を加える際に利用する
- 破壊的な操作 = 関数を出てもその影響が残る
sample.gofunc f(xp *int) { // intのポインタ型 fmt.Println("xp", xp) // xp 0xc000016068 fmt.Println("*xp", *xp) // *xp 0 fmt.Println("&xp", &xp) // &xp 0xc00000e028 *xp = 100 // *でポインタの指す先に値を入れる } func main() { var x int // ゼロ値 = 0 f(&x) // &でポインタを取得する fmt.Println(x) // x == 100 }
- 以下の型では内部でポインタが用いられているためポインタを使用する必要がない
- スライス
- マップ
- チャネル
sample.gons := []int{10, 20, 30} ns2 := ns ns[1] = 200 fmt.Println(ns[0], ns[1], ns[2]) // 10 200 30 fmt.Println(ns2[0], ns2[1], ns2[2]) // 10 200 30パッケージ
- 関数や定数・変数・型を意味のある単位でまとめたもの
- Goプログラムはパッケージの組み合わせでできあがる
- mainパッケージから別のパッケージをimportする
- importすることで様々な機能が使えるようになる
標準パッケージ:https://golang.org/pkg/
package名 用途 fmt 書式に関する処理など net/http webサーバ archive, comress zipなどの圧縮形式 encording JSON, XML, CSV, TSVなどのファイル形式 html/template web用のHTMLを生成する機能 os, path/filepath osへのアクセスや操作、ファイル操作など パッケージ外へのエクスポート
- 先頭を大文字にした識別子がエクスポートされる
var Abc string // exported
var abc string // unexported
go get
でライブラリの取得をする
- goのライブラリを取得するコマンド
- 依存するライブラリも一緒に取得してくれる
- 指定した場所からダウンロード&インストールしてくれる
$ go get github.com/tenntenn/greeting $ ls $GOPATH/src/github.com/tenntenn/greeting README.md greeting.go
go mod
コマンド
go mod
コマンドを有効にする
- 環境変数の
GO111MODULE
をon
にするgo mod
コマンドのサブコマンド
コマンド 処理 go mod init
go mod init モジュール名指定したモジュール名でgo.modファイルを作成する
モジュール名を省略するとGOPATHから推測するgo mod tidy 使用していないパッケージのgo.modからの削除
必要なパッケージのダウンロードとgo.modへの追加go mod why 指定したパッケージがなぜ必要になったか表示 go mod vendor 依存するパッケージをvendor以下にコピーする
- 投稿日:2020-03-27T18:20:33+09:00
Gin+Realize+docker-compose+Nginxで環境を整えてみる
Gin + Nginxで仕事でも使えるかな、と思いつつ開発環境を作成する
ソースはこちら
環境
Go 1.14
Gin 1.5.0
Docker 19.03.8
docker-compose 1.25.4やること
- GinでREST APIを作成
- realizeでホットリロードできるようする
- docker-composeでNginxを立ててリバプロする
GinでREST APIを作成
GETでリクエストがきたら、
{"key":"value"}
を返すだけのコードを作成main.gopackage main import "github.com/gin-gonic/gin" func main() { router := gin.Default() router.GET("/api", func(context *gin.Context) { context.JSON(200, gin.H{ "key": "value", }) }) router.Run() }realizeでホットリロードできるようする
変更後に
go run main.go
をするのが面倒なので、ホットリロードをできるようにする
realizeをInstallするgo get github.com/oxequa/realizemain.goがあるディレクトリで
realize startをすると、そのディレクトリに
.realize.yaml
が作成される.realize.yamlsettings: legacy: force: false interval: 0s schema: - name: src path: . commands: {} watcher: extensions: - go paths: - / ignored_paths: - .git - .realize - vendorこのままでは、watch状態にならないようなので、以下に変更する
.realize.yamlsettings: legacy: force: false interval: 0s schema: - name: src path: . commands: run: status: true watcher: extensions: - go paths: - / ignored_paths: - .git - .realize - vendorそして再度実行すると、起動ログが出力される
realize start[21:15:11][SRC] : Watching 1 file/s 1 folder/s [21:15:11][SRC] : Install started [21:15:12][SRC] : Install completed in 0.946 s [21:15:12][SRC] : Running.. [21:15:12][SRC] : [GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [21:15:12][SRC] : [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. [21:15:12][SRC] : - using env: export GIN_MODE=release [21:15:12][SRC] : - using code: gin.SetMode(gin.ReleaseMode) [21:15:12][SRC] : [GIN-debug] GET /api --> main.main.func1 (3 handlers) [21:15:12][SRC] : [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [21:15:12][SRC] : [GIN-debug] Listening and serving HTTP on localhost:8080この状態で、コードの変更をすると変更を検知してリロードが走る
Dockerリバプロの設定
localhost:80でリクエストがあると、Nginxが3000番ポートへ流す構成にする
Gin環境のDockerfileとコードを少し改変
FROM golang:latest RUN go get -u github.com/gin-gonic/gin && \ go get -u github.com/oxequa/realize WORKDIR /go/src/github.com/gin-nginx-sample/src CMD [ "realize", "start"]3000番ポートで起動するように変更
(一応ログファイルも出るようにして、リクエストIDを表示するだけのmiddlewareも書いてみた)main.gopackage main import ( "github.com/gin-gonic/gin" "github.com/labstack/gommon/log" "io" "os" ) func main() { file, _ := os.Create("gin.log") gin.DefaultWriter = io.MultiWriter(file) log.SetOutput(gin.DefaultWriter) router := gin.Default() router.GET("/api", myMiddleware(), func(context *gin.Context) { context.JSON(200, gin.H{ "key": "value", }) }) router.Run(":3000") } func myMiddleware() gin.HandlerFunc { return func(context *gin.Context) { header := context.GetHeader("X-REQUEST-ID") log.Print("RequestId=" + header) context.Next() }Nginx環境のDockerfile
自分で書いた
nginx.conf
をnginx.confに上書きするFROM nginx:latest COPY ./nginx-docker/nginx.conf /etc/nginx/nginx.confnginx.confworker_processes auto; events { worker_connections 1024; } http { server { listen 80; location / { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_pass http://golang:3000; } } }docker-composeをかく
2つの
Dockerfile
を読み込んでdocker-composeを設定するdocker-compose.ymlversion: '3' services: golang: container_name: "gin-nginx-sample" tty: true build: context: . dockerfile: ./go-docker/Dockerfile volumes: - ./src:/go/src/github.com/gin-nginx-sample/src ports: - "3000" nginx: container_name: "nginx" build: context: . dockerfile: ./nginx-docker/Dockerfile ports: - "80:80" depends_on: - "golang"確認
locahost:80/api
でアクセスしたら、{"key":"value"}
が返ってるmain.go
を変更して、ホットリロードが動く これらができたら、成功!まとめ
普段意識して触ることのない技術スタックだったため、これだけの半日かかりました
もっと良い方法があったらぜひコメントください参考
- 投稿日:2020-03-27T18:05:00+09:00
Goの門をくぐってみる(GoとGinのQuickStart)
なんとなく触ってみたいなと前々から思ってたので、チャレンジ。普段はJavaを書いています。なので、型が欲しいという思いでGoに触れてみます。僕はGoについての知識0なので、どこまで簡単に作れるのかもみていきたいです。
いろんな規則や、わからないことがあれば、以下の2つのサイトを探りながら書いていきたいと思います。
事前準備
homebrew
このサイトからinstallできます
go
コマンド打つだけ、簡単
brew install goversionは1.14
ri2kku~:$: ->> go version go version go1.14 darwin/amd64
何がともあれGoのQuickStart
Go公式のQuickStartを参考に世界に挨拶してみます
https://golang.org/ファイル作成 -> コード書く -> 動かす、まで一気に
ri2kku~/sandbox/go/go-sample:$: ->> vi hello_world.go
hello_world.gopackage main import "fmt" func main() { fmt.Println("Hello, World") }ri2kku~/sandbox/go/go-sample:$: ->> go run hello_world.go Hello, World雑感
10分ほどで世界に挨拶できました!こんなに簡単なら早く触っておけばよかったです。シンタックス等はJavaと結構違っているのかな?セミコロンがないことにすごく違和感を感じていました。
Gin
次はFWを使って簡単なアプリを作成してみます
一番使われてそうなFWを選びました
https://github.com/gin-gonic/ginインストール
Goのversionは1.1以降が必要らしいです
go get -u github.com/gin-gonic/ginGinのQuickStart
書いて動かすところまで
exmaple.gopackage main import "github.com/gin-gonic/gin" func main() { r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{"message": "pong",}) }) r.Run() }localhost:8080でlistenしてる
ri2kku~/sandbox/go/gin-sample:$: ->> go run example.go GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. [GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. - using env: export GIN_MODE=release - using code: gin.SetMode(gin.ReleaseMode) [GIN-debug] GET /ping --> main.main.func1 (3 handlers) [GIN-debug] Environment variable PORT is undefined. Using port :8080 by default [GIN-debug] Listening and serving HTTP on localhost:8080
http://localhost:8080/ping
にアクセスすると、pong
が返ってくるようになっているのを確認できましたri2kku~/sandbox/go/gin-sample:$: ->> curl http://localhost:8080/ping {"message":"pong"}雑感
すごく簡単に作れる模様?インストールがすごく楽ですぐに何かを試したい!という時にはさらっとかけそうな印象を持ちました、この時点では。まだ海面に触れたようなものなので、次はもう少し深く潜って簡単なWebアプリを作成したいと思います。
- 投稿日:2020-03-27T09:22:57+09:00
Golangで、デザインパターン「Flyweight」を学ぶ
GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Flyweight」を学ぶ"今回は、Pythonで実装した”Flyweight”のサンプルアプリをGolangで実装し直してみました。
■ Flyweight(フライウェイト・パターン)
Flyweightパターン(フライウェイト・パターン)とは、GoFによって定義されたデザインパターンの1つである。 等価なインスタンスを別々の箇所で使用する際に、一つのインスタンスを再利用することによってプログラムを省リソース化することを目的とする。
UML class and sequence diagram
UML class diagram
□ 備忘録
flyweightというのは、「フライ級」のことで、ボクシングで最も体重が軽い階級を示すものであり、このデザインパターンでは、オブジェクトを「軽く」するためのものだそうです。
Flyweight
パターンで使っている技法は、「インスタンスをできるだけ共有させて、無駄にnewしない」というもので、インスタンスが必要なときに、いつもnewするのではなく、すでに作ってあるインスタンスを利用できるなら、それを共有して使うものだそうです。
直感的に、デザインパターン「Singleton」の応用だと感じますね。■ "Flyweight"のサンプルプログラム
(1) 事前準備
アクキーアートっぽく、数字を表示するテキストファイルを準備しておきます。
big0.txt....######...... ..##......##.... ..##......##.... ..##......##.... ..##......##.... ..##......##.... ....######...... ................big1.txt......##........ ..######........ ......##........ ......##........ ......##........ ......##........ ..##########.... ................big2.txt....######...... ..##......##.... ..........##.... ......####...... ....##.......... ..##............ ..##########.... ................(以下、略)
(2) 動作確認
実際に、Flyweightパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
- 引数の数字の順番に、指定された数字をアスキーアートで表示する
$ go run Main.go 012123 ....######...... ..##......##.... ..##......##.... ..##......##.... ..##......##.... ..##......##.... ....######...... ................ ......##........ ..######........ ......##........ ......##........ ......##........ ......##........ ..##########.... ................ ....######...... ..##......##.... ..........##.... ......####...... ....##.......... ..##............ ..##########.... ................ ......##........ ..######........ ......##........ ......##........ ......##........ ......##........ ..##########.... ................ ....######...... ..##......##.... ..........##.... ......####...... ....##.......... ..##............ ..##########.... ................ ....######...... ..##......##.... ..........##.... ......####...... ..........##.... ..##......##.... ....######...... ................■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Flyweight
- ディレクトリ構成
. ├── Main.go ├── big0.txt ├── big1.txt ├── big2.txt ├── big3.txt ├── big4.txt ├── big5.txt ├── big6.txt ├── big7.txt ├── big8.txt ├── big9.txt └── flyweight └── big_char_factory.go(1) Flyweight(フライ級)の役
普通に扱うとプログラムが重くなるので共有した方が良いものを表す役です。
サンプルプログラムでは、bigChar
構造体が、この役を努めます。flyweight/big_char_factory.gopackage flyweight import ( "fmt" "os" ) ... (snip) type bigChar struct { fontdata string } func newBigChar(charname string) *bigChar { char := &bigChar{} data := make([]byte, 256) f, err := os.Open(fmt.Sprintf("big%s.txt", charname)) if err == nil { f.Read(data) char.fontdata = string(data) } else { char.fontdata = charname + "?" } return char } func (b *bigChar) print() { fmt.Println(b.fontdata) }(2) FlyweightFactory(フライ級の工場)の役
Flyweight
を作る工場の役です。この工場を使ってFlyweight
役を作ると、インスタンスが共有されます。
サンプルプログラムでは、bigCharFactory
構造体が、この役を努めます。flyweight/big_char_factory.gotype bigCharFactory struct { pool map[string]*bigChar } func newBigCharFactory() *bigCharFactory { bigChrFct := &bigCharFactory{ pool: make(map[string]*bigChar), } return bigChrFct } var instance *bigCharFactory func getInstance() *bigCharFactory { if instance == nil { instance = newBigCharFactory() } return instance } func (b *bigCharFactory) getBigChar(charname string) *bigChar { var bc *bigChar if _, ok := b.pool[charname]; !ok { bc = newBigChar(charname) b.pool[charname] = bc } else { bc = b.pool[charname] } return bc }(3) Client(依頼人)の役
FlyweightFactory
役を使ってFlyweight
を作り出し、それを利用する役です。
サンプルプログラムでは、BigString
構造体とstartMain
関数が、この役を努めます。flyweight/big_char_factory.go// BigString is struct type BigString struct { bigchars []*bigChar } // NewBigString func for initalizing BigString func NewBigString(str string) *BigString { bigStr := &BigString{} factory := getInstance() for _, s := range str { bigStr.bigchars = append(bigStr.bigchars, factory.getBigChar(string(s))) } return bigStr } // Print func for print something func (b *BigString) Print() { for _, bc := range b.bigchars { bc.print() } }Main.gopackage main import ( "flag" "./flyweight" ) func startMain(str string) { bs := flyweight.NewBigString(str) bs.Print() } func main() { flag.Parse() startMain(flag.Arg(0)) }■ 参考URL
- 投稿日:2020-03-27T05:51:17+09:00
Golangで、デザインパターン「Facade」を学ぶ
GoFのデザインパターンを学習する素材として、書籍「増補改訂版Java言語で学ぶデザインパターン入門」が参考になるみたいですね。
取り上げられている実例は、JAVAベースのため、Pythonで同等のプラクティスに挑んだことがありました。
Qiita記事: "Pythonで、デザインパターン「Facade」を学ぶ"今回は、Pythonで実装した”Facade”のサンプルアプリをGolangで実装し直してみました。
■ Facade(ファサード・パターン)
Facadeパターンあるいは Façadeパターン(ファサード・パターン)とは、GoF(Gang of Four; 4人のギャングたち)によって定義された、コンピュータソフトウェアのデザインパターンの1つである。Facade(ファサード)とは「建物の正面」を意味する。異なるサブシステムを単純な操作だけを持ったFacadeクラスで結び、サブシステム間の独立性を高める事を目的とする。
UML class and sequence diagram
UML class diagram
■ "Facade"のサンプルプログラム
実際に、Facadeパターンを活用したサンプルプログラムを動かしてみて、次のような動作の様子を確認したいと思います。
$ go run Main.go welcome1.html is created for hyuki@hyuki.com (Hiroshi Yuki) welcome2.html is created for mamoru@hyuki.com (Mamoru Takahashi)サンプルプログラムを起動すると、2つのhtmlファイルが生成されます。
おのおのを、Webブラウザを確認してみるとこんな感じになりました。
- welcome1.html
- welcome2.html
■ サンプルプログラムの詳細
Gitリポジトリにも、同様のコードをアップしています。
https://github.com/ttsubo/study_of_design_pattern_with_golang/tree/master/Facade
- ディレクトリ構成
. ├── Main.go ├── facade │ ├── database.go │ ├── html_writer.go │ └── page_maker.go ├── maildata.json ├── welcome1.html └── welcome2.html(1) Facade(正面)の役
Facade
役は、システムを構成しているその他大勢の役の「シンプルな窓口」となります。Facade
役は、高レベルでシンプルなインタフェースをシステム外部に提供します。
サンプルプログラムでは、PageMaker
構造体が、この役を努めます。facade/page_maker.gopackage facade import ( "fmt" "os" ) // PageMaker is struct type PageMaker struct { } // Pagemaker is variable var Pagemaker = &PageMaker{} // MakeWelcomePage func for making welcome page func (p *PageMaker) MakeWelcomePage(mailaddr, filename string) { username := "" database := &Database{} prop := database.getProperties("maildata") for _, person := range prop { if person.Mail == mailaddr { username = person.Name } } file, _ := os.Create(filename) writer := &HTMLWriter{file} writer.title(fmt.Sprintf("Welcom to %s's page!", username)) writer.paragraph("We'll wait for your sending") writer.mailto(mailaddr, username) writer.close() fmt.Printf("%s is created for %s (%s)\n", filename, mailaddr, username) }(2) システムを構成しているその他大勢の役
その他大勢の役は、それぞれの仕事を行いますが、
Facade
役のことは意識しません。Facade
役から呼び出されて仕事を行いますが、その他大勢の役の方からFacade
役を呼び出すことはありません。
サンプルプログラムでは、Database
構造体と、HTMLWriter
構造体が、この役を努めます。facade/database.gopackage facade import ( "encoding/json" "fmt" "io/ioutil" "os" ) // Database is struct type Database struct { } // Person is struct type Person struct { Name string `json:"name"` Mail string `json:"mail"` } var maildata []Person func (d *Database) getProperties(dbname string) []Person { filename := dbname + ".json" jsonString, err := ioutil.ReadFile(filename) if err != nil { fmt.Println(err) os.Exit(1) } if err = json.Unmarshal(jsonString, &maildata); err != nil { fmt.Println(err) os.Exit(1) } return maildata }maildata.json[ { "mail": "hyuki@hyuki.com", "name": "Hiroshi Yuki" }, { "mail": "hanako@hyuki.com", "name": "Hananko Sato" }, { "mail": "tomura@hyuki.com", "name": "Tomura" }, { "mail": "mamoru@hyuki.com", "name": "Mamoru Takahashi" } ]facade/html_writer.gopackage facade import ( "fmt" "os" ) // HTMLWriter is struct type HTMLWriter struct { writer *os.File } func (h *HTMLWriter) title(title string) { h.writer.Write([]byte("<html>\n")) h.writer.Write([]byte("<head>")) h.writer.Write([]byte(fmt.Sprintf("<title>%s</title>", title))) h.writer.Write([]byte("</head>\n")) h.writer.Write([]byte("<body>\n")) h.writer.Write([]byte(fmt.Sprintf("<h1>%s</h1>\n", title))) } func (h *HTMLWriter) paragraph(msg string) { h.writer.Write([]byte(fmt.Sprintf("<p>%s</p>\n", msg))) } func (h *HTMLWriter) link(href, caption string) { h.writer.Write([]byte(fmt.Sprintf("<a href=\"%s\">%s</a>", href, caption))) } func (h *HTMLWriter) mailto(mailaddr, username string) { h.link(fmt.Sprintf("mailto:%s", mailaddr), username) } func (h *HTMLWriter) close() { h.writer.Write([]byte("</body>\n")) h.writer.Write([]byte("</html>\n")) h.writer.Close() }(3) Client(依頼人)の役
Facadeパターンを利用する役です。
サンプルプログラムでは、startMain
関数が、この役を努めます。Main.gopackage main import ( "./facade" ) func startMain() { facade.Pagemaker.MakeWelcomePage("hyuki@hyuki.com", "welcome1.html") facade.Pagemaker.MakeWelcomePage("mamoru@hyuki.com", "welcome2.html") } func main() { startMain() }■ 参考URL