The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
guonaihong

golang context 简介(1)

  •  
  •   guonaihong ·
    guonaihong · Sep 23, 2019 · 4743 views
    This topic created in 2434 days ago, the information mentioned may be changed or developed.

    这个系列主要聊下 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

    12 replies    2019-09-24 18:25:08 +08:00
    poplar50
        1
    poplar50  
       Sep 23, 2019 via Android
    request with context go1.7 出的功能 是很好用
    guonaihong
        2
    guonaihong  
    OP
       Sep 23, 2019
    @poplar50 是的好用,最重要的是 context 已经被官方扶正,后面一些并发套路都要用 context 的设计模式了。
    AngelCriss
        3
    AngelCriss  
       Sep 23, 2019 via Android   ❤️ 1
    一个连续的过程该等多久还是得等多久,比如同步查数据库要花 10 分
    AngryPanda
        4
    AngryPanda  
       Sep 23, 2019 via Android   ❤️ 1
    楼主的语文需要重修。
    guonaihong
        5
    guonaihong  
    OP
       Sep 23, 2019
    @AngryPanda 可有重修的门路?
    guonaihong
        6
    guonaihong  
    OP
       Sep 23, 2019
    @AngelCriss 兄弟,没太明白提出的问题。。。
    sealingpp
        7
    sealingpp  
       Sep 23, 2019 via iPhone
    赞,昨天因为在自己写接口的时候发现读库的时候有个 query 跟 quertContext 两个方法,于是去查了一下 context 的相关知识,只是有了解还不太会用
    lazyfighter
        8
    lazyfighter  
       Sep 23, 2019   ❤️ 1
    其实我一直没有理解 TODO 以及 backGround 两个使用的特点,什么时候用哪个
    labulaka521
        9
    labulaka521  
       Sep 23, 2019 via Android
    @lazyfighter todo 就是暂时作为父 context 后续可能会改,另一个就是父 context 不会改了
    guonaihong
        10
    guonaihong  
    OP
       Sep 23, 2019
    @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
        11
    AngelCriss  
       Sep 24, 2019 via Android
    @guonaihong 楼主以前是不是写 PHP 的啊?
    guonaihong
        12
    guonaihong  
    OP
       Sep 24, 2019
    @AngelCriss 以前写 linux c 的。怎么了?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   844 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 44ms · UTC 21:34 · PVG 05:34 · LAX 14:34 · JFK 17:34
    ♥ Do have faith in what you're doing.