网络编程中绕不过去的一个板块就是http
服务,无论是老架构上还是微服务架构上,客户端服务端、服务端直接通信几乎都是以http
、RPC
通信为主,这里先记录Golang
中的http
库以及常用的Gin
框架。
http
服务端
1 2 3 4 5 6 7 8 9
| func HelloServer(w http.ResponseWriter, r *http.Request) { io.WriteString(w, "hello world") return }
func server() { http.HandleFunc("/", HelloServer) log.Fatal(http.ListenAndServe(":8080", nil)) }
|
客户端
客户端调用方式
1 2 3
| response, _ := http.Get("https://www.baidu.com") // 源码看,其实上面这种就是下面 response, _ := http.DefaultClient.Get("https://www.baidu.com")
|
http
的response
返回的是一个http.Response
,解析的方式一般有两种
1 2
| response, _ := http.DefaultClient.Get("https://www.baidu.com") defer response.Body.Close()
|
只获取body
:
1
| ioutil.ReadAll(response.Body)
|
解析response
1
| dumpResponse, _ := httputil.DumpResponse(response, true)
|
通过client
控制header
1 2 3 4
| client := http.DefaultClient request, err := http.NewRequest(http.MethodGet, "https://www.baidu.com", nil) request.Header.Add("User-Agent", "test") // Header增加内容 response, err := client.Do(request)
|
查看转发
1 2 3 4 5 6 7 8
| client := http.Client{ CheckRedirect: func(req *http.Request, via []*http.Request) error { fmt.Println("req: ", req) // 打印请求的信息,如果有转发,会打印转发的信息 return nil }, } request, err := http.NewRequest(http.MethodGet, "http://www.baidu.com/", nil) response, err := client.Do(request)
|
性能分析
启动http
监听时,加上pprof
打开对应页面即可
1
| http://127.0.0.1:8080/debug/pprof/
|
使用go tool 获取30s的CPU的使用率
1
| go tool pprof http://127.0.0.1:8080/debug/pprof/profile
|
内存
1
| go tool pprof http://localhost:6060/debug/pprof/heap
|
具体获取的内容可以查看源码说明文档或者指导文档
https://go.dev/blog/pprof
json格式
将结构体序列化成json
字符串,json
中的key
可以通过tag
指定。omitempty
当结构体中的值为零值,则该字段不序列化。
1 2 3 4 5 6
| type User struct { ID int `json:"id"` Name string `json:"name"` Age int `json:"age,omitempty"` Books []Book `json:"books"` }
|
反序列化
方式一:通过相同的结构体反序列化
1
| err := json.Unmarshal([]byte(res), &m) // 传入地址
|
方式二:通过map[string]interface{}以及类型断言
1 2 3
| m := make(map[string]interface{}) // 先反序列成map[string]interface,然后类型断言interface是[]interface,获取角标之后,再类型断言成map[string]interface fmt.Println(m["books"].([]interface{})[1].(map[string]interface{})["name"])
|
gin
比较常用的第三方http
库,提供context
和midware
能力
1 2 3 4 5 6 7
| r := gin.Default() r.GET("/ping", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) r.Run(":8080")
|
midware
1 2 3 4 5 6 7 8 9 10
| r.Use(func(c *gin.Context) { requestID := rand.Int() c.Set("requestID", requestID) s := time.Now() // path logger.Info("incoming request", zap.String("path", c.Request.URL.String())) c.Next() // c.Next()上面的是接受到请求就可以处理;c.Next()下面的是请求处理完之后运行的逻辑。 // response code logger.Info("message", zap.Int("code", c.Writer.Status()), zap.Duration("elapsed", time.Now().Sub(s))) })
|
context
1 2 3 4 5 6 7
| // 在midware中的context设置key value requestID := rand.Int() c.Set("requestID", requestID) // 在handler中通过context获取key value if requestID, exists := c.Get("requestID"); exists { res["requestID"] = requestID }
|