golang context 简介(1)

2019-09-23 09:10:53 +08:00
 guonaihong

这个系列主要聊下 context 的出发点,带来了哪些便利的地方,常用 API,以及源代码分析

很多童鞋忽略的问题

API 服务是很多童鞋开发过的套路,从 API 取得数据或者控制字段,查询数据库返回业务数据。 那好,问题来了,如果 http client(curl 或者浏览器)在数据没有返回的过程中,异常中止了。你的 API 服务是不是还在傻傻的查询呢? 先来个伪代码模拟,下面会用 fmt.Printf+time.Sleep 模拟数据库查询操作。

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "time"
)

func main() {

    router := gin.Default()
    router.POST("/", func(c *gin.Context) {

        //模拟操作数据库
        for i := 0; i < 200; i++ {
            fmt.Printf("read db\n")
            time.Sleep(time.Second * 1)
        }
    })  

    router.Run()
}
// curl -X POST 127.0.0.1:8080/
// 你会发现如果客户段 ctrl+c 后,还会不停打印 read db,直到计算器结束。

调研 http.Request 数据结构

上面的资源浪费有没有办法优化?先瞄下 http.Request 源代码。好像有个 context 的东西还挺有意思的。

type Request struct {
    // ctx is either the client or server context. It should only
    // be modified via copying the whole Request using WithContext.
    // It is unexported to prevent people from using Context wrong
    // and mutating the contexts held by callers of the same request.
    ctx context.Context
}

// Context returns the request's context. To change the context, use 
// WithContext.
//
// The returned context is always non-nil; it defaults to the
// background context.
//
// For outgoing client requests, the context controls cancelation.
//
// For incoming server requests, the context is canceled when the
// client's connection closes, the request is canceled (with HTTP/2),
// or when the ServeHTTP method returns.
func (r *Request) Context() context.Context {
    if r.ctx != nil {
        return r.ctx
    }
    return context.Background()
}

改造资源浪费代码

package main

import (
    "fmt"
    "github.com/gin-gonic/gin"
    "time"
)

func main() {

    router := gin.Default()
    router.POST("/", func(c *gin.Context) {

        //模拟操作数据库
        ctx := c.Request.Context()
        for i := 0; i < 200; i++ {
            select {
            case <-ctx.Done(): // 新增加的关键代码
                fmt.Printf("found client ctrl+c\n")
                return
            default:
                fmt.Printf("read db\n")
            }
            time.Sleep(time.Second * 1)
        }
    })

    router.Run()
}

// curl -X POST 127.0.0.1:8080/
// 你会发现如果客户段 ctrl+c 后,已经不打印出来 read db。

ok,完美解决资源浪费的问题。 还有更多玩法,下篇介绍。

我的 github

https://github.com/guonaihong/gout

4202 次点击
所在节点    Go 编程语言
12 条回复
poplar50
2019-09-23 09:46:24 +08:00
request with context go1.7 出的功能 是很好用
guonaihong
2019-09-23 12:31:45 +08:00
@poplar50 是的好用,最重要的是 context 已经被官方扶正,后面一些并发套路都要用 context 的设计模式了。
AngelCriss
2019-09-23 12:49:58 +08:00
一个连续的过程该等多久还是得等多久,比如同步查数据库要花 10 分
AngryPanda
2019-09-23 12:55:28 +08:00
楼主的语文需要重修。
guonaihong
2019-09-23 12:59:19 +08:00
@AngryPanda 可有重修的门路?
guonaihong
2019-09-23 13:27:01 +08:00
@AngelCriss 兄弟,没太明白提出的问题。。。
sealingpp
2019-09-23 14:51:14 +08:00
赞,昨天因为在自己写接口的时候发现读库的时候有个 query 跟 quertContext 两个方法,于是去查了一下 context 的相关知识,只是有了解还不太会用
lazyfighter
2019-09-23 15:58:11 +08:00
其实我一直没有理解 TODO 以及 backGround 两个使用的特点,什么时候用哪个
labulaka521
2019-09-23 19:28:42 +08:00
@lazyfighter todo 就是暂时作为父 context 后续可能会改,另一个就是父 context 不会改了
guonaihong
2019-09-23 20:03:13 +08:00
@lazyfighter
从实现上来看没啥区别,都是从 emptyCtx 构造出来。更多的区别还是字面意思。
```go
var (
background = new(emptyCtx)
todo = new(emptyCtx)
)

// Background returns a non-nil, empty Context. It is never canceled, has no
// values, and has no deadline. It is typically used by the main function,
// initialization, and tests, and as the top-level Context for incoming
// requests.
func Background() Context {
return background
}

// TODO returns a non-nil, empty Context. Code should use context.TODO when
// it's unclear which Context to use or it is not yet available (because the
// surrounding function has not yet been extended to accept a Context
// parameter).
func TODO() Context {
return todo
}
```
AngelCriss
2019-09-24 18:02:18 +08:00
@guonaihong 楼主以前是不是写 PHP 的啊?
guonaihong
2019-09-24 18:25:08 +08:00
@AngelCriss 以前写 linux c 的。怎么了?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/603199

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX