Jex’s Note

Golang 其他

Cpu Usage

package main

import (
    "fmt"
    "io/ioutil"
    "strconv"
    "strings"
    "time"
)

func getCPUSample() (idle, total uint64) {
    contents, err := ioutil.ReadFile("/proc/stat")
    if err != nil {
        return
    }
    lines := strings.Split(string(contents), "\n")
    for _, line := range lines {
        fields := strings.Fields(line)
        if fields[0] == "cpu" {
            numFields := len(fields)
            for i := 1; i < numFields; i++ {
                val, err := strconv.ParseUint(fields[i], 10, 64)
                if err != nil {
                    fmt.Println("Error: ", i, fields[i], err)
                }
                total += val // tally up all the numbers to get total ticks
                if i == 4 {  // idle is the 5th field in the cpu line
                    idle = val
                }
            }
            return
        }
    }
    return
}

func main() {
    idle0, total0 := getCPUSample()
    time.Sleep(3 * time.Second)
    idle1, total1 := getCPUSample()

    idleTicks := float64(idle1 - idle0)
    totalTicks := float64(total1 - total0)
    cpuUsage := 100 * (totalTicks - idleTicks) / totalTicks

    fmt.Printf("CPU usage is %f%% [busy: %f, total: %f]\n", cpuUsage, totalTicks-idleTicks, totalTicks)
}

執行 command

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    text := "hello world"
    cmd := exec.Command(
        "echo",
        text,
        ">",
        "test.txt")

    fmt.Println(cmd.Args)

    out, err := cmd.Output()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(string(out))
}

result :

[echo hello world > test.txt]
hello world > test.txt

progress bar

package main
import (
  "fmt"
  "strings"
  "time"
  "os"
)
func main() {
  for i := 0; i < 50; i++ {
    time.Sleep(100 * time.Millisecond)
    h := strings.Repeat("=", i) + strings.Repeat(" ", 49-i)
    fmt.Printf("\r%.0f%%[%s]", float64(i)/49*100, h)
    os.Stdout.Sync()
  }
  fmt.Println("\nAll System Go!")
}

讓字一個一個 print 出來

package main

import (
    "bufio"
    "fmt"
    "os"
    "time"
)

func main() {
    text := "快來玩玩吧! Try it out!"
    printSlowly(text, 200)

    bufio.NewReader(os.Stdin).ReadBytes('\n')
}

func printSlowly(text string, speed int) {
    text_rune := []rune(text)

    for i := 0; i < len(text_rune); i++ {
        time.Sleep(time.Duration(speed) * time.Millisecond)
        fmt.Printf("\r%s", string(text_rune[0:i+1]))
        os.Stdout.Sync()
    }
    fmt.Print("\n")
}

Dump (for debug) - spew

a := map[string]int64{}
a["A"] = 1
a["B"] = 2
debug.Dump(a)

執行結果 :

(map[string]int64) (len=2) {
 (string) (len=1) "A": (int64) 1,
 (string) (len=1) "B": (int64) 2
}

curl

_, str := curl.String("http://www.google.com")
fmt.Println(str)

gorequest

agent := gorequest.New().CustomMethod("POST", "url").Timeout(30 * time.Second)
agent.Header = header               // 將 HEADER 以 map 型態傳進去
agent.Send(post_data)               // POST 需要, GET 不用, 傳入 String
resp, body, errs := agent.End()
if errs != nil {
    // Do something
}

if resp.StatusCode != 200 {
    // Do something
}

MySQL

(last updated at 2016-12-21)

Connect

conn, err = sql.Open("mysql", "root:password@tcp(127.0.0.1:3306)/db_name")
或
conn, err = sql.Open("mysql", "root:password@/db_name")
if err != nil {
    os.Exit(1)
}
err = conn.Ping()
if err != nil {
    os.Exit(1)
}
defer db.Close()

Prepare (prevent sql injection)

有兩種寫法,第一種是用 Query;第二種是先 PrepareQueryExec

SELECT with Query (with Prepare)

rows, err := conn.Query("SELECT name FROM users WHERE age=?", req.FormValue("age)
defer rows.Close()
if rows.Next() {
    var name string
    err = rows.Scan(&name)
} else {
    // no data
}

SELECT with Prepare

stmt, err := conn.Prepare("SELECT name, address FROM user WHERE age = ? AND name = ?")
defer stmt.Close()
rows, err := stmt.Query(27, "jex") // replace the params
defer rows.Close()
for rows.Next() {
    var name, address string
    err = rows.Scan(&name, &address)
    fmt.Println(name, address)
}

使用 Query 要注意 :

// this is safe
conn.Query("SELECT name FROM users WHERE age=?", req.FormValue("age)

// this allows sql injection.
conn.Query("SELECT name FROM users WHERE age=" + req.FormValue("age"))

UPDATE with Prepare

stmt, err := conn.Prepare("UPDATE user SET name = ?, age = ? WHERE id = ?")
defer stmt.Close()
res, err := stmt.Exec("jex", 27, 123)   // replace the params
num, err := res.RowsAffected()          // 判斷是否有成功影響欄位的值
fmt.Println(num)
// where 不到,num 是 0
// where 到,但資料沒變也是 0
// where 到,但資料有變是 1

CRUD 操作

SELECT 取第一筆

var str string
err = conn.QueryRow("SELECT id FROM user").Scan(&str)

SELECT 多筆 (fetch every single row)

var id int
var name string
rows, err := conn.Query("SELECT id, name FROM user")
defer rows.Close()
for rows.Next() {
    err := rows.Scan(&id, &name)    // do check
    fmt.Println(id, name)
}
err = rows.Err()        // do check

Query Row 取第一筆

var name string
err = db.QueryRow("SELECT name FROM users WHERE id = ?", 1).Scan(&name)

stmt, err := db.Prepare("SELECT name FROM users WHERE id = ?")
var name string
err = stmt.QueryRow(1).Scan(&name)

INSERT (TODO)

stmt, err := db.Prepare("INSERT ... ")
res, err := stmt.Exec("A", "B")
id, err := res.LastInsertId()

UPDATE (TODO)

stmt, err = db.Prepare("UPDATE ...")
res, err = stmt.Exec("A", id)
affect, err := res.RowsAffected()

DELETE (TODO)

stmt, err = db.Prepare("DELETE ...")
res, err = stmt.Exec(id)
affect, err = res.RowsAffected()

ORM

package main

import (
    "database/sql"
    "github.com/coopernurse/gorp"
    _ "github.com/go-sql-driver/mysql"
    "log"
)

func main() {
    // initialize the DbMap
    dbmap := initDb()
    defer dbmap.Db.Close()

    err := dbmap.Insert(&Tt{Name: "test"})
    checkErr(err, "Insert failed")
}

type Tt struct {
    Name string
}

func initDb() *gorp.DbMap {
    // connect to db using standard Go database/sql API
    // use whatever database/sql driver you wish
    db, err := sql.Open("mysql", "root:password@/go_test")
    checkErr(err, "sql.Open failed")
    dbmap := &gorp.DbMap{Db: db, Dialect: gorp.MySQLDialect{}}
    dbmap.AddTableWithName(Tt{}, "tt")
    return dbmap
}

func checkErr(err error, msg string) {
    if err != nil {
        log.Fatalln(msg, err)
    }
}

unix

調整系統設定最大讀寫檔案數量 (for currency)

unix 預設一個 process 最多可開 1024 個檔案,也就是能出去的 network currency 數量是 1024

import "golang.org/x/sys/unix"

var rLimit unix.Rlimit
var max_rlimit uint64 = 50000
var cur_rlimit uint64 = 50000
rLimit.Max = max_rlimit
rLimit.Cur = cur_rlimit
err := unix.Setrlimit(unix.RLIMIT_NOFILE, &rLimit)
if err != nil {
    log.Println("Setting Rlimit Error:", err)
}

err = unix.Getrlimit(unix.RLIMIT_NOFILE, &rLimit)
if err != nil {
    log.Println("Getting Rlimit Error:", err)
.

fasthttp

效能比原生的 http 好

import (
    "github.com/buaazp/fasthttprouter"
    "github.com/valyala/fasthttp"
)

router := fasthttprouter.New()
router.GET("/", Index)
fasthttp.ListenAndServe(":8010", router.Handler)

func Index(ctx *fasthttp.RequestCtx, ps fasthttprouter.Params) {
    // `Get` num params
    n := string(ctx.FormValue("num"))

    // 輸出
    fmt.Fprintf(ctx, "hello, %s!n", ps.ByName("name"))
}

帶入 net.Listener 的方式

l, err := net.Listen("tcp", ":"+strconv.Itoa(port))
if err != nil {
    fmt.Println("net.Listen error: %v", err)
    os.Exit(1)
}
router := fasthttprouter.New()
router.GET("/", Index)
err = fasthttp.Serve(l, router.Handler)
if err != nil {
    fmt.Println(err)
    os.Exit(1)
}

go-logging

一套 log 的 package 特色如下 :

  • 可以自由的設計 log format
  • 可以自動地紀錄 call log 時當時是在什麼 package 及 func, 在 debug 時相當好用
  • 可以設定當 log 達到什麼層級時另外紀錄下來
  • 輸出不同 level 的 log 時有顏色

Example :

// 標準輸出的 log
logger = logging.MustGetLogger("example")
format := logging.MustStringFormatter(`%{color}%{time:15:04:05} %{shortpkg} %{shortfunc} [%{level}] %{color:reset} %{message}`)
backend := logging.NewLogBackend(os.Stderr, "", 0)
backendFormatter := logging.NewBackendFormatter(backend, format)

// 如果發生 Error 層級以上的 log 另外做處理
// 如果 buffer 有值就 post 到 slack
var buf bytes.Buffer
go func() {
    // 攔截 log 的內容送到 slack
    for {
        if buf.Len() > 0 {
            fmt.Println(buf.String())   // 這段可以改成 post 到 slack
            buf.Reset()                 // 將 buffer 清空
        }
        time.Sleep(1 * time.Second)     // 每秒檢查一次
    }
}()
// 將會進到 backend2 的 log 暫存到 buffer
backend2 := logging.NewLogBackend(&buf, "", 0)
format2 := logging.MustStringFormatter(`%{time:15:04:05} %{shortpkg} %{shortfunc} [%{level}] %{message}`)
backend2Formatter := logging.NewBackendFormatter(backend2, format2)
backend2Leveled := logging.AddModuleLevel(backend2Formatter)
// 設定什麼層級的 log 會進到 backend2
backend2Leveled.SetLevel(logging.ERROR, "")

// 註冊
logging.SetBackend(backendFormatter, backend2Leveled)

Mux - API Router

func main() {
    l, _ := net.Listen("tcp", ":3333")
    r := mux.NewRouter()
    r.HandleFunc("/", Index)
    log.Fatal(http.Serve(l, r))
}

func Index(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Time: " + time.Now().Format(time.RFC1123)))
}

Comments