Below you will find pages that utilize the taxonomy term “Go”
VS Codeで利用するGoのバージョンを指定する方法
VS CodeのGo拡張機能を利用していると、新しいバージョンのgoがありますと良く言われます。 新しいバージョンを使うには以下の2通りの方法が考えられます。
- 古いバージョンを新しいバージョンで上書きする
- 古いバージョンを残したまま新しいバージョンを別名で用意する
後者の別名を用意する場合、どのバージョンを使うかをどこかで明示的に指定する必要があります。 プロジェクト毎にバージョンを指定する場合はgo build する際のコマンド指定で対応しても良いかもしれませんが、 一括で変更するには、VS Code側(Go拡張機能)で設定することもできます。 以下のようにsettings.jsonに記載することで使うバージョンを指定できます。
"go.alternateTools": {
"go": "/home/user/go/bin/go1.16.2"
},
肝心の別名での用意の仕方は以下です。
go install golang.x/dl/go1.16.2
go1.16.2 download
GoのEchoフレームワークのsessionの無効化
GoのWebフレームワークのEchoを使っています。Echoにはミドルウェアとしてsessionというものがあり、これを使ってセッション管理を行うことができるようになっています。 このsessionミドルウェアはバックエンドとして、gorilla/sessionsを使っています。
この仕組みを使ってセッションを実装したときに、クライアントのクッキーにセッション情報が記録されます。 これをサーバ側で無効にしたい場合等に、セッションキーをサーバ側で変えてしまうということができます。 セッションキーを変更した後で、クライアントが接続すると古いセッション情報を使うために、これは無効と判定されます。 このとき、実現したい挙動としては、古いセッション情報をクッキーから削除して新しいセッション情報を作成できるようにすることです。
Echoでは、以下のような書き方でセッション情報を取得することができます。 しかし、Getに対応するDeleteというような関数は用意されていません。
sess, err = session.Get("session", c)
セッションが存在しない場合(新規の場合)は、session.Get()でセッションが新規作成されますが、無効なセッション情報である場合には、session.Get()の結果エラーとなり有効なセッション情報が得られません。 そのため、無効なセッション情報は削除してからsession.Get()を実行する必要があります。 session.Get()がエラーになった場合に削除するということも考えましたが、処理を継続したい場合はsession.Get()より前に削除してある必要があります。 処理を継続できなくて良い場合は、削除後に再度HTTPリクエストを出してもらうように促すことで対処できます。
削除の方法として、HTTPレスポンスを返す以外に、JavaScriptで削除する方法も考えられますが、HTTP Only属性を有効にすべき種類のものなのでこれはできません。 そのため、HTTPレスポンスとしてクッキー削除のヘッダを返すことで削除する方法を採用します。 このとき注意が必要なのが、HTTPレスポンスのキャッシュです。 キャッシュが有効になっていると、HTTPクライアントはコンテンツのみをキャッシュから読み込み、HTTPヘッダによるクッキー削除が行われません。 そのため、クッキー削除のヘッダを付ける可能性のあるURLはキャッシュ無効のヘッダも併せて返す必要があります。
つまり、以下のようなヘッダを返す必要があるということです。 もちろん、動的に生成するページの場合はCache-Controlヘッダを付けなくてもキャッシュされないので、Cache-Controlヘッダは不要です。
Cache-Control: no-store Set-Cookie: session=; Path=/; Max-Age=0
もし、ほかに良いやり方があるようでしたら教えてもらえると嬉しいです。
Goのnet/httpでWebサーバを作って確認したこと
Go のnet/httpパッケージを使うと簡単にWebサーバが構築できます。 実際に動作を確認した内容について備忘録としてメモしています。
並列処理可能
特に何も工夫せずにWebサーバとして動作させるだけで、goroutineによる並列処理が可能です。
動作確認のため、Apache Bench(ab)を使って確認しましたが、1000セッション同時に処理できていました。
動作確認の際は、リクエストの応答にsleepを1秒入れて1000セッションが10リクエストを発行しました。
すると、10秒くらいですべてのリクエストが処理されました。
動作確認に使ったコマンドは以下です。
実行すると約1秒毎に1000リクエストが処理されて、約10秒後に完了します。
結果のサマリを見ても、ほとんどのリクエストが約1秒で完了していました。
ab -c 1000 -n 10000 http://localhost:8080/
レスポンスのContent-Type自動設定
レスポンスをhtmlにすると、自動的にContent-Typeがtext/htmlになりました。
レスポンスをダブルクォートで囲んだ文字列にすると、中身がhtmlでもtext/plainになりました。
強制的に変更したいときは、以下のようにして指定できます。
w.Header().Set("Content-Type", "text/html; charset=utf-8")
コード
実際の動作確認の際は、不要なものを削除したりコメントアウトしたりしながら実施していますが、
上記の確認に必要なものはこれで足りると思います。
package main
import (
"fmt"
"html"
"log"
"net/http"
"time"
)
func main() {
http.HandleFunc("/", uploader)
log.Fatal(http.ListenAndServe(":8080", nil))
}
func uploader(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-type" , "text/html; charset=utf-8")
time.sleep(1 * time.Second)
fmt.Fprintf(w, "%s", html.EscapeString(r.URL.Path)
}
A Tour of Go の split関数
A Tour of GoというGo言語のチュートリアルがあるのでそれを見ていたら、サンプルコードにsplit関数というのがあった。 split関数に17という値を渡すと、7と10の二つの値を戻り値として返す関数なのだが、1の位と10の位に分けられたの!?と驚いたので整理。
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
上記関数に17を与えると x = 7, y = 10 のそれぞれの値が返る。 x は4/9を掛けているだけで、yはxを引くだけ。なんでそれで7と10に分けられるのか。。 単純に計算すると以下の通り。
x = 17 * 4 / 9
= 68 / 9
= 7 余り 5
= 7 (intなので切り捨て)
y = 17 - 7
= 10
ふーむ。
一つ目のxの式をyの式に代入してみる。
y = sum - sum * 4 / 9
= sum * 5 / 9
sum = 17
y = 17 * 5 / 9
= 9 余り 4
= 9(intなので切り捨て)
!= 10
一致しない。 今度はsumの式として二つ目の式を一つ目の式に代入。
Goはじめ
Goを触ることにしたので、色々なドキュメントを読んだメモを書こうと思って始めたものの二つで力尽きたので続きはまた別に。
Go のインストール
tarボールを展開するだけでとりあえず終わり。 GOPATH環境変数は設定しない場合は/goが設定される。設定しなくても良い。 /go以外にしたい場合に設定すれば良いとのこと。
インストールした後に、普通設定すべきこととしてパスの設定がある。
export PATH=$PATH:$(go env GOPATH)/bin
https://golang.org/doc/install
Go の基本
以下のURLに基本が書いてあるのでとりあえずこれを読むのが良さそう。
https://golang.org/doc/code.html
$GOPATH(~/go/)が唯一のワークスペース。複数作らないで一つにする。と書いてある。 Go以外の言語では、プロジェクト毎にワークスペースがあるけれども、Goでは単一のワークスペースであることが違い。 そこ代わりに、単一のワークスペースに複数のリポジトリが含まれており、さらにそれらは複数のパッケージを含む。 リポジトリというのはバージョン管理の対象の単位。.gitディレクトリが存在するディレクトリのこと。 パッケージとは、ディレクトリ一つ毎にパッケージ一つであり、例えばパッケージ毎に異なる実行ファイルを提供する。 パッケージは複数のソースファイルを含んで良いが、ディレクトリは単一。
ワークスペースには、src/とbin/というディレクトリがあり、src/配下に複数のリポジトリを配置する。bin/には、それらの実行ファイルを配置する。
ワークスペースにはシンボリックリンクは使うべきではないとのこと。 現在のワークスペースは以下のコマンドで確認できる。
go env GOPATH
パッケージパス
自分で作ったGoソースファイルや誰かの作ったパッケージ等は、$GOPATH/src/の下のディレクトリパスを使って指定する。 fmtパッケージのような標準パッケージは短い。自分でパッケージを作成するときは将来的にも標準パッケージと同じにならないように注意する必要がある。ただし、github.comで公開するような場合は、github.com/ユーザ名 がそのベースパスとなり、その下にパッケージ名が続くようになる。
build and install
ソースファイルをビルドしてインストールするには、 go install コマンドを使う。 パッケージパスを引数に指定するか、パッケージディレクトリにcdしてからコマンドを実行する。
go install github.com/hseiyo/hello
or
cd $GOPATH/src/github.com/hseiyo/hello
go install
installできたコマンドは実行可能。 GOPATH/binをPATHに追加してあればコマンド名だけで実行可能。 go build コマンドを代わりに使うと、ビルドして実行ファイルは作成されるが、$GOPATH/binにはコピーされずにパッケージディレクトリに作成される。これも実行可能ではあるが、このパッケージが実行コマンドではなくライブラリの場合にはinstallではなく、buildを実行してパッケージディレクトリにバイナリファイルを置いておく。 他のソースファイルからimportする際に利用される。
パッケージ名
ソースファイルには必ず以下のようにようにしてパッケージ名を指定する。 同一のパッケージに所属するソースファイルはすべて同じパッケージ名を指定する。これは必ず指定が必要。 ディレクトリ名と同じ文字列をパッケージ名として指定する。
パッケージ名はほかのパッケージと同じものを利用しても問題ないが、インポートパス名は異なる必要がある。
テスト用ソースファイル
_test.goで終わるファイル名のファイルはテスト用に使用する。
testingパッケージをインポートした上で、Test<関数名>(t *testing.T)という関数を定義する。 そして以下のコマンドを実行する。
go test github.com/hseiyo/hello
or
cd $GOPATH/src/github.com/hseiyo/hello
go test
go get
例えば以下のコマンドは、リモートからファイルを取得(ダウンロード)して、ビルドしてインストール($GOPATH/bin/へのコピー)を行う。