sidekiqをGracefulShutdownする
# >= 5.0.0 kill -TSTP [worker_pid] # < 5.0.0 kill -USR1 [worker_pid] # すると、 # 該当のworkerにキューが入らなくなる。 # psコマンドでみたときに stopping がつく。 sidekiq 4.2.10 app_name [0 of 8 busy] stopping # busyが0になったら、-TERM送って終了 kill -TERM [worker_pid]
goenv環境でdepを使うメモ
QuickStart
まずはこれでgoenvをインストールする。
http://kinosuke.hatenablog.jp/entry/2019/04/11/104837
# depのインストール # ※GOPATH以下にインストールされる go get -u github.com/golang/dep/cmd/dep # goenv環境でやるとdepコマンドのパスが通らないので、とりあえずパス指定で実行する。 # パスを通してもいいけど、goのバージョン切り替えるたびにパス変えるのめんどくさいかも。 # 現在の構成を解析して Gopkg.toml、Gopkg.lock を生成 "${GOPATH}/bin/dep" init # パッケージをvendorディレクトリ以下にインストール "${GOPATH}/bin/dep" ensure # 新しいパッケージを追加 "${GOPATH}/bin/dep" ensure -add github/gin-gonic/gin # パッケージをアップデート "${GOPATH}/bin/dep" ensure -update github/gin-gonic/gin # 現在の状態を出力する "${GOPATH}/bin/dep" status
RspecでSQLやRailsのログを出力したい
こんな感じで
# spec/rails_helper.rb # 略 Rails.logger = Logger.new(STDOUT) # Rails.loggerを出す ActiveRecord::Base.logger = Logger.new(STDOUT) # SQLログ出す RSpec.configure do |config| # 略 end
※STDOUTは組み込み変数
STDOUT.puts "hogehoge"
のようにすれば標準出力される。
GoModulesを使ってパッケージ管理する
QuickStartメモ
# プロジェクトディレクトリを掘る mkdir -p github.com/kinosuke01/gin-tutorial cd github.com/kinosuke01/gin-tutorial/ # 初期化する → go.modというパッケージリストを保持するファイルができる go mod init github.com/kinosuke01/gin-tutorial/ go: creating new go.mod: module github.com/kinosuke01/gin-tutorial/ # main.go書いてみる cat main.go ===== package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { fmt.Println("hello!!!") r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run() // listen and serve on 0.0.0.0:8080 } ===== # ビルドする # 必要なパッケージが ${GOPATH}/pkg/* 以下に自動でインストールされてbuildされる。 # パッケージリストが go.mod に追記される go build main.go # 実行ファイルを実行する ./main hello!!! [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 :8080
参考にさせていただきました。 https://qiita.com/propella/items/e49bccc88f3cc2407745
aws-cliで複数のプロファイル設定を持つ
複数のAWSアカウントを扱うことになったので、aws-cliのプロファイルの使い方をメモしておく。
# 設定やプロファイル一覧を見る cat ~/.aws/config cat ~/.aws/credentials # デフォルトの設定をする aws configure AWS Access Key ID [None]: xxx AWS Secret Access Key [None]: xxx Default region name [None]: ap-northeast-1 Default output format [None]: json # プロファイルの設定をする aws configure --profile user1 AWS Access Key ID [None]: xxx AWS Secret Access Key [None]: xxx Default region name [None]: ap-northeast-1 Default output format [None]: json # 現在使用しているプロファイルの確認 aws configure list # 指定のプロファイルの確認 aws configure list --profile user1 # 一時的にプロファイルを切り替えて実行 aws s3 ls --profile user1 # デフォルト変更 export AWS_PROFILE=user1 aws configure list
Goことはじめ(並列処理)
http://gihyo.jp/dev/feature/01/go_4beginners/0005?page=1
より学んだことメモ。
package main import ( "fmt" "log" "net/http" "sync" ) func main() { wait := new(sync.WaitGroup) urls := []string{ "https://www.example.com", "https://www.example.co.jp", "https://www.example.com/", "https://www.example.com/jp/", } for _, url := range urls { // waitGroup に追加(カウントを増やす) wait.Add(1) // 取得処理をゴルーチン(軽量スレッド)で実行 // goと書くことで、並列に処理を実行できる go func(url string) { res, err := http.Get(url) if err != nil { log.Fatal(err) } defer res.Body.Close() fmt.Println(url, res.Status) // waitGroupから削除(カウントを減らす) wait.Done() }(url) } // カウントが0になるまで待つ wait.Wait() }
チャネルを使ってゴルーチン間でやりとりする
package main import ( "fmt" "log" "net/http" ) func main() { urls := []string{ "https://www.example.com", "https://www.example.co.jp", "https://www.example.com/", "https://www.example.com/jp/", } // stringを扱うチャネルを作成 // これでゴルーチン間のメッセージのやり取りができる statusChan := make(chan string) for _, url := range urls { go func(url string) { res, err := http.Get(url) if err != nil { log.Fatal(err) } defer res.Body.Close() // 結果をチャネルに書き込む statusChan <- res.Status }(url) } for i := 0; i < len(urls); i++ { // チャネルからstringを読み出す // 書き込みがされるまで待つ fmt.Println(<-statusChan) } }
チャネルを返す関数に切り出す版
package main import ( "fmt" "log" "net/http" ) // // 関数でチャネルを作成 // func getStatus(urls []string) <-chan string { statusChan := make(chan string) for _, url := range urls { go func(url string) { res, err := http.Get(url) if err != nil { log.Fatal(err) } defer res.Body.Close() statusChan <- res.Status }(url) } // チャネルを返す return statusChan } func main() { urls := []string{ "https://www.example.com", "https://www.example.co.jp", "https://www.example.com/", "https://www.example.com/jp/", } statusChan := getStatus(urls) for i := 0; i < len(urls); i++ { fmt.Println(<-statusChan) } }
select文を用いたイベント制御
package main import ( "fmt" "log" "net/http" "time" ) // // 関数でチャネルを作成 // func getStatus(urls []string) <-chan string { statusChan := make(chan string) for _, url := range urls { go func(url string) { res, err := http.Get(url) if err != nil { log.Fatal(err) } defer res.Body.Close() statusChan <- res.Status }(url) } // チャネルを返す return statusChan } // // select文を用いたイベント制御 // func selectFunc() { // 1秒後に値が読み出せるチャネル timeout := time.After(time.Second) urls := []string{ "https://www.example.com", "https://www.example.co.jp", "https://www.example.com/", "https://www.example.com/jp/", } statusChan := getStatus(urls) LOOP: // 任意のラベル名を指定 for { // selectのcaseに指定すると // いずれかのcaseの操作が行われたときに // 該当する処理が実行される // caseが実行されるまで後の処理は待つ select { case status := <-statusChan: // 受信データを表示 fmt.Println(status) case <- timeout: // このfor/selectを抜ける // breakだけだとselectだけ抜けるためラベルを指定 break LOOP } } } func main() { selectFunc() }
同時期同数制御
チャネルバッファを使って、ゴルーチンの同時期同数を制御する。
package main import ( "fmt" "log" "net/http" ) // サイズ0の構造体 // limitチャネルに入ればなんでもいい var empty struct{} func getStatus(urls []string) <-chan string { statusChan := make(chan string, 3) // バッファを5にして生成 limit := make(chan struct{}, 5) go func() { for _, url := range urls { select { case limit <- empty: // limitに書き込みが可能な場合は取得処理を実施 go func(url string) { // このゴルーチンは同時に5つしか起動しない res, err := http.Get(url) if err != nil { log.Fatal(err) } statusChan <- res.Status // 終わったら1つ読み出して空きを作る <-limit }(url) } } }() return statusChan } func main() { urls := []string{ "https://example.com", "https://example.com", "https://example.com", "https://example.com", "https://example.com", "https://example.com", } statusChan := getStatus(urls) for i := 0; i < len(urls); i++ { fmt.Println(<-statusChan) } }
Goことはじめ(net/httpパッケージ)
http://gihyo.jp/dev/feature/01/go_4beginners/0004?page=3
より学んだことメモ。
hello worldを返すだけのhttpサーバ
package main import ( "fmt" "net/http" ) // // http.ResponseWriterがレスポンスデータ // *http.Requestはリクエストデータ // func IndexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "hello world") } func main() { // ルーティング設定 http.HandleFunc("/", IndexHandler) // ポートを割り当ててサーバ起動 http.ListenAndServe(":3000", nil) }
JSONをPOSTでうけてファイルに保存する
package main import ( "encoding/json" "fmt" "log" "net/http" "os" ) type Person struct { ID int `json:"id"` Name string `json:"name"` } // // http.ResponseWriterがレスポンスデータ // *http.Requestはリクエストデータ // func IndexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "hello world") } func PersonHandler(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() // 処理の最後にBodyを閉じる if r.Method == "POST" { // リクエストボディをJSONに変換 var person Person decoder := json.NewDecoder(r.Body) err := decoder.Decode(&person) if err != nil { // エラー処理 log.Fatal(err) } // ファイル名を {id}.txt とする filename := fmt.Sprintf("%d.txt", person.ID) file, err := os.Create(filename) // ファイルを生成 if err != nil { log.Fatal(err) } defer file.Close() // ファイルにNameを書き込む _, err = file.WriteString(person.Name) if err != nil { log.Fatal(err) } // レスポンスデータとしてステータスコード201を返す w.WriteHeader(http.StatusCreated) } } func main() { // ルーティング設定 http.HandleFunc("/", IndexHandler) http.HandleFunc("/persons", PersonHandler) // ポートを割り当ててサーバ起動 http.ListenAndServe(":3000", nil) }
GETでうけてtemplateを使ってHTMLを返す
package main import ( "encoding/json" "fmt" "log" "net/http" "os" "strconv" "html/template" "io/ioutil" ) type Person struct { ID int `json:"id"` Name string `json:"name"` } // テンプレートのコンパイル // Mustを使うとエラー時にパニックを起こす // var t, err := template.personFiles("index.html") var t = template.Must(template.ParseFiles("index.html")) // // http.ResponseWriterがレスポンスデータ // *http.Requestはリクエストデータ // func IndexHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "hello world") } func PersonHandler(w http.ResponseWriter, r *http.Request) { defer r.Body.Close() // 処理の最後にBodyを閉じる if r.Method == "POST" { // リクエストボディをJSONに変換 var person Person decoder := json.NewDecoder(r.Body) err := decoder.Decode(&person) if err != nil { // エラー処理 log.Fatal(err) } // ファイル名を {id}.txt とする filename := fmt.Sprintf("%d.txt", person.ID) file, err := os.Create(filename) // ファイルを生成 if err != nil { log.Fatal(err) } defer file.Close() // ファイルにNameを書き込む _, err = file.WriteString(person.Name) if err != nil { log.Fatal(err) } // レスポンスデータとしてステータスコード201を返す w.WriteHeader(http.StatusCreated) } else if r.Method == "GET" { // パラメータを取得 id, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil { log.Fatal(err) } filename := fmt.Sprintf("%d.txt", id) b, err := ioutil.ReadFile(filename) if err != nil { log.Fatal(err) } // personを生成 person := Person { ID: id, Name: string(b), } // レスポンスにエンコーディングしたHTMLを書き込む t.Execute(w, person) } } func main() { // ルーティング設定 http.HandleFunc("/", IndexHandler) http.HandleFunc("/persons", PersonHandler) // ポートを割り当ててサーバ起動 http.ListenAndServe(":3000", nil) }
データを埋め込むには{{ }}で値をくくり,その中に埋め込みたいプロパティを指定する。
<!DOCTYPE html> <title>person</title> <h1>{{ .ID }} : {{ .Name }}</h1>