なんかするコマンド

  • 一定の間隔でなんかしたいときは time.Ticker が便利
ticker.go
package main

import (
    "flag"
    "fmt"
    "os/exec"
    "strings"
    "time"
)

var (
    s = flag.Int("s", 1, "unit seconds")
    c = flag.Int("c", 1, "unit count")
    d = flag.Int("d", 1, "duration seconds")
    e = flag.String("e", "", "execute command")
)

func main() {
    flag.Parse()

    ticker := time.NewTicker(time.Second * time.Duration(*s) / time.Duration(*c))
    defer ticker.Stop()

    done := make(chan bool)
    go func() {
        time.Sleep(time.Second * time.Duration(*d))
        done <- true
    }()

    e := strings.Split(*e, " ")

    for {
        select {
        case <-done:
            return
        case <-ticker.C:
            if out, err := exec.Command(e[0], e[1:]...).Output(); err != nil {
                panic(err)
            } else {
                fmt.Printf("%s", out)
            }
        }
    }
}

やる

# 1 秒に 10 回のペースで 5 秒間 echo hello world する
$ go run ticker.go -s 1 -c 10 -e "echo hello world" -d 5

# 1 秒に 1000 回ペースを指定するが exec が遅いので自分の環境だと秒間 360 回程度しか実行できない
$ go run ticker.go -s 1 -c 1000 -e "echo hello world" -d 5 | wc
1837    3674   22044

Go でなんかする

  • コマンド実行だと遅いので実処理部分を Go で実装する
  • ここでは任意のファイルに任意の長さのログを書き出すスクリプトを作る
ticker2.go
package main

import (
    "bytes"
    "flag"
    "os"
    "time"
)

var (
    s = flag.Int("s", 1, "unit seconds")
    c = flag.Int("c", 1, "unit count")
    d = flag.Int("d", 1, "duration seconds")
    f = flag.String("f", "", "log filename")
    l = flag.Int("l", 1, "log length")
)

func main() {
    flag.Parse()

    ticker := time.NewTicker(time.Second * time.Duration(*s) / time.Duration(*c))
    defer ticker.Stop()

    fd, err := os.OpenFile(*f, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
    if err != nil {
        panic(err)
    }
    defer fd.Close()
    txt := append(bytes.Repeat([]byte("0"), *l), []byte("\n")...)

    done := make(chan bool)
    go func() {
        time.Sleep(time.Second * time.Duration(*d))
        done <- true
    }()

    for {
        select {
        case <-done:
            return
        case <-ticker.C:
            if _, err := fd.Write(txt); err != nil {
                panic(err)
            }
        }
    }
}

Go でやる

# 1000 件/秒で 1 秒間 500(499 + 改行) バイトのログを out.log ファイルに出力する
$ go run ticker.go -s 1 -c 1000 -d 1 -f out.log -l 499

# 毎秒 1000 件程度は楽勝
$ wc out.log
    1000    1000  500000 out.log

# 100000 件/秒で 1 秒間 500 バイトのログを out.log ファイルに出力する
$ go run ticker.go -s 1 -c 100000 -d 1 -f out2.log -l 499

# 自分の環境だと毎秒 7, 8 万件程度まで実行できた
$ wc out2.log
   72853   72853 36426500 out2.log

どっかでなんかやる

  • Go なので Docker や Kubernetes 環境とかの Linux 向けにポータブルにビルドして実行するのが楽
# Linux 向けにビルド
GOOS=linux GOARCH=amd64 go build ticker.go

# Docker で実行
docker cp ticker $CONTAINER_ID:/path/to/ticker
docker exec $CONTAINER_ID sh -c '/path/to/ticker -s 1 -c 10 -e "echo hello world" -d 5'

# Kubernetes で実行
kubectl cp ./ticker $POD_NAME:/path/to/ticker
kubectl exec $POD_NAME -- /path/to/ticker -s 1 -c 10 -e "echo hello world" -d 5
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.