Edit
Go言語

2009年にグーグルが開発したマルチスレッドに強い言語。主にサーバー向けの開発用として幅広く使われている。

Edit
開発環境

Edit
Visual Studio Code + Go

マイクロフトが提供する VSCにGoのextentionをインストール事で、無料で利用できる。

Edit
インストール

Edit
環境変数の設定

基本的にはインストール時に自動的に設定されます。(下記は参考まで)

Edit
VSCにGoの追加

VSC起動をすると、左側にアイコンが並ぶ。そこの四角い「機能拡張」を選択すると、追加でできる機能拡張がリスト形式で表示される。そこから likehoban の Go をインストールする。

Edit
VSCの再起動とツールインストール

VSCを再起動したあと、新しいファイルで、sample.go などのファイルを作って保存するだけで、不足しているツール類をインストールするか聞いてくるので、Install All を選ぶと必要なものがインストールされます。

Edit
コンパイルと実行

package main
import "fmt"
func main() {
	fmt.Printf("Hello World\n")
}

上記のサンプルソースを、sample.go などで保存するとソースの整形が自動的に行われたあと、コンパイルされ保存されます。その後、VSCのメニュー「デバッグ」から「デバッグ実行」を選ぶことで、実行されVSCのウィンドウ下部のデバッグコンソールに、標準出力として hello world が表示されます。

Edit
ブレイクポイントの利用

デバッグコンソールの横の、コンソールで

go get github.com/derekparker/delve/cmd/dlv

を行うことで、delve をインストールする。これによってステップ実行などのテストが可能になる。行番号の隣にマウスカーソルを持っていくと赤い●が出るので、押すとブレークポイントを設定できる。

Edit
実行ファイルの作成

そきほどのコンソールで、

go build sample.go

と入力するだけでそのにある、sample.go をコンパイルし、exe ファイルを作ってくれる。

Edit
ワークスペースについて

Goでは、環境変数GOPATHに定義されたワークスペース用フォルダを利用して、ビルド環境とする。フォルダの下には bin(実行ファイル)、pkg(パッケージオブジェクト)、src(ソース本体)の最低3つのフォルダが作成されワークスペースとして利用される。

VSCではワークスペース毎に別ウィンドウを起動して利用する。これはVisual Studio Communication などのソリューションと同じ概念と思って大差はない。ワークスペースが設置されたフォルダには、保存時にVSCが settings.json やlaunch.json というワークスペースの設定ファイルが設置する。エクスプローラーでこのファイルをダブルクリックすることで、VSC+対象のワークスペースを直ぐにオープンできる。

setting.png

またワークスペースやユーザー設定は、VSCのメニューの、ファイル(F) > 基本設定 > 設定 から設定することが可能。またその画面から既存の設定(初期値)も確認できる。(個別の設定には、この既存の設定を必要な部分のみコピー修正すると便利。GOPATHもここで設定可能)

{
     "go.gopath": "${workspaceRoot}",
}

個人的にはこのVSCのGOPATH設定には、対象のワークスペースを設定しておくと、パッケージの依存関係及び、バージョンロックに対応できる。(初期値はユーザーフォルダの下の go フォルダが VSCの GOPATHに設定されている)但しコンソールの環境設定には、このGOPATHが反映されていないので、go get コマンドを利用する場合には、

set GOPATH=c:/Users/me/go/myprivate/test/sample_ws

等、自分のワークスペースの位置で環境設定しておくと良い。

Edit
GoLand(有料)

Edit
その他のIDE環境

Edit
Go言語の構文や文法

Edit
defer関数(終了処理の記述)

それを呼び出した関数が終了する際に実行すべき処理を記述することができる

func main() {
   defer fmt.Println("End")
   fmt.Println("Start")
}

これが実行されても、表示はStartの後にEndが表示される。ちなみに Startの表示がエラーで処理されなくても、Endは表示される。

※通常 panic/recover も合わせて利用されることが、多いが panic は基本非推奨。本来、外部にエラーを伝える手段としては error インターフェース(戻り値の1つに error 変数を含ませること)を使うのが正式なやり方で、panic/recover はあくまで内部的に用いるべきもの(主にテスト用)

Edit
パッケージ機能が基本機能として実装

外部のパッケージは、パッケージ取得コマンドを、ターミナルで入力することで、パッケージがダウンロード・コンパイルが行われimportで利用できるようになる。

> go get github.com/gorilla/websocket

このコマンド実行によって、ソース内で下記のように利用できる

import "github.com/gorilla/websocket"

これらのパケージの実体は /Users/<user_name>/go/src/ の下に設置される。 また、自分のソース中にパッケージを組み込む時は import のフォルダ位置に注意すること。(特に同じ名前のパッケージでは注意が必要)

Edit
git以外のimport

go get コマンドで下記のリポジトリからパッケージの取得はできるが、コマンド起動できる、各ツールを前もってインストールしておく必要がある。

Edit
関数名の頭を大文字にすると

パッケージの外から参照可能な関数となるので、いちいち、extern宣言のような外部参照定義を行わなくても良い。

Edit
関数も変数に代入できる

JavaScriptのように関数も変数に代入できる。これによって、コールバックなどの処理が簡単になる。

f := myFunc
ret := f(引数)

代入した後は変数名に引数を付けることで呼び出せる。

Edit
基本的な関数の記述方法

func <関数名>([引数]) [戻り値の型] {
    [関数の本体]
}

他の言語と違って、戻り値の定義がfuncの前になく、引数の後ろに記述される。また戻り値がない場合は記載する必要は無し。(voidもいらない)また()で囲えば、複数の戻り値を指定可能。(関数名を省略すると無名関数になる。その場合関数を変数に代入しないと呼び出せない)

例:func myFunc (arg1 int, arg2 int) (string, int) {}

この関数の呼ばれ方は下記の通り

例:str1, num1 := myFunc(10, 20)

※ := は代入。 戻り値を使わない場合には、戻り値用の変数の代わりに"_“を利用して戻り値を破棄できる。

例:_, num := myFunc(10, 20)

また戻り値に変数名を付けると、その変数名に格納した値が戻り値となる。

func myFunc (arg1 int, arg2 int) (ret1 string, ret2 int) {
    ret1 := "Test String"
    ret2 := arg1 + arg2
    return
}

Edit
関数を引数に渡す場合。

func(引数の型) 戻り値の型

通常の関数定義のように、上記をワンセットで設定すれば良い。無記名関数のように変数として関数を扱うので、関数名は当然必要ない。

func myFunc (arg func(int) int) func (int) {
     ret := arg(10)
     return func(ret)
}

Edit
関数の中で関数を定義する場合

Go言語の関数はクロージャであるため、関数内の関数は外側のローカル変数を参照できる。これは for や if などのブロック関数とスコープの範囲は同じ(関数内のローカル変数にはJavaなどのように普通にアクセスできる)。但し JavaScript のように関数内に有名関数は作成できないようで、無名関数で作らないとならないようだ。

func myFunc ( arg1 int ) func(int) int {
    sum := 0
    f := func (a1 int) int {
          sum = sum + a1
           return sum
    }
    return f
}

※但し関数で定義されたローカル変数と同じ変数名を、ブロック内で再定義してもエラーにならずに、新規で作成されてしまう。(グローバル変数とローカル変数の関係のように)

Edit
文字の取扱い

"(ダブルクオーテーション)で囲まれた文字を文字列としてあつかう。'(シングルクォーテーション)ではRune という 型になり、表示させるとコード値が表示される。

Edit
構造体

Cのように下記のように構造体を定義できる。

type Animal struct {
	Name string
	Age  int
}
type Cat struct {
	Animal
	Type string
}

この場合 cat はAnimal構造体を内包する。但し初期化時は子構造体の型を指定する必要があるが(JSON風指定)

c1 := Cat{Animal: Animal{Name: "hage", Age: 9}, Type: "russia"}

利用時は子構造体の型を指定せずにアクセスできる。

fmt.Printf("name=%s, age=%d, type=%s", c1.Name, c1.Age, c1.Type)

Edit
構造体のポインタ参照

Goの構造体は値渡しとポインタ渡しの受け渡し方法があり、値渡しの場合は構造体はコピーされ渡されるが、ポインタ渡しの場合データのポインタ(アドレス)が渡される為、後で上書きされてしまう。(但しコピー処理が無いので早い。ポインタ型にするには頭に&を付ける)

type Dog struct {
	name string
	age  int
}
func main() {
       c1 := &Dog{name: "Hoge", age: 3}
       c2 := c1
       c2.age = 5
       fmt.Printf("c1 age = %d, c2 age = %d \n", c1.age, c2.age)
}

この結果では、c1 age = 5, c2 age = 5 と表示され、c2 の修正が c1 にも影響している。これは最初に作成した構造体のポインタを c1 に代入しているためである。(サンプルをみると分かるように利用する場合はポインタを意識する必要はない)ちなみに初期化の&Dogを Dogにすれば値渡しとなり中がコピーされるため結果は c1 age = 3, c2 age = 5 となる。

Edit
構造体への関数埋め込み(メソッド)

JavaScriptのprototype関数のように、構造体の関数を func を使って定義できる。この関数をオブジェクト思考的(Goはオブジェクト思考言語ではない)にメソッドと呼ぶ。

func (<レシーバー>) <関数名>([引数]) [戻り値の型] {
      [関数の本体]
}
レシーバーの部分は、組み込む構造体の名前とメソッド内でのローカル変数名の定義を指す。
type Dog struct {
	name string
	age  int
}
func (c3 Dog) printName(num int) {
	fmt.Printf("num=%d,name=%s,age=%d \n", num, c3.name, c3.age)
}
func main() {
	c1 := Dog{name: "Hage", age: 7}
	c2 := &Dog{name: "Hoge", age: 3}
	c1.printName(1)
	c2.printName(2)
}

利用するには、構造体変数.メソッド名 で呼ぶだけで良い。結果は

num=1,name=Hage,age=7 
num=2,name=Hoge,age=3 

と表示される。

Edit
構造体のバイトストリームへの相互変換

import 	"bytes"
import	"encoding/binary"

バイトストリームを構造体へ変換する

buf := bytes.NewBuffer(b)                         // バイト配列 []bytes を io.buffer型に
var str 構造体名
binary.Read(buf, binary.LittleEndian, &str)   // 構造体に割当 (buf -> str)

構造体をバイト配列に変換

buf := new(bytes.Buffer)
var str 構造体                                    // 構造体への値設定は別途行う必要
binary.Write(buf, binary.LittleEndian, &str)  // バイトバッファに割当(str -> buf)※追記される。

Edit
構造体の一部を設定して代入

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,                     // これと下の項目に値を設定
	WriteBufferSize: 1024,
}

Edit
構造体の変数にタグを設定

主に json や DBなどのキー名などの設定に使われることが多い。詳しくはこのページ

type Person struct {
   Name     string `label:"名前" json:"name" db:"name" validate:"required"`
   Age        int     `label:"年齢" json:"age" db:"age" validate:"required"`
}

JSONパッケージではJSON形式のフォーマットを利用するときに、自動的にこの json タグを参照してデータを作成する。(パッケージの実装に依存)

import reflect
p := &Person{ Name:"hoge", Age:18 }
fmt.Printf("Var count = %d \n", reflect.TypeOf(*p).NumField())
fmt.Printf("Name(Label) = %s \n", reflect.TypeOf(*p).Field(0).Tag.Get("label"))
fmt.Printf(" Age(Label) = %s \n", reflect.TypeOf(*p).Field(1).Tag.Get("label"))

構造体の変数の数と、タグの名前から値を表示する。

Edit
make関数

構造体は new で初期化できますが、マップ、スライス、チャネルを利用する場合の初期化は make を利用します。

make (chan []byte, 256)

Edit
並列処理

Goでは他の言語よりも並列処理に特化した言語であり、シンプルな実装で応用ができる。

Edit
goroutine(ゴルーチン)

coroutineとは違い、実際に軽量スレッドのように割り当てられる(実際は複数の Thread 上に多重化されて実装されてる)ので、coroutineのように終わったら自分の関数に戻ってきて関数の処理順が維持されたりしない。(goroutineを作動させたら直ぐに次の行に進む。但し channelを使えば似たようなことは可能) goroutineは起動した関数が終われば自動的に終了する。(但しmainが終了すると強制終了)

Edit
チャンネル

goroutine間通信をチャンネルを使うことで、mutexなどのフラグ処理を行わずに通信ができる。(但し遅いらしい)

func f(ch chan string) {
	ch <- "Goroutine End"
}
func main() {
	ch := make(chan string)
	go f(ch)
	fmt.Print(<-ch) // ここでデータが来るまでブロックする
}

この <-ch による、受取ではデータを受け取るまでブロックされ、mainは終了しない。また引数にチャネルを利用する場合は、受取側を明記する必要がある。

func task(m string) <-chan string {           // ここの <- が受取側のサイン
	r := make(chan string)
	go func() {
		msg := fmt.Sprintf("%s done", m)
		r <- msg
	}()
	return r
}
func main() {
	chn := task("job 1")
	fmt.Printf("task=%s \n", <-chn)
}

また複数の goroutine を纏めて待つ場合は sync モジュールを使うと便利。close( chn )を利用する方法もある。

func main() {
	var wg sync.WaitGroup
	for i:=0; i<3; i++ {
		wg.Add(1)                 // goroutine内で設定すると、goroutineが起動するまえに、wg.Wait()に達してしまうため Addでカウントされずに main が終了する。
		go func(i int) {
			fmt.Printf("count = %d \n", i)
			wg.Done()
		}(i)
	}
	wg.Wait()
}

一定時間すぎるとメッセージを送るチャネルもある。(makeの必要はない)

for {
	select {
	case c := <-chn:                        // 通常のチャネル受取
		normal(c)
	case <-time.After(time.Second): // 一定時間経過したら受信
		timeout()
	}
}

Edit
その他

現在のCPUの数と、goroutineの数を表示

fmt.Printf("cpu=%d,goru=%d \n", runtime.NumCPU(), runtime.NumGoroutine())

マルチコア対応の為の呪文

cpus := runtime.NumCPU()
runtime.GOMAXPROCS(cpus)

Edit
パッケージ管理

Edit
テストフレームワークについて

Goでは簡単なテストフレームワーク環境が提供されている。ファイル名の最後に _test.go を付けると、対象のファイルのテストコードとして識別される。その場合、ファイル内の関数には、Test(関数名) と関数名の前にTestという文字列を追加し引数を (t *testing.T) とする事で、指定の関数のテストプログラムとして認識される。

ファイル名の例:myfunc_test.go
関数の例:func TestMyFunc(t *testing.T) {}

この関数の中で、t.Errorやt.Fail などが呼ばれるとテストの失敗と認識される。

Edit
WebSocket

基本的な WebSocketの開き方

import github.com/gorilla/websocket
func handler(w http.ResponseWriter, r *http.Request) {
   var upgrader = websocket.Upgrader{
       EnableCompression: true,                                  // deflate 圧縮 ON
   }
   conn, err := upgrader.Upgrade(w, r, nil)
}

メッセージの読み書き(messageType:1...text, 2...binary)

messageType, p, err := conn.ReadMessage()         // 読み出し
err := conn.WriteMessage(messageType, p)          // 書き出し

JSONの読み書き

func ReadJSON(c *Conn, v interface{}) error
func WriteJSON(c *Conn, v interface{}) error

Edit
メッセージパック

MessagePackはJSONのテキストエンコード(シリアライズ)と違いバイナリでデータを送るシリアライズライブラリ。

go get -u github.com/vmihailenco/msgpack

Edit
JSONで通信

type Msg struct {
    Msg string // メッセージ
    ID  int32  // ID
}
func  echoHandler(ws *websocket.Conn) {
    data := Msg
    websocket.JSON.Receive(ws, &data)
    log.Printf("Recieved=%#v\n", data)
    websocket.JSON.Send(ws, data)
}
func main() {
    http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
           s := websocket.Server{Handler: websocket.Handler(echoHandler)}
           s.ServeHTTP(w, r)
       })
}

Edit
ワンポイント

Edit
クロスドメイン通信の許可

通常は許可する必要は無いがテストで許可したい場合など

http.ResponseWriter.Header().Set("Access-Control-Allow-Origin", "*")

Edit
セッションのタイムオーバー設定

接続のタイムアウトへの書き込み/読み取りを設定するのに用いられ、接続は自動的に切断される。

func (c *TCPConn) SetReadDeadline(t time.Time) error
func (c *TCPConn) SetWriteDeadline(t time.Time) error

Edit
注意事項及びノウハウ

Edit
Go言語向けのパッケージ、ライブラリの一覧

Edit
TIPS

Edit
データベースのマイグレーション

#  wget https://github.com/golang-migrate/migrate/releases/download/v3.5.1/migrate.linux-amd64.tar.gz

※ Version は希望のもの。

専用のフォルダを作りSQLを記載したファイルを作成する。(ファイル名は、Version_名前.up.sql とする。)その後、下記コマンドで実行

# migrate -database 'mysql://user:pswd@tcp(localhost:3306)/db名' -path ./ up

userとpswdは、データベースのログインユーザー情報。db名はデータベース名。-path オプションは作成した、作成したSQLファイルの場所を指定。

※既存のDBからテーブル構造を取得するには、下記で可能。

mysql>  SHOW CREATE TABLE <テーブル名>;

添付ファイル: filesetting.png 396件 [詳細]