学习go context

go context

http包的Server中每个请求对应一个goroutine处理

上层任务取消后,所有的下层任务都会被取消;
中间某一层的任务取消后,只会将当前任务的下层任务取消,而不会影响上层的任务以及同级任务。

一个请求被取消或超市,所有用来处理该请求的goroutine都应退出

context包简化单个请求的多个goroutine之间的相关操作

context 接口

type Context interface {

    Deadline() (deadline time.Time, ok bool)   //返回任务被取消的截止时间

    Done() <-chan struct{}   //任务取消时返回一个关闭的channel

    Err() error    //channel关闭的原因

    Value(key interface{}) interface{}    //存储键值对(树)
}

emptyCtx

type emptyCtx int
var (
    background = new(emptyCtx)
    todo       = new(emptyCtx)
)

func Background() Context {
    return background
}

func TODO() Context {
    return todo
}

一般不会直接使用emptyCtx,而是使用由emptyCtx实例化的两个变量,分别可以通过调用Background和TODO方法得到。
Background通常被用于主函数、初始化以及测试中,作为一个顶层的context,也就是说一般我们创建的context都是基于Background;而TODO是在不确定使用什么context的时候才会使用。

几种context类型

valueCtx

WithValue(parent Context, key, val interface{}) Context 方法添加key-value

cancelCtx

WithCancel(parent Context) (ctx Context, cancel CancelFunc)方法创建可取消的context

timerCtx

WithDeadline(parent Context, d time.Time) (Context, CancelFunc)方法返回一个可取消的context(时间点)
WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) (相对当前时间的时间段)

net/http包源码中的应用

  1. server 开启服务时:
    由ctx.Background()创建一个baseCtx;
    并由context.WithValue()创建一个valueCtx存储server信息,建立一个连接,即开启一个协程携带ctx
  2. 处理方法:
    由context.WithValue(ctx)创建一个valueCtx存储本地地址信息;
    创建一个cancelCtx,每读取一个request传入该ctx;(一旦请求超时,即可中断当前请求)
  3. 读取request:
    基于传入的ctx创建新的cancelCtx,设置到req上;
    ???生成的response中的cancelCtx保存了当前context取消方法。
    (在处理构建response过程中如果发生错误,可直接调用response对象的cancelCtx方法结束当前请求;
    在处理构建response完成之后,调用response对象的cancelCtx方法结束当前请求。)

注意事项

  • 上游任务仅仅使用context通知下游任务不再需要,但不会直接干涉和中断下游任务的执行,由下游任务自行决定后续的处理操作,也就是说context的取消操作是无侵入的;
  • context是线程安全的,因为context本身是不可变的(immutable),因此可以放心地在多个协程中传递使用

server程序

func handleSearch(w http.ResponseWriter, req *http.Request) { //注册到路由
    // ctx is the `Context` for this handler. Calling cancel closes the
    // ctx.Done channel, which is the cancellation signal for requests
    // started by this handler.
    var (
        ctx    context.Context
        cancel context.CancelFunc
    )
    timeout, err := time.ParseDuration(req.FormValue("timeout"))
    if err == nil {
        // The request has a timeout, so create a `Context` that is
        // canceled automatically when the timeout expires.
        ctx, cancel = context.WithTimeout(context.Background(), timeout)
    } else {
        ctx, cancel = context.WithCancel(context.Background())
    }
    defer cancel() // Cancel ctx as soon as handleSearch returns.
    
    // Check the search query.
    query := req.FormValue("q")
    if query == "" {
        http.Error(w, "no query", http.StatusBadRequest)
        return
    }

    // Store the user IP in ctx for use by code in other packages.
    userIP, err := userip.FromRequest(req)   //从一个 http.Request 对象中解析出 userIP
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    ctx = userip.NewContext(ctx, userIP)     //新context,关联IP

    // Run the Google search and print the results.
    start := time.Now()
    results, err := google.Search(ctx, query)    //发送HTTP请求
    elapsed := time.Since(start)

    if err := resultsTemplate.Execute(w, struct {
        Results          google.Results
        Timeout, Elapsed time.Duration
    }{
        Results: results,
        Timeout: timeout,
        Elapsed: elapsed,
    }); err != nil {
        log.Print(err)
        return
    }

userip包 :解析Client IP/将其关联到一个Context对象

func FromRequest(req *http.Request) (net.IP, error) //从一个 http.Request 对象中解析出 userIP
func NewContext(ctx context.Context, userIP net.IP) //ip关联到一个新的Context对象
func FromContext(ctx context.Context) (net.IP, bool) //从一个 Context 对象中解析 userIP

google包 : 发送HTTP请求

func Search(ctx context.Context, query string) (Results, error) // 发送一个http请求,并解析返回的JSON数据。
使用httpDo(ctx, req, func(resp *http.Response, err error)发送HTTP请求,在ctx.Done关闭时取消请求。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值