浅谈Go的标准库(二)

61 阅读4分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第8天,点击查看活动详情

今天来简单学习下Go常用的标准库Http,Context的操作,具体的细节部分之后会再介绍,今天就学习下如何使用。

Http

Go内置的net/http包提供了HTTP客户端和服务端的具体实现,可以减少开发的难度

HTTP协议

超文本传输协议(HTTP,HyperText Transfer Protocol) 是互联网上应用最为广泛的一种网络传输协议,所有的WWW文件都必须遵守这个标准,通过HTTP协议可以发布和接收HTML页面

Get请求

通过net/http包编写一个简单的发送Http请求的客户端,示例如下:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    //新建Get请求
    resp, err := http.Get("https://baidu.com/")
    if err != nil {
        fmt.Println("get failed, err:", err)
        return
    }
    defer resp.Body.Close() //关闭回复的主体
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("read from resp.Body failed,err:", err)
        return
    }
    fmt.Print(string(body))
}

运行上述代码,会收到网站返回的HTML、CSS数据,浏览器会通过相应的规则将网页渲染出来

POST请求

使用net/http包发送POST请求的示例,示例代码如下:

package main

import (
    "fmt"
    "io/ioutil"
    "net/http"
    "strings"
)

func main() {
    url := "http://127.0.0.1:9090/post"
    // json
    contentType := "application/json"
    data := `{"name":"YYQQ","age":18}`
    resp, err := http.Post(url, contentType, strings.NewReader(data))
    if err != nil {
        fmt.Println("post failed, err:%v\n", err)
        return
    }
    defer resp.Body.Close()
    b, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Println("get resp failed,err:%v\n", err)
        return
    }
    fmt.Println(string(b))
} 

当然有封装好的Web框架例如Gin, 这是用Go语言编写的HTTP Web框架,它的优点有这些:

  • 封装比较好,API清晰,源码注释比较明确,上手简单
  • 运行速度快,分组的路由器,错误捕获,可以支持扩展、json格式

Context

什么是Context?

在 Go中的http包的Server中,每次处理一次请求都会开启一个对应的goroutine去处理,不同的处理函数可以会访问数据库或RPC等后台服务,或者访问一些与请求特定的数据,比如终端用户的身份认证信息、验证相关的token、请求的截止时间等,当一个请求被取消或超时时,所有用来处理该请求的 goroutine 都应该迅速退出,然后系统才能释放这些 goroutine 占用的资源

Go1.7加入了一个新的标准库context,它定义了Context类型,专门用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作

当一个上下文被取消时,它的子类的所有上下文也会被取消

Context接口

context.Context是一个接口,定义了四个需要实现的方法

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}
  • Deadline()需要返回当前Context被取消的时间,也就是完成工作的截止时间(deadline)

  • Done()需要返回一个Channel,这个Channel会在当前工作完成或者上下文被取消之后关闭,多次调用Done方法会返回同一个Channel

  • Err()会返回当前Context结束的原因,它只会在Done返回的Channel被关闭时才会返回非空的值;

    • 如果当前Context被取消,返回Canceled错误;
    • 如果当前Context超时,返回DeadlineExceeded错误;
  • Value()会从Context中返回Key对应的Value,对于同一个上下文来说,多次调用Value 并传入相同的Key会返回相同的结果,大部分存储的应该是一些共同的数据,例如:登陆的 session、cookie 等。

Background()和TODO()

Go内置两个函数:Background()和TODO(),这两个函数分别返回一个实现了Context接口的background和todo

Background()主要用于main函数、初始化以及测试代码中,作为Context这个树结构的根节点

TODO():通常用在不知道传递什么内容的时候,相当于一个"占位符",最终会替换成其他Context

background和todo本质上都是emptyCtx结构体类型,background是一个不可取消,没有设置截止时间,todo是没有携带任何值的Context

image.png

With系列

通过上述方法background和todo可以创建一个空的 context

有了根节点的context,接下来需要创建子节点,context提供了四个创建子节点的方法

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

使用Context的注意事项:

  • 将Context作为函数的第一个参数,显示传递Context
  • 给一个函数方法传递Context的时候,不要传递nil,需要使用官方提供的context.TODO()
  • Context的Value相关方法应该传递请求域的必要数据,例如session,cookie等
  • Context是线程安全的,可以在多个goroutine中传递

总结

今天简单的学习了Go常用的标准库(二),还有很多细节的用法可以查看官方文档,之后要开始着手学习框架方面的知识,对于刚入门go语言的我来说,还有许多地方需要学习,有错误的地方欢迎大家指出,共同进步!!