golang context 简介(1)--如何避免资源浪费

原创
2019/09/26 09:27
阅读数 200

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

展开阅读全文
打赏
0
1 收藏
分享
加载中
更多评论
打赏
0 评论
1 收藏
0
分享
返回顶部
顶部