コピペコードで快適生活

明日使えるソースを自分のために

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]

sidekiq を安全に止める - Qiita より

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

参考
Goオフィシャルチーム作成の依存関係管理ツール dep を試してみた | DevelopersIO

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>