[关闭]
@zhongdao 2021-03-02T20:35:05.000000Z 字数 13231 阅读 1692

go 实例代码

未分类


包名引用与目录设置等

子目录包名引用

cat ~/go/src/test/greet.go

  1. package main
  2. import (
  3. "fmt"
  4. "test/util"
  5. )
  6. func main(){
  7. info := util.Info{15}
  8. fmt.Println(info.Age)
  9. fmt.Println(util.Greeting())
  10. }

cat ~/go/src/test/util/util.go

  1. package util
  2. type Info struct{
  3. Age int
  4. }
  5. func Greeting()string{
  6. return "world hello"
  7. }

编译时:
go run greet.go 即可

遇到提示:

  1. go run greet.go
  2. build command-line-arguments: cannot load greet/util: malformed module path "greet/util": missing dot in first path element

说明是go.1.13的高版本,从Go 1.13开始,模块module模式将是所有开发的默认模式。
需要执行如下命令

  1. go mod init

同一个目录同一个package,分不同.go的情况:

cat greet2.go

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func main(){
  6. info := Info{15}
  7. fmt.Println(info.Age)
  8. fmt.Println(Greeting())
  9. }

cat util.go

  1. package main
  2. type Info struct{
  3. Age int
  4. }
  5. func Greeting()string{
  6. return "world hello"
  7. }

go run greet2.go util.go 即可

一个main 调用多个处于同一个package里的多个源文件

本质是相当于把所有源代码都编译到当前main里面了, main里面通过目录名和package包名来调用。

  1. package main
  2. import (
  3. "fmt"
  4. "test/util"
  5. )
  6. func main(){
  7. info := util.Info{15}
  8. fmt.Println(info.Age)
  9. fmt.Println(util.Greeting())
  10. fmt.Println(util.Greeting2())
  11. fmt.Println(util.Return2(info))
  12. fmt.Println(util.Return(info))
  13. }

util/info.go

  1. package util
  2. type Info struct{
  3. Age int
  4. }

util/util.go

  1. package util
  2. func Greeting()string{
  3. return "world hello "
  4. }
  5. func Return(info Info) int {
  6. return info.Age * 2
  7. }

util/util2.go

  1. package util
  2. func Greeting2()string{
  3. return "world hello 2"
  4. }
  5. func Return2(info Info) int {
  6. return info.Age+2
  7. }

http

interface

  1. // A Handler responds to an HTTP request.
  2. //
  3. // ServeHTTP should write reply headers and data to the ResponseWriter
  4. // and then return. Returning signals that the request is finished; it
  5. // is not valid to use the ResponseWriter or read from the
  6. // Request.Body after or concurrently with the completion of the
  7. // ServeHTTP call.
  8. //
  9. // Depending on the HTTP client software, HTTP protocol version, and
  10. // any intermediaries between the client and the Go server, it may not
  11. // be possible to read from the Request.Body after writing to the
  12. // ResponseWriter. Cautious handlers should read the Request.Body
  13. // first, and then reply.
  14. //
  15. // Except for reading the body, handlers should not modify the
  16. // provided Request.
  17. //
  18. // If ServeHTTP panics, the server (the caller of ServeHTTP) assumes
  19. // that the effect of the panic was isolated to the active request.
  20. // It recovers the panic, logs a stack trace to the server error log,
  21. // and either closes the network connection or sends an HTTP/2
  22. // RST_STREAM, depending on the HTTP protocol. To abort a handler so
  23. // the client sees an interrupted response but the server doesn't log
  24. // an error, panic with the value ErrAbortHandler.
  25. type Handler interface {
  26. ServeHTTP(ResponseWriter, *Request)
  27. }

http 详解1

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. )
  7. func main() {
  8. // associate URLs requested to functions that handle requests
  9. http.Handle("/hello/lanre", &HelloWorld{"Lanre"})
  10. http.Handle("/hello/doe", &HelloWorld{"John doe"})
  11. //http.HandleFunc("/", getRequest)
  12. // start web server
  13. log.Fatal(http.ListenAndServe(":9999", nil))
  14. }
  15. type HelloWorld struct {
  16. Name string
  17. }
  18. func (h *HelloWorld) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  19. fmt.Fprint(w, "Hello "+h.Name)
  20. }
  1. curl http://localhost:9999/hello/dos

http 详解2

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "strconv"
  6. "net/http"
  7. )
  8. type user struct {
  9. ID int `json:"id"`
  10. Moniker string `json:"moniker"`
  11. Bio string `json:"bio"`
  12. Languages []string `json:"languages`
  13. }
  14. var allUsers []*user
  15. func init() {
  16. allUsers = []*user{
  17. {ID: 1, Moniker: "Hades", Bio: "god of the underworld, ruler of the dead and brother to the supreme ruler of the gods, Zeus", Languages: []string{"Greek"}},
  18. {ID: 2, Moniker: "Horus", Bio: "god of the sun, sky and war", Languages: []string{"Arabic"}},
  19. {ID: 3, Moniker: "Apollo", Bio: "god of light, music, manly beauty, dance, prophecy, medicine, poetry and almost every other thing. Son of Zeus", Languages: []string{"Greek"}},
  20. {ID: 4, Moniker: "Artemis", Bio: "goddess of the wilderness and wild animals. Sister to Apollo and daughter of Zeus", Languages: []string{"Greek"}},
  21. }
  22. }
  23. func main() {
  24. http.Handle("/users/", users{})
  25. http.HandleFunc("/about", func(w http.ResponseWriter, r *http.Request) {
  26. w.WriteHeader(http.StatusOK)
  27. fmt.Fprintf(w, "You just reached the page of our startup. Thank you for visiting")
  28. })
  29. http.ListenAndServe(":4000", nil)
  30. }
  31. type users struct {
  32. }
  33. func (u users) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  34. s := r.URL.Path[len("/users/"):]
  35. if s != "" {
  36. id, _ := strconv.Atoi(s)
  37. var requestedUser *user
  38. var found bool
  39. for _, v := range allUsers {
  40. if v.ID == id {
  41. found = true //god exists
  42. requestedUser = v
  43. break
  44. }
  45. }
  46. if found {
  47. w.WriteHeader(http.StatusOK)
  48. j, _ := json.Marshal(&requestedUser)
  49. fmt.Fprintf(w, string(j))
  50. return
  51. }
  52. w.WriteHeader(http.StatusNotFound)
  53. return
  54. }
  55. //No id was specified so we return the default
  56. w.WriteHeader(http.StatusOK)
  57. j, _ := json.Marshal(allUsers)
  58. fmt.Fprintf(w, string(j))
  59. }
  1. curl -X GET http://localhost:4000/users/3
  2. curl -X GET http://localhost:4000/users

http server

https://gobyexample.com/http-servers

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. func hello(w http.ResponseWriter, req *http.Request) {
  7. fmt.Fprintf(w, "hello\n")
  8. }
  9. func headers(w http.ResponseWriter, req *http.Request) {
  10. for name, headers := range req.Header {
  11. for _, h := range headers {
  12. fmt.Fprintf(w, "%v: %v\n", name, h)
  13. }
  14. }
  15. }
  16. func main() {
  17. http.HandleFunc("/hello", hello)
  18. http.HandleFunc("/headers", headers)
  19. http.ListenAndServe(":8090", nil)
  20. }

http with mux with github

go get -u github.com/gorilla/mux

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "net/http"
  6. "github.com/gorilla/mux"
  7. )
  8. func homeLink(w http.ResponseWriter, r *http.Request) {
  9. fmt.Fprintf(w, "Welcome home!")
  10. }
  11. func main() {
  12. router := mux.NewRouter().StrictSlash(true)
  13. router.HandleFunc("/", homeLink)
  14. log.Fatal(http.ListenAndServe(":8190", router))
  15. }

go routine

多个goroutine展示实际运行情况

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. func numbers() {
  7. for i := 1; i <= 5; i++ {
  8. time.Sleep(250 * time.Millisecond)
  9. fmt.Printf("%d ", i)
  10. }
  11. }
  12. func alphabets() {
  13. for i := 'a'; i <= 'e'; i++ {
  14. time.Sleep(400 * time.Millisecond)
  15. fmt.Printf("%c ", i)
  16. }
  17. }
  18. func main() {
  19. go numbers()
  20. go alphabets()
  21. time.Sleep(3000 * time.Millisecond)
  22. fmt.Println("main terminated")
  23. }

1 a 2 3 b 4 c 5 d e main terminated

https://studygolang.com/articles/12342

image_1dvlqdl1h1kn01p3q26tuui18lc9.png-40.8kB
第一张蓝色的图表示 numbers 协程,第二张褐红色的图表示 alphabets 协程,第三张绿色的图表示 Go 主协程,而最后一张黑色的图把以上三种协程合并了,表明程序是如何运行的。在每个方框顶部,诸如 0 ms 和 250 ms 这样的字符串表示时间(以微秒为单位)。在每个方框的底部,1、2、3 等表示输出。蓝色方框表示:250 ms 打印出 1,500 ms 打印出 2,依此类推。最后黑色方框的底部的值会是 1 a 2 3 b 4 c 5 d e main terminated,这同样也是整个程序的输出。以上图片非常直观,你可以用它来理解程序是如何运作的。

单个goroutine也包括main 会引发 chan 的panic.

使用信道需要考虑的一个重点是死锁。当 Go 协程给一个信道发送数据时,照理说会有其他 Go 协程来接收数据。如果没有的话,程序就会在运行时触发 panic,形成死锁。

同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。

  1. package main
  2. func main() {
  3. ch := make(chan int)
  4. ch <- 5
  5. }

fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox249677995/main.go:6 +0x80

关闭信道与 for range遍历信道

数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来。

当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。
v, ok := <- ch

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func producer(chnl chan int) {
  6. for i := 0; i < 10; i++ {
  7. chnl <- i
  8. }
  9. close(chnl)
  10. }
  11. func main() {
  12. ch := make(chan int)
  13. go producer(ch)
  14. for { // 无限的 for 循环
  15. v, ok := <-ch
  16. if ok == false {
  17. break
  18. }
  19. fmt.Println("Received ", v, ok)
  20. }
  21. }

for range 循环用于在一个信道关闭之前,从信道接收数据。

接下来我们使用 for range 循环重写上面的代码。

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. func producer(chnl chan int) {
  6. for i := 0; i < 10; i++ {
  7. chnl <- i
  8. }
  9. close(chnl)
  10. }
  11. func main() {
  12. ch := make(chan int)
  13. go producer(ch)
  14. for v := range ch {
  15. fmt.Println("Received ",v)
  16. }
  17. }

在第 16 行,for range 循环从信道 ch 接收数据,直到该信道关闭。一旦关闭了 ch,循环会自动结束。

信道 chan

无缓冲信道的发送和接收过程是阻塞的。

我们还可以创建一个有缓冲(Buffer)的信道。只在缓冲已满的情况,才会阻塞向缓冲信道(Buffered Channel)发送数据。同样,只有在缓冲为空的时候,才会阻塞从缓冲信道接收数据。

  1. func write(ch chan int) {
  2. for i := 0; i < 5; i++ {
  3. ch <- i
  4. fmt.Println("successfully wrote", i, "to ch")
  5. }
  6. close(ch)
  7. }
  8. func main() {
  9. ch := make(chan int, 2)
  10. go write(ch)
  11. time.Sleep(2 * time.Second)
  12. for v := range ch {
  13. fmt.Println("read value", v,"from ch")
  14. time.Sleep(2 * time.Second)
  15. }
  16. }

说明: 对于 有容量的ch 信道, 可以采用 for v:= range ch的 循环来读取。直到 ch关闭。

若用 for range 接收数据时,对于关闭了的信道,会接收完剩下的有效数据,并退出循环。如果没有 close 提示数据发送完毕的话,for range 会接收完剩下所有有效数据后发生阻塞。

wait

WaitGroup 用于实现工作池,因此要理解工作池,我们首先需要学习 WaitGroup。

WaitGroup 用于等待一批 Go 协程执行结束。程序控制会一直阻塞,直到这些协程全部执行完毕。假设我们有 3 个并发执行的 Go 协程(由 Go 主协程生成)。Go 主协程需要等待这 3 个协程执行结束后,才会终止。这就可以用 WaitGroup 来实现。

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. "time"
  6. )
  7. func process(i int, wg *sync.WaitGroup) {
  8. fmt.Println("started Goroutine ", i)
  9. time.Sleep(2 * time.Second)
  10. fmt.Printf("Goroutine %d ended\n", i)
  11. wg.Done()
  12. }
  13. func main() {
  14. no := 3
  15. var wg sync.WaitGroup
  16. for i := 0; i < no; i++ {
  17. wg.Add(1)
  18. go process(i, &wg)
  19. }
  20. wg.Wait()
  21. fmt.Println("All go routines finished executing")
  22. }

var wg sync.WaitGroup
wg.Add() // 添加计数,需要
wg.Done() // 完成的进程
wg.Wait() // 等待所有完成。
注意: &wg ,

wokerpool

Visually Understanding Worker Pool
https://medium.com/coinmonks/visually-understanding-worker-pool-48a83b7fc1f5

select

select 语句用于在多个发送/接收信道操作中进行选择。select 语句会一直阻塞,直到发送/接收操作准备就绪。如果有多个信道操作准备完毕,select 会随机地选取其中之一执行。该语法与 switch 类似,所不同的是,这里的每个 case 语句都是信道操作。我们好好看一些代码来加深理解吧。

  1. output1 := make(chan string)
  2. output2 := make(chan string)
  3. go server1(output1)
  4. go server2(output2)
  5. select {
  6. case s1 := <-output1:
  7. fmt.Println(s1)
  8. case s2 := <-output2:
  9. fmt.Println(s2)
  10. }

临界区 & mutex

在学习 Mutex 之前,我们需要理解并发编程中临界区(Critical Section)的概念。当程序并发地运行时,多个 Go 协程不应该同时访问那些修改共享资源的代码 (例如: x = x +1 )

根据上下文切换的不同情形,x 的最终值是 1 或者 2。这种不太理想的情况称为竞态条件(Race Condition),其程序的输出是由协程的执行顺序决定的。

如果在任意时刻只允许一个 Go 协程访问临界区,那么就可以避免竞态条件。而使用 Mutex 可以达到这个目的。

Mutex 用于提供一种加锁机制(Locking Mechanism),可确保在某时刻只有一个协程在临界区运行,以防止出现竞态条件。

  1. mutex.Lock()
  2. x = x + 1
  3. mutex.Unlock()

总体说来,当 Go 协程需要与其他协程通信时,可以使用信道。而当只允许一个协程访问临界区时,可以使用 Mutex。

json

go json introduction

https://blog.golang.org/json-and-go

推荐:
https://yourbasic.org/golang/json-example/

json1

// json.go

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "net/http"
  6. )
  7. type User struct {
  8. Firstname string `json:"firstname"`
  9. Lastname string `json:"lastname"`
  10. Age int `json:"age"`
  11. }
  12. func main() {
  13. http.HandleFunc("/decode", func(w http.ResponseWriter, r *http.Request) {
  14. var user User
  15. json.NewDecoder(r.Body).Decode(&user)
  16. fmt.Fprintf(w, "%s %s is %d years old!", user.Firstname, user.Lastname, user.Age)
  17. })
  18. http.HandleFunc("/encode", func(w http.ResponseWriter, r *http.Request) {
  19. peter := User{
  20. Firstname: "John",
  21. Lastname: "Doe",
  22. Age: 25,
  23. }
  24. json.NewEncoder(w).Encode(peter)
  25. })
  26. http.ListenAndServe(":8080", nil)
  27. }

run:

  1. $ go run json.go
  2. $ curl -s -XPOST -d'{"firstname":"Elon","lastname":"Musk","age":48}' http://localhost:8080/decode
  3. Elon Musk is 48 years old!
  4. $ curl -s http://localhost:8080/encode
  5. {"firstname":"John","lastname":"Doe","age":25}

json2

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. )
  6. // Employee struct with struct tags
  7. type Employee struct {
  8. ID int `json:"id,omitempty"`
  9. FirstName string `json:"firstname"`
  10. LastName string `json:"lastname"`
  11. JobTitle string `json:"job"`
  12. }
  13. func main() {
  14. emp := Employee{
  15. FirstName: "Shiju",
  16. LastName: "Varghese",
  17. JobTitle: "Architect",
  18. }
  19. // Encoding to JSON
  20. data, err := json.Marshal(emp)
  21. if err != nil {
  22. fmt.Println(err.Error())
  23. return
  24. }
  25. jsonStr := string(data)
  26. fmt.Println("The JSON data is:")
  27. fmt.Println(jsonStr)
  28. b := []byte(`{"id":101,"firstname":"Irene","lastname":"Rose","job":"Developer"}`)
  29. var emp1 Employee
  30. // Decoding JSON to a struct type
  31. err = json.Unmarshal(b, &emp1)
  32. if err != nil {
  33. fmt.Println(err.Error())
  34. return
  35. }
  36. fmt.Println("The Employee value is:")
  37. fmt.Printf("ID:%d, Name:%s %s, JobTitle:%s", emp1.ID, emp1.FirstName, emp1.LastName,
  38. emp1.JobTitle)
  39. }

output:

  1. The JSON data is:
  2. {"firstname":"Shiju","lastname":"Varghese","job":"Architect"}
  3. The Employee value is:
  4. ID:101, Name:Irene Rose, JobTitle:Developer

json3

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. )
  7. func main() {
  8. type ColorGroup struct {
  9. ID int
  10. Name string
  11. Colors []string
  12. }
  13. group := ColorGroup{
  14. ID: 1,
  15. Name: "Reds",
  16. Colors: []string{"Crimson", "Red", "Ruby", "Maroon"},
  17. }
  18. b, err := json.Marshal(group)
  19. if err != nil {
  20. fmt.Println("error:", err)
  21. }
  22. os.Stdout.Write(b)
  23. }

json4

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "os"
  6. )
  7. type response1 struct {
  8. Page int
  9. Fruits []string
  10. }
  11. type response2 struct {
  12. Page int `json:"page"`
  13. Fruits []string `json:"fruits"`
  14. }
  15. func main() {
  16. bolB, _ := json.Marshal(true)
  17. fmt.Println(string(bolB))
  18. intB, _ := json.Marshal(1)
  19. fmt.Println(string(intB))
  20. fltB, _ := json.Marshal(2.34)
  21. fmt.Println(string(fltB))
  22. strB, _ := json.Marshal("gopher")
  23. fmt.Println(string(strB))
  24. slcD := []string{"apple", "peach", "pear"}
  25. slcB, _ := json.Marshal(slcD)
  26. fmt.Println(string(slcB))
  27. mapD := map[string]int{"apple": 5, "lettuce": 7}
  28. mapB, _ := json.Marshal(mapD)
  29. fmt.Println(string(mapB))
  30. res1D := &response1{
  31. Page: 1,
  32. Fruits: []string{"apple", "peach", "pear"}}
  33. res1B, _ := json.Marshal(res1D)
  34. fmt.Println(string(res1B))
  35. res2D := &response2{
  36. Page: 1,
  37. Fruits: []string{"apple", "peach", "pear"}}
  38. res2B, _ := json.Marshal(res2D)
  39. fmt.Println(string(res2B))
  40. byt := []byte(`{"num":6.13,"strs":["a","b"]}`)
  41. var dat map[string]interface{}
  42. if err := json.Unmarshal(byt, &dat); err != nil {
  43. panic(err)
  44. }
  45. fmt.Println(dat)
  46. num := dat["num"].(float64)
  47. fmt.Println(num)
  48. strs := dat["strs"].([]interface{})
  49. str1 := strs[0].(string)
  50. fmt.Println(str1)
  51. str := `{"page": 1, "fruits": ["apple", "peach"]}`
  52. res := response2{}
  53. json.Unmarshal([]byte(str), &res)
  54. fmt.Println(res)
  55. fmt.Println(res.Fruits[0])
  56. enc := json.NewEncoder(os.Stdout)
  57. d := map[string]int{"apple": 5, "lettuce": 7}
  58. enc.Encode(d)
  59. }

output:

  1. $ go run json.go
  2. true
  3. 1
  4. 2.34
  5. "gopher"
  6. ["apple","peach","pear"]
  7. {"apple":5,"lettuce":7}
  8. {"Page":1,"Fruits":["apple","peach","pear"]}
  9. {"page":1,"fruits":["apple","peach","pear"]}
  10. map[num:6.13 strs:[a b]]
  11. 6.13
  12. a
  13. {1 [apple peach]}
  14. apple
  15. {"apple":5,"lettuce":7}

json5 simple way by github projs

  1. package main
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "io/ioutil"
  7. "log"
  8. "os"
  9. "github.com/tidwall/gjson"
  10. "github.com/tidwall/pretty"
  11. "github.com/tidwall/sjson"
  12. )
  13. const jsons = `{"name":{"first":"Janet","last":"Prichard"},"age":47}`
  14. func main() {
  15. jsons, _ := sjson.Set(jsons, "name.last", "Anderson")
  16. jsons, _ = sjson.Set(jsons, "work", "IT manager")
  17. println(jsons)
  18. bjson := pretty.Pretty([]byte(jsons))
  19. println(string(bjson))
  20. ioutil.WriteFile("p_marhsall.json", bjson, os.ModePerm)
  21. /*
  22. jsonstr, _ := getjsonstr("p_marhsall.json")
  23. fmt.Println(gjson.Get(jsonstr, "work").String())
  24. */
  25. }

exec

https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html

go

  1. package main
  2. // https://blog.kowalczyk.info/article/wOYk/advanced-command-execution-in-go-with-osexec.html
  3. // to run:
  4. // go run 01-simple-exec.go
  5. import (
  6. "fmt"
  7. "log"
  8. "os/exec"
  9. "runtime"
  10. )
  11. func main() {
  12. cmd := exec.Command("ls", "-lah")
  13. if runtime.GOOS == "windows" {
  14. cmd = exec.Command("tasklist")
  15. }
  16. out, err := cmd.CombinedOutput()
  17. if err != nil {
  18. log.Fatalf("cmd.Run() failed with %s\n", err)
  19. }
  20. fmt.Printf("combined out:\n%s\n", string(out))
  21. }

改变执行路径

  1. package main
  2. import (
  3. "fmt"
  4. "log"
  5. "os/exec"
  6. )
  7. func main() {
  8. cmd := exec.Command("program") // or whatever the program is
  9. cmd.Dir = "C:/usr/bin" // or whatever directory it's in
  10. out, err := cmd.Output()
  11. if err != nil {
  12. log.Fatal(err)
  13. } else {
  14. fmt.Printf("%s", out);
  15. }
  16. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注