golang常用標(biāo)準(zhǔn)庫(kù)


輸入與輸出-fmt包

輸入與輸出

  • 常用輸出函數(shù)

    Print晰搀、Printf、Println:直接輸出內(nèi)容

    Sprint办斑、Sprintf外恕、Sprintln:生成內(nèi)容并返回字符串

    Fprint:將內(nèi)容輸出到一個(gè)io.Writer接口類型的變量,經(jīng)常用于寫入文件

    Errorf:根據(jù)format參數(shù)生成格式化字符串并返回一個(gè)包含該字符串的錯(cuò)誤

```go
fmt.Println("打開(kāi)文件出錯(cuò)乡翅,err:", err)//輸出帶換行
s2 := fmt.Sprintf("name:%s,age:%d", name, age)//帶格式生成并返回
fmt.Fprintf(fileObj, "往文件中寫如信息:%s", name)//帶格式寫入文件
err := fmt.Errorf("這是一個(gè)錯(cuò)誤")
```
  • 常用占位符

    占位符 說(shuō)明
    %v 值的默認(rèn)格式表示
    %+v 類似%v鳞疲,但輸出結(jié)構(gòu)體時(shí)會(huì)添加字段名
    %#v 值的golang語(yǔ)法表示
    %T 打印值的類型
    %% 百分號(hào)
    %d 表示10進(jìn)制數(shù)
    %b 表示2進(jìn)制數(shù)
    %f 浮點(diǎn)數(shù),有小數(shù)
    %9.2f 寬度9蠕蚜,精度2
    %e 科學(xué)計(jì)數(shù)法
    %s 直接輸出字符串或[]byte
    %q 該值對(duì)應(yīng)的雙引號(hào)括起來(lái)的go語(yǔ)法字符串字面值尚洽,必要時(shí)會(huì)采用安全的轉(zhuǎn)義表示
    %p 指針,表示未16進(jìn)制靶累,并加上前綴0x
  • 常用輸入函數(shù)

    Scan腺毫、Scanf、Scanln:可以在程序運(yùn)行過(guò)程中從標(biāo)準(zhǔn)輸入獲取用戶的輸入挣柬。

    Scanln比較常用:在終端掃描標(biāo)準(zhǔn)輸入潮酒,以空格分隔,直到換行結(jié)束掃描

```go
fmt.Scanln(&name, &age, &married)
fmt.Printf("掃描結(jié)果 name:%s age:%d married:%t \n", name, age, married)
```

bufio.NewReader:獲取完整輸入內(nèi)容

FScan邪蛔、Fscanf急黎、Fscanln:從文件中獲取輸入

Sscan、Sscanf侧到、Sscanln:從字符串獲取輸入

時(shí)間與日期-time包

  • 時(shí)間與日期轉(zhuǎn)換

    func timeDemo() {
        now := time.Now() //獲取當(dāng)前時(shí)間
        fmt.Printf("current time:%v\n", now)
    
        year := now.Year()     //年
        month := now.Month()   //月
        day := now.Day()       //日
        hour := now.Hour()     //小時(shí)
        minute := now.Minute() //分鐘
        second := now.Second() //秒
        fmt.Printf("%d-%02d-%02d %02d:%02d:%02d\n", year, month, day, hour, minute, second)
    
        timestamp1 := now.Unix()     //時(shí)間戳
        timestamp2 := now.UnixNano() //納秒時(shí)間戳
        fmt.Printf("current timestamp1:%v\n", timestamp1)
        fmt.Printf("current timestamp2:%v\n", timestamp2)   
        
        timeObj := time.Unix(timestamp1, 0)//時(shí)間戳轉(zhuǎn)換成時(shí)間對(duì)象勃教,再通過(guò)類似以上當(dāng)前時(shí)間轉(zhuǎn)換成時(shí)間格式
    
    }
    
  • 時(shí)間間隔(單位)

    time包中時(shí)間間隔的定義:

    const (
        Nanosecond  Duration = 1
        Microsecond          = 1000 * Nanosecond
        Millisecond          = 1000 * Microsecond
        Second               = 1000 * Millisecond
        Minute               = 60 * Second
        Hour                 = 60 * Minute
    )
    
    var _time = 10 * time.Second //10秒
    

    例如:time.Duration表示1納秒,time.Second表示1秒匠抗。

  • 時(shí)間格式化

    golang的常用時(shí)間格式化模板并不是常見(jiàn)的:Y-m-d H:i:s荣回,而是2006-01-02 15:04 ,這是24小時(shí)制戈咳,2006-01-02 03:04 PM心软,則是12小時(shí)制

    fmt.Println(now.Format("2006-01-02 15:04:05.000 Mon Jan"))
    // 12小時(shí)制
    fmt.Println(now.Format("2006-01-02 03:04:05.000 PM Mon Jan"))
    fmt.Println(now.Format("2006/01/02 15:04"))
    fmt.Println(now.Format("15:04 2006/01/02"))
    fmt.Println(now.Format("2006/01/02"))
    

    golang大佬壕吹,何必呢,增加跨語(yǔ)言語(yǔ)法記憶難度删铃,雖然只是一點(diǎn)點(diǎn)耳贬,但如果其他地方也這樣有'意思',累計(jì)下難度就不小了猎唁。

  • 時(shí)間操作

    golang中的時(shí)間咒劲,并不是簡(jiǎn)單的數(shù)字加減,time包提供了實(shí)踐操作的方法:

    • Add:時(shí)刻+時(shí)間段

      func main() {
          now := time.Now()
          later := now.Add(time.Hour) // 當(dāng)前時(shí)間加1小時(shí)后的時(shí)間
          beforer := now.Add(-time.Hour) // 當(dāng)前時(shí)間減1小時(shí)后的時(shí)間
          fmt.Println(later)
      }
      
    • Sub:時(shí)刻1 - 時(shí)刻2诫隅,求兩個(gè)時(shí)間的差值腐魂,注意這里并不是(時(shí)刻-時(shí)間段)的實(shí)現(xiàn),(時(shí)刻-時(shí)間段)仍然可以用Add(-時(shí)間段)來(lái)實(shí)現(xiàn)

    • Equal:判斷時(shí)間是否相等逐纬,會(huì)考慮時(shí)區(qū)

    • Before:判斷是否在某個(gè)時(shí)刻之前

    • After:判斷是否在某個(gè)時(shí)刻之后

  • 定時(shí)器

    定時(shí)器蛔屹,本質(zhì)上是一個(gè)channel,golang中使用time.Ticker(duration)來(lái)設(shè)置定時(shí)器

    • 一次性定時(shí)器(延時(shí))

      package main
      
      import (
          "fmt"
          "time"
      )
      
      func main() {
          /*
              用sleep實(shí)現(xiàn)定時(shí)器
          */
          fmt.Println(time.Now())
          time.Sleep(time.Second)
          fmt.Println(time.Now())
          /*
              用timer實(shí)現(xiàn)定時(shí)器
          */
          timer := time.NewTimer(time.Second)
          fmt.Println(<-timer.C)
          /*
              用after實(shí)現(xiàn)定時(shí)器
          */
          fmt.Println(<-time.After(time.Second))
      
      }
      
    • 周期性定時(shí)器

      func tickDemo() {
          ticker := time.Tick(time.Second) //定義一個(gè)1秒間隔的定時(shí)器
          //ticker := time.NewTicker(time.Second)
          for i := range ticker {
              fmt.Println(i)//每秒都會(huì)執(zhí)行的任務(wù)
          }
      }
      

命令行參數(shù)解析-flag包

flag包是的golang開(kāi)發(fā)命令行工具更為簡(jiǎn)單豁生。

看一個(gè)完整示例兔毒,我們就更清楚flag的用途了:

執(zhí)行命令時(shí)要求輸入4個(gè)參數(shù),并指定了參數(shù)的類型與默認(rèn)值

func main() {
    //定義命令行參數(shù)方式1
    var name string
    var age int
    var married bool
    var delay time.Duration
    flag.StringVar(&name, "name", "張三", "姓名")
    flag.IntVar(&age, "age", 18, "年齡")
    flag.BoolVar(&married, "married", false, "婚否")
    flag.DurationVar(&delay, "d", 0, "延遲的時(shí)間間隔")

    //解析命令行參數(shù)
    flag.Parse()
    fmt.Println(name, age, married, delay)
    //返回命令行參數(shù)后的其他參數(shù)
    fmt.Println(flag.Args())
    //返回命令行參數(shù)后的其他參數(shù)個(gè)數(shù)
    fmt.Println(flag.NArg())
    //返回使用的命令行參數(shù)個(gè)數(shù)
    fmt.Println(flag.NFlag())
}

首先f(wàn)lag提供了命令行help功能甸箱,執(zhí)行命令行會(huì)給出相應(yīng)提示:

    $ ./flag_demo -help
    Usage of ./flag_demo:
      -age int
            年齡 (default 18)
      -d duration
            時(shí)間間隔
      -married
            婚否
      -name string
            姓名 (default "張三")

其次flag提供命令行參數(shù)parse解析能力:

注意:Args()\NArg()\NFlag()的含義

$ ./flag_demo -name pprof --age 28 -married=false -d=1h30m
pprof 28 false 1h30m0s
[]
0
4

當(dāng)然如果我們僅僅只是需要簡(jiǎn)單命令行輸入的參數(shù)育叁,我們也可以簡(jiǎn)單的考慮os.Args來(lái)獲取命令行參數(shù)。

os.Args是一個(gè)存儲(chǔ)命令行參數(shù)的字符串切片芍殖,它的第一個(gè)元素是執(zhí)行文件的名稱豪嗽,這和大部分語(yǔ)言命令行模式是類似的(python、php等)

日志-log包

官方標(biāo)準(zhǔn)簡(jiǎn)單log包豌骏,功能有限龟梦,更多可以實(shí)現(xiàn)流水賬的日志記錄,如果我們需要更多比如不同級(jí)別的日志記錄肯适,可以選擇第三方日志庫(kù):logrus、zap等

//使用標(biāo)準(zhǔn)日志log成榜,設(shè)置日志輸出到xx.log文件框舔,設(shè)置flags支持文件名、行號(hào)赎婚、日志前綴刘绣、時(shí)間格式
func main() {
    logFile, err := os.OpenFile("./xx.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    if err != nil {
        fmt.Println("open log file failed, err:", err)
        return
    }
    log.SetOutput(logFile)
    log.SetFlags(log.Llongfile | log.Lmicroseconds | log.Ldate)    
    log.SetPrefix("[JM]")
    log.Println("這里記錄一條日志。")
}
[JM]2020/01/14 15:32:45.431506 .../log_demo/main.go:13: 這里記錄一條日志挣输。

IO操作-os包

os包提供了Create纬凤、NewFile、Open撩嚼、OpenFile停士、Remove方法

返回的文件對(duì)象挖帘,提供了讀寫方法,比如Write恋技、WriteAt拇舀、WriteString、Read蜻底、ReadAt方法

  • 文件打開(kāi)與關(guān)閉

    package main
    
    import (
        "fmt"
        "os"
    )
    
    func main() {
        // 只讀方式打開(kāi)當(dāng)前目錄下的main.go文件
        file, err := os.Open("./main.go")
        if err != nil {
            fmt.Println("open file failed!, err:", err)
            return
        }
        // 關(guān)閉文件
        file.Close()
    }
    

    golang可以考慮再簡(jiǎn)化下文件的打開(kāi)及異常的處理和關(guān)閉骄崩,python的with open簡(jiǎn)化了try異常和close的處理,讓代碼更簡(jiǎn)潔薄辅,編寫者不用去關(guān)心異常處理和文件關(guān)閉處理代碼要拂,這本不屬于業(yè)務(wù)邏輯代碼,趨勢(shì)應(yīng)該是讓語(yǔ)言或機(jī)器自動(dòng)實(shí)現(xiàn)了

  • 寫文件
```go
file.WriteString("ab\n")
file.Write([]byte("cd\n"))
```

我們會(huì)更新一篇關(guān)于byte與string區(qū)別的文章
  • 讀文件

    golang文件讀取可以用file.Read()和file.ReadAt()站楚,讀到文件末尾會(huì)返回io.EOF的錯(cuò)誤脱惰,EOF這是大部分語(yǔ)言讀取結(jié)尾的標(biāo)識(shí)符了

    golang的os包的讀寫還需要偏上層封裝,不要讓開(kāi)發(fā)者去了解這么多讀寫的原理源请,比如讀取一行枪芒、整個(gè)文件,這些是開(kāi)發(fā)者更樂(lè)意用到的谁尸,不用關(guān)心讀寫的原理舅踪,至于性能開(kāi)發(fā)者會(huì)認(rèn)為是語(yǔ)言要解決的問(wèn)題,當(dāng)然當(dāng)我們有足夠經(jīng)驗(yàn)以后良蛮,就可以使用更底層些的方法來(lái)實(shí)現(xiàn)抽碌,提高性能。如果硬件的發(fā)展更快决瞳,我們更希望大家都可以不關(guān)心底層實(shí)現(xiàn)的去使用更上層的方法

    package main
    
    import (
        "fmt"
        "io"
        "os"
    )
    
    func main() {
        // 打開(kāi)文件
        file, err := os.Open("./xxx.txt")
        if err != nil {
            fmt.Println("open file err :", err)
            return
        }
        defer file.Close()
        // 定義接收文件讀取的字節(jié)數(shù)組
        var buf [128]byte
        var content []byte
        for {
            n, err := file.Read(buf[:])
            if err == io.EOF {
                // 讀取結(jié)束
                break
            }
            if err != nil {
                fmt.Println("read file err ", err)
                return
            }
            content = append(content, buf[:n]...)
        }
        fmt.Println(string(content))
    }
    

IO操作-bufio包與ioutil包

bufio包實(shí)現(xiàn)了帶緩沖區(qū)的讀寫货徙,是對(duì)文件讀寫的封裝

前面說(shuō)到開(kāi)發(fā)者更喜歡使用更上層的讀寫方法,golang的bufio包除了實(shí)現(xiàn)帶緩沖區(qū)的讀寫提高效率和穩(wěn)定性外皮胡,還提供按行讀方法痴颊,ioutil包提供了讀取整個(gè)文件、寫文件方法

bufio屡贺、ioutil包更多文件蠢棱、目錄讀寫詳見(jiàn)官方標(biāo)準(zhǔn)庫(kù)

strconv包

  • Atoi

    string類型轉(zhuǎn)換成int類型

  • Itoa

    int轉(zhuǎn)換成string

  • ParaseType系列

    將string轉(zhuǎn)換成指定Type類型

    b, err := strconv.ParseBool("true")
    f, err := strconv.ParseFloat("3.1415", 64)
    i, err := strconv.ParseInt("-2", 10, 64)
    u, err := strconv.ParseUint("2", 10, 64)
    
  • FormatType系列

    將給定類型數(shù)據(jù)格式化為string類型數(shù)據(jù)的功能

    s1 := strconv.FormatBool(true)
    s2 := strconv.FormatFloat(3.1415, 'E', -1, 64)
    s3 := strconv.FormatInt(-2, 16)
    s4 := strconv.FormatUint(2, 16)
    

strconv包中還有Append系列、Quote系列等函數(shù)甩栈。詳細(xì)見(jiàn)官方標(biāo)準(zhǔn)庫(kù)

模板-template包

html/template包實(shí)現(xiàn)了數(shù)據(jù)驅(qū)動(dòng)的模板泻仙,用于生成可對(duì)抗代碼注入的安全HTML輸出。

  • 模板語(yǔ)法

    {{.}}
    

    模板語(yǔ)法都包含在{{和}}中間量没,其中{{.}}中的點(diǎn)表示當(dāng)前對(duì)象玉转。

    當(dāng)我們傳入一個(gè)結(jié)構(gòu)體對(duì)象時(shí),我們可以根據(jù).來(lái)訪問(wèn)結(jié)構(gòu)體的對(duì)應(yīng)字段殴蹄。例如:

    type UserInfo struct {
    Name   string
    Gender string
    Age    int
    }
    
    func sayHello(w http.ResponseWriter, r *http.Request) {
        // 解析指定文件生成模板對(duì)象
        tmpl, err := template.ParseFiles("./hello.html")
        if err != nil {
            fmt.Println("create template failed, err:", err)
            return
        }
        user := UserInfo{
            Name:   "枯藤",
            Gender: "男",
            Age:    18,
        }
        // 利用給定數(shù)據(jù)渲染模板究抓,并將結(jié)果寫入w
        tmpl.Execute(w, user)
    }
    

    .表示當(dāng)前對(duì)象/結(jié)構(gòu)體user(w只是服務(wù)端的一個(gè)變量猾担,也保存了user這個(gè)結(jié)構(gòu)體而已)

    <body>
    <p>Hello {{.Name}}</p>
    <p>性別:{{.Gender}}</p>
    <p>年齡:{{.Name}}</p>
    </body>
    
  • 模板注釋

    {{/* a comment */}}
    注釋,執(zhí)行時(shí)會(huì)忽略漩蟆±萏剑可以多行。注釋不能嵌套怠李,并且必須緊貼分界符始止圾叼。
    
  • 管道pipeline

    Go的模板語(yǔ)法中支持使用管道符號(hào)|鏈接多個(gè)命令,用法和unix下的管道類似:|前面的命令會(huì)將運(yùn)算結(jié)果(或返回值)傳遞給后一個(gè)命令的最后一個(gè)位置捺癞。

  • Actions

    以下這些動(dòng)作基本包含golang模板中常用的動(dòng)作與含義說(shuō)明

    {{/* a comment */}}
    注釋夷蚊,執(zhí)行時(shí)會(huì)忽略∷杞椋可以多行惕鼓。注釋不能嵌套,并且必須緊貼分界符始止唐础,就像這里表示的一樣箱歧。
    {{pipeline}}
        pipeline的值的默認(rèn)文本表示會(huì)被拷貝到輸出里。
    {{if pipeline}} T1 {{end}}
        如果pipeline的值為empty一膨,不產(chǎn)生輸出呀邢,否則輸出T1執(zhí)行結(jié)果。不改變dot的值豹绪。
        Empty值包括false价淌、0、任意nil指針或者nil接口瞒津,任意長(zhǎng)度為0的數(shù)組蝉衣、切片、字典巷蚪。
    {{if pipeline}} T1 {{else}} T0 {{end}}
        如果pipeline的值為empty病毡,輸出T0執(zhí)行結(jié)果,否則輸出T1執(zhí)行結(jié)果屁柏。不改變dot的值啦膜。
    {{if pipeline}} T1 {{else if pipeline}} T0 {{end}}
        用于簡(jiǎn)化if-else鏈條,else action可以直接包含另一個(gè)if前联;等價(jià)于:
            {{if pipeline}} T1 {{else}}{{if pipeline}} T0 {{end}}{{end}}
    {{range pipeline}} T1 {{end}}
        pipeline的值必須是數(shù)組功戚、切片娶眷、字典或者通道似嗤。
        如果pipeline的值其長(zhǎng)度為0,不會(huì)有任何輸出届宠;
        否則dot依次設(shè)為數(shù)組烁落、切片乘粒、字典或者通道的每一個(gè)成員元素并執(zhí)行T1;
        如果pipeline的值為字典伤塌,且鍵可排序的基本類型灯萍,元素也會(huì)按鍵的順序排序。
    {{range pipeline}} T1 {{else}} T0 {{end}}
        pipeline的值必須是數(shù)組每聪、切片旦棉、字典或者通道。
        如果pipeline的值其長(zhǎng)度為0药薯,不改變dot的值并執(zhí)行T0绑洛;否則會(huì)修改dot并執(zhí)行T1。
    {{template "name"}}
        執(zhí)行名為name的模板童本,提供給模板的參數(shù)為nil真屯,如模板不存在輸出為""
    {{template "name" pipeline}}
        執(zhí)行名為name的模板,提供給模板的參數(shù)為pipeline的值穷娱。
    {{with pipeline}} T1 {{end}}
        如果pipeline為empty不產(chǎn)生輸出绑蔫,否則將dot設(shè)為pipeline的值并執(zhí)行T1。不修改外面的dot泵额。
    {{with pipeline}} T1 {{else}} T0 {{end}}
        如果pipeline為empty配深,不改變dot并執(zhí)行T0,否則dot設(shè)為pipeline的值并執(zhí)行T1梯刚。
    
  • 比較

    eq      如果arg1 == arg2則返回真
    ne      如果arg1 != arg2則返回真
    lt      如果arg1 < arg2則返回真
    le      如果arg1 <= arg2則返回真
    gt      如果arg1 > arg2則返回真
    ge      如果arg1 >= arg2則返回真
    
     {{eq arg1 arg2 arg3}}
    
  • 嵌套模板

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>tmpl test</title>
    </head>
    <body>
    
        <h1>測(cè)試嵌套template語(yǔ)法</h1>
        <hr>
        {{template "ul.html"}}
        <hr>
        {{template "ol.html"}}
    </body>
    </html>
    
    {{ define "ol.html"}}
    <h1>這是ol.html</h1>
    <ol>
        <li>AA</li>
        <li>BB</li>
        <li>CC</li>
    </ol>
    {{end}}
    

    ul.html模板 不在當(dāng)前html文檔內(nèi)凉馆,需要通過(guò)服務(wù)端template.ParseFiles指定加載的模板頁(yè),才能使用(有點(diǎn)耦合亡资,要是都由前端模板文件去include包含模板可能會(huì)更自然些)

    func tmplDemo(w http.ResponseWriter, r *http.Request) {
    tmpl, err := template.ParseFiles("./t.html", "./ul.html")
    if err != nil {
        fmt.Println("create template failed, err:", err)
        return
    }
    user := UserInfo{
        Name:   "枯藤",
        Gender: "男",
        Age:    18,
    }
    tmpl.Execute(w, user)
    

}
```

http包

Go語(yǔ)言內(nèi)置的net/http包十分的優(yōu)秀澜共,提供了HTTP客戶端和服務(wù)端的實(shí)現(xiàn)

  • 客戶端

    resp, err := http.Get("http://www.baidu.com/")
    
    resp, err := http.Post("http://www.9ong.com/post", "image/jpeg", &buf)
    
    resp, err := http.PostForm("http://www.9ong.com/form",
        url.Values{"key": {"Value"}, "id": {"123"}})
    
    if err != nil {
        // handle error
    }
    //使用完response后必須關(guān)閉回復(fù)的主體
    defer resp.Body.Close()
    body, err := ioutil.ReadAll(resp.Body)
    

    使用完response后必須關(guān)閉回復(fù)的主體,總是會(huì)有不完美的地方存在哈锥腻,還需要自己關(guān)閉Body嗦董,從編碼的角度看。

  • 帶參數(shù)的請(qǐng)求

    帶參數(shù)get請(qǐng)求

    apiUrl := "http://127.0.0.1:9090/get"
    // URL param
    data := url.Values{}
    data.Set("name", "枯藤")
    data.Set("age", "18")
    u, err := url.ParseRequestURI(apiUrl)
    if err != nil {
        fmt.Printf("parse url requestUrl failed,err:%v\n", err)
    }
    u.RawQuery = data.Encode() // URL encode
    fmt.Println(u.String())
    resp, err := http.Get(u.String())
    if err != nil {
        fmt.Println("post failed, err:%v\n", err)
        return
    }
    

    帶參數(shù)post請(qǐng)求

    // 表單數(shù)據(jù)
    //contentType := "application/x-www-form-urlencoded"
    //data := "name=jm&age=20"
    // json
    contentType := "application/json"
    data := `{"name":"jm","age":20}`
    resp, err := http.Post(url, contentType, strings.NewReader(data))
    

    看完以下的編寫方式瘦黑,我們覺(jué)得應(yīng)該有更舒適第三方http庫(kù)京革,只需要:

    http.Get(url,json對(duì)象參數(shù)|結(jié)構(gòu)體參數(shù))
    http.Post(url,options,data) //options負(fù)責(zé)設(shè)置http頭等信息,data是參數(shù)json對(duì)象或結(jié)構(gòu)體
    
  • 服務(wù)端

```go
// http server

func sayHello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Hello world")
}

func main() {
    http.HandleFunc("/", sayHello)
    err := http.ListenAndServe(":9527", nil)
    if err != nil {
        fmt.Printf("http server failed, err:%v\n", err)
        return
    }
}
```

自定義server

```go
s := &http.Server{
    Addr:           ":9527",
    Handler:        myHandler,
    ReadTimeout:    10 * time.Second,
    WriteTimeout:   10 * time.Second,
    MaxHeaderBytes: 1 << 20,
}
log.Fatal(s.ListenAndServe())
```

context

在 Go http包的Server中幸斥,每一個(gè)請(qǐng)求在都有一個(gè)對(duì)應(yīng)的 goroutine 去處理匹摇。請(qǐng)求處理函數(shù)通常會(huì)啟動(dòng)額外的 goroutine 用來(lái)訪問(wèn)后端服務(wù),比如數(shù)據(jù)庫(kù)和RPC服務(wù)甲葬。用來(lái)處理一個(gè)請(qǐng)求的 goroutine 通常需要訪問(wèn)一些與請(qǐng)求特定的數(shù)據(jù)廊勃,比如終端用戶的身份認(rèn)證信息、驗(yàn)證相關(guān)的token经窖、請(qǐng)求的截止時(shí)間坡垫。 當(dāng)一個(gè)請(qǐng)求被取消或超時(shí)時(shí)梭灿,所有用來(lái)處理該請(qǐng)求的 goroutine 都應(yīng)該迅速退出,然后系統(tǒng)才能釋放這些 goroutine 占用的資源冰悠。

介紹goroutine時(shí)堡妒,我們看到范例并沒(méi)有在main函數(shù)里使用context,goroutine也會(huì)自動(dòng)退出溉卓。原因是只有一種情況正在運(yùn)行的goroutine會(huì)因?yàn)槠渌鹓oroutine的結(jié)束被終止皮迟,就是main函數(shù)的退出或程序停止執(zhí)行.

sync.WaitGroup解決了協(xié)程協(xié)同同步完成問(wèn)題,context主要為了解決協(xié)程協(xié)同取消問(wèn)題

  • Context接口

    context.Context是一個(gè)接口桑寨,該接口定義了四個(gè)需要實(shí)現(xiàn)的方法

    type Context interface {
        Deadline() (deadline time.Time, ok bool) //返回當(dāng)前Context被取消的時(shí)間万栅,也就是完成工作的截止時(shí)間
        Done() <-chan struct{} //返回一個(gè)Channel,這個(gè)Channel會(huì)在當(dāng)前工作完成或者上下文被取消之后關(guān)閉西疤,多次調(diào)用Done方法會(huì)返回同一個(gè)Channel
        Err() error //返回當(dāng)前Context結(jié)束的原因
        Value(key interface{}) interface{} //從Context中返回鍵對(duì)應(yīng)的值烦粒,對(duì)于同一個(gè)上下文來(lái)說(shuō),多次調(diào)用Value 并傳入相同的Key會(huì)返回相同的結(jié)果代赁,該方法僅用于傳遞跨API和進(jìn)程間跟請(qǐng)求域的數(shù)據(jù)
    }
    

注意:以下介紹的都是函數(shù)扰她,并不是創(chuàng)建后context上下問(wèn)對(duì)像的方法,而是context包的函數(shù)

  • context.Background函數(shù)

  • context.TODO函數(shù)

    Go內(nèi)置兩個(gè)函數(shù):Background()和TODO()芭碍,這兩個(gè)函數(shù)分別返回一個(gè)實(shí)現(xiàn)了Context接口的background和todo徒役。我們代碼中最開(kāi)始都是以這兩個(gè)內(nèi)置的上下文對(duì)象作為最頂層的partent context,衍生出更多的子上下文對(duì)象窖壕。

    Background()主要用于main函數(shù)忧勿、初始化以及測(cè)試代碼中,作為Context這個(gè)樹(shù)結(jié)構(gòu)的最頂層的Context瞻讽,也就是根Context鸳吸。

    TODO(),它目前還不知道具體的使用場(chǎng)景速勇,如果我們不知道該使用什么Context的時(shí)候晌砾,可以使用這個(gè)。

    background和todo本質(zhì)上都是emptyCtx結(jié)構(gòu)體類型烦磁,是一個(gè)不可取消养匈,沒(méi)有設(shè)置截止時(shí)間,沒(méi)有攜帶任何值的Context都伪。

  • context.withCancel函數(shù)

    WithCancel返回帶有新Done通道的父節(jié)點(diǎn)的副本呕乎。當(dāng)調(diào)用返回的cancel函數(shù)或當(dāng)關(guān)閉父上下文的Done通道時(shí),將關(guān)閉返回上下文的Done通道陨晶,無(wú)論先發(fā)生什么情況猬仁。

    func gen(ctx context.Context) <-chan int {
        dst := make(chan int)
        n := 1
        go func() {
            for {
                select {
                case <-ctx.Done():
                    return // return結(jié)束該goroutine,防止泄露
                case dst <- n:
                    n++
                }
            }
        }()
        return dst
    }
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        defer cancel() // 當(dāng)我們?nèi)⊥晷枰恼麛?shù)后調(diào)用cancel
    
        for n := range gen(ctx) {
            fmt.Println(n)
            if n == 5 {
                break
            }
        }
    }
    
  • context.withDeadline函數(shù)

    返回父上下文的副本,并將deadline調(diào)整為不遲于d逐虚。如果父上下文的deadline已經(jīng)早于d,則WithDeadline(parent, d)在語(yǔ)義上等同于父上下文谆膳。當(dāng)截止日過(guò)期時(shí)叭爱,當(dāng)調(diào)用返回的cancel函數(shù)時(shí),或者當(dāng)父上下文的Done通道關(guān)閉時(shí)漱病,返回上下文的Done通道將被關(guān)閉买雾,以最先發(fā)生的情況為準(zhǔn)。

    func main() {
        d := time.Now().Add(50 * time.Millisecond)
        ctx, cancel := context.WithDeadline(context.Background(), d)
    
        // 盡管ctx會(huì)過(guò)期杨帽,但在任何情況下調(diào)用它的cancel函數(shù)都是很好的實(shí)踐漓穿。
        // 如果不這樣做,可能會(huì)使上下文及其父類存活的時(shí)間超過(guò)必要的時(shí)間注盈。
        defer cancel()
    
        select {
        case <-time.After(1 * time.Second):
            fmt.Println("overslept")
        case <-ctx.Done():
            fmt.Println(ctx.Err())
        }
    }
    
  • context.WithTimeout函數(shù)

    func main() {
        // 設(shè)置一個(gè)50毫秒的超時(shí)
        ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*50)
        wg.Add(1)
        go worker(ctx)
        time.Sleep(time.Second * 5)
        cancel() // 通知子goroutine結(jié)束
        wg.Wait()
        fmt.Println("over")
    }
    
  • context.WithValue函數(shù)

    WithValue返回父節(jié)點(diǎn)的副本晃危,其中與key關(guān)聯(lián)的值為val。

    僅對(duì)API和進(jìn)程間傳遞請(qǐng)求域的數(shù)據(jù)使用上下文值老客,而不是使用它來(lái)傳遞可選參數(shù)給函數(shù)僚饭。

    type TraceCode string
    
    func worker(){
        ...
        key := TraceCode("TRACE_CODE")
        traceCode, ok := ctx.Value(key).(string) // 在子goroutine中獲取trace code        
        ...
    }
    func main(){
        ...
        // 在系統(tǒng)的入口中設(shè)置trace code傳遞給后續(xù)啟動(dòng)的goroutine實(shí)現(xiàn)日志數(shù)據(jù)聚合
        ctx = context.WithValue(ctx, TraceCode("TRACE_CODE"), "2009")
        wg.Add(1)
        go worker(ctx)
        ...
    }
    
  • 關(guān)于cancel()函數(shù)

    以上范例中的cancel函數(shù),是通過(guò)context.With*系列函數(shù)返回得到的第二個(gè)值胧砰,用于通知同一個(gè)context下的所有g(shù)oroutine結(jié)束/取消鳍鸵。

golang的context設(shè)計(jì),讓我們明白一個(gè)道理尉间,能簡(jiǎn)單處理好一個(gè)問(wèn)題偿乖,就是好的解決方案,沒(méi)有高貴之分哲嘲。

json/xml

  • json

    json可以和map贪薪、struct、interface相互轉(zhuǎn)換

    // 將struct眠副、map轉(zhuǎn)換成json 字符串
    json.Marshal(struct|map)
    
    //將json字符串轉(zhuǎn)換成Person結(jié)構(gòu)體
    type Person struct{
        ...
    }
    jsonStr := []byte(`{"age":"18","name":"5lmh.com","marry":false}`)
    var p Person
    json.Unmarshal(jsonStr,&p)
    

    弱類型的js古掏、php可以隨時(shí)動(dòng)態(tài)自由的轉(zhuǎn)換json字符串,這個(gè)確實(shí)舒服太多侦啸,怪不得php開(kāi)發(fā)者總說(shuō)數(shù)組強(qiáng)大槽唾。

  • xml

    與json包的方法是一樣,只是數(shù)據(jù)源不一樣

    xml.Marshal(struct|map)
    xml.Unmarshal(xmlStr,&p)
    
  • msgpack

MSGPack是二進(jìn)制的json光涂,性能更快庞萍,更省空間

需要安裝第三方包:go get -u github.com/vmihailenco/msgpack

```go
msgpack.Marshal(struct|map)
msgpack.Unmarshal(msgpackbinary,&p)
```

reflect反射

反射是指在程序運(yùn)行期對(duì)程序本身進(jìn)行訪問(wèn)和修改的能力

reflect包封裝了反射相關(guān)的方法:

獲取類型信息:reflect.TypeOf,是靜態(tài)的

獲取值信息:reflect.ValueOf忘闻,是動(dòng)態(tài)的

反射可以獲取interface類型信息钝计、獲取值信息、修改值信息

反射可以查看結(jié)構(gòu)體字段、類型私恬、方法债沮,修改結(jié)構(gòu)體的值,調(diào)用方法

  • 空接口結(jié)合反射

    可以通過(guò) 空接口 可以表示任何參數(shù)本鸣,利用反射判斷參數(shù)類型

官方標(biāo)準(zhǔn)庫(kù)

官方標(biāo)準(zhǔn)庫(kù)


轉(zhuǎn)載請(qǐng)標(biāo)注來(lái)源:golang常用標(biāo)準(zhǔn)庫(kù).html - 9ong.com

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疫衩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子荣德,更是在濱河造成了極大的恐慌闷煤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,451評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件涮瞻,死亡現(xiàn)場(chǎng)離奇詭異鲤拿,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)署咽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,172評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門近顷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人宁否,你說(shuō)我怎么就攤上這事幕庐。” “怎么了家淤?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,782評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵异剥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我絮重,道長(zhǎng)冤寿,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,709評(píng)論 1 294
  • 正文 為了忘掉前任青伤,我火速辦了婚禮督怜,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狠角。我一直安慰自己号杠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,733評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布丰歌。 她就那樣靜靜地躺著姨蟋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪立帖。 梳的紋絲不亂的頭發(fā)上眼溶,一...
    開(kāi)封第一講書(shū)人閱讀 51,578評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音晓勇,去河邊找鬼堂飞。 笑死灌旧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绰筛。 我是一名探鬼主播枢泰,決...
    沈念sama閱讀 40,320評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼铝噩!你這毒婦竟也來(lái)了衡蚂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,241評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤薄榛,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后让歼,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體敞恋,經(jīng)...
    沈念sama閱讀 45,686評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,878評(píng)論 3 336
  • 正文 我和宋清朗相戀三年谋右,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了硬猫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,992評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡改执,死狀恐怖啸蜜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情辈挂,我是刑警寧澤衬横,帶...
    沈念sama閱讀 35,715評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站终蒂,受9級(jí)特大地震影響蜂林,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜拇泣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,336評(píng)論 3 330
  • 文/蒙蒙 一噪叙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧霉翔,春花似錦睁蕾、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,912評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)呛凶。三九已至搔体,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間求泰,已是汗流浹背芝加。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,040評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工硅卢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留射窒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,173評(píng)論 3 370
  • 正文 我出身青樓将塑,卻偏偏與公主長(zhǎng)得像脉顿,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子点寥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,947評(píng)論 2 355