教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang int 转 duration_一看就懂系列之Golang的context

golang int 转 duration_一看就懂系列之Golang的context

发布时间:2022-01-09   编辑:jiaochengji.com
教程集为您提供golang int 转 duration,一看就懂系列之Golang的context等资源,欢迎您收藏本站,我们将为您提供最新的golang int 转 duration,一看就懂系列之Golang的context资源

共2409字,读完此文将花费您6min


前言

是的,今天本来还想出去玩的。买了动车票,然后又睡过头了。。没办法,可能是天意,只好总结一下golang的context,希望能与context之间做一个了断,成为这个周末最靓的仔

公司里头大家写各种服务,必须需要将Context作为第一个参数,刚开始以为主要用于全链路排查跟踪。但是随着接触多了,原来不止于此

以下知识点,10s即将达到战场。

1.context源码详解

2.context实战

3.context建议

正文

1.context详解

1.1 产生背景

在go的1.7之前,context还是非编制的(包golang.org/x/net/context中),golang团队发现context这个东西还挺好用的,很多地方也都用到了,就把它收编了,1.7版本正式进入标准库。

context常用的使用姿势:

1.web编程中,一个请求对应多个goroutine之间的数据交互

2.超时控制

3.上下文控制

1.2 context的底层结构

<pre class="has"><code>type Context interface { Deadline() (deadline time.Time, ok bool) Done() struct{} Err() error Value(key interface{}) interface{}}</code></pre>

这个就是Context的底层数据结构,同志们继续往下看,一起来分析下:

<table><tbody><tr><td>字段</td><td>含义</td></tr><tr><td>Deadline</td><td>返回一个time.Time,表示当前Context应该结束的时间,ok则表示有结束时间</td></tr><tr><td>Done</td><td>当Context被取消或者超时时候返回的一个close的channel,告诉给context相关的函数要停止当前工作然后返回了。(这个有点像全局广播)</td></tr><tr><td>Err</td><td>context被取消的原因</td></tr><tr><td>Value</td><td>context实现共享数据存储的地方,是协程安全的(还记得之前有说过map是不安全的?所以遇到map的结构,如果不是sync.Map,需要加锁来进行操作)</td></tr></tbody></table>

同时包中也定义了提供cancel功能需要实现的接口。这个主要是后文会提到的“取消信号、超时信号”需要去实现。

<pre class="has"><code>// canceler是可以直接取消的接口类型。// 它的具体实现是 cancelCtx 和 timerCtxtype canceler interface { cancel(removeFromParent bool, err error) Done() chan }</code></pre>

那么库里头提供了4个Context实现,来供大家玩耍

<table><tbody><tr><td>实现</td><td>结构体</td><td>作用</td></tr><tr><td>emptyCtx</td><td>type emptyCtx int</td><td>完全的Context,实现的函数也都是返回nil,仅仅只是实现了Context的接口</td></tr><tr><td>cancelCtx</td><td>type cancelCtx struct {    Context    mu    sync.Mutex    done chan struct{}        children map[canceler]struct{}    err error}</td><td>继承自Context,同时也实现了canceler接口</td></tr><tr><td>timerCtx</td><td>type timerCtx struct {    cancelCtx    timer *time.Timer // Under cancelCtx.mu.    deadline time.Time}</td><td>继承自cancelCtx,增加了timeout机制</td></tr><tr><td>valueCtxtype</td><td>type valueCtx struct {    Context    key, val interface{}}</td><td>存储键值对的数据</td></tr></tbody></table>

1.3 context的创建

为了更方便的创建Context,包里头定义了Background来作为所有Context的根,它是一个emptyCtx的实例。

<pre class="has"><code>var ( background = new(emptyCtx)    todo       = new(emptyCtx) )func Background() Context { return background}</code></pre>

你可以认为所有的Context是树的结构,Background是树的根,当任一Context被取消的时候,那么继承它的Context 都将被回收。

2.context实战应用

2.1 WithCancel

实现源码:(源码看着烦的小伙伴,直接看实战)

<pre class="has"><code>func WithCancel(parent Context) (ctx Context, cancel CancelFunc) { c := newCancelCtx(parent) propagateCancel(parent, &c) return &c, func() { c.cancel(true, Canceled) }}</code></pre>

实战场景

执行一段代码,控制执行到某个度的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃0-5个,计算吃到10的用时

实战代码:

<pre class="has"><code>func main() { // 定义一个超时的context ctx, cancel := context.WithCancel(context.Background()) // 调用吃汉堡的业务函数,这边通过通道,得到实时吃汉堡的数量 eatNum := chiHanBao(ctx) // 读取通道拿到数量 for n := range eatNum {    // 一旦数量>=10个,则往context释放停止操作 if n >= 10 { cancel() break } } fmt.Println("正在统计结果。。。") time.Sleep(1 * time.Second)}func chiHanBao(ctx context.Context) chan int { c := make(chan int) // 个数 n := 0 // 时间 t := 0 go func() { for { //time.Sleep(time.Second) select { // 埋点停止 case fmt.Printf("耗时 %d 秒,吃了 %d 个汉堡 \n", t, n) return case c // 每次随机增加至多5个汉堡 incr := rand.Intn(5) n = incr if n >= 10 { n = 10 } // 增加1s时间 t fmt.Printf("我吃了 %d 个汉堡\n", n) } } }() return c}</code></pre>

输出:

<pre class="has"><code>我吃了 1 个汉堡我吃了 3 个汉堡我吃了 5 个汉堡我吃了 9 个汉堡我吃了 10 个汉堡正在统计结果。。。耗时 6 秒,吃了 10 个汉堡</code></pre>

2.2 WithDeadline & WithTimeout

实现源码:(源码看着烦的小伙伴,直接看实战)

<pre class="has"><code>func WithDeadline(parent Context, d time.Time) (Context, CancelFunc) { if cur, ok := parent.Deadline(); ok && cur.Before(d) { // The current deadline is already sooner than the new one. return WithCancel(parent) } c := &timerCtx{ cancelCtx: newCancelCtx(parent), deadline: d, } propagateCancel(parent, c) dur := time.Until(d) if dur <= 0 { c.cancel(true, DeadlineExceeded) // deadline has already passed return c, func() { c.cancel(true, Canceled) } } c.mu.Lock() defer c.mu.Unlock() if c.err == nil { c.timer = time.AfterFunc(dur, func() { c.cancel(true, DeadlineExceeded) }) } return c, func() { c.cancel(true, Canceled) }}func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) { return WithDeadline(parent, time.Now().Add(timeout))}</code></pre>

实战场景:

执行一段代码,控制执行到某个时间的时候,整个程序结束。

吃汉堡比赛,奥特曼每秒吃0-5个,用时10秒,可以吃多少个。

实战代码:

<pre class="has"><code>func main() { // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10))  // 定义10s自动发送停止信号 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) chiHanBao(ctx) defer cancel()}func chiHanBao(ctx context.Context) { n := 0 for { select {    // 时刻接收停止信号 case fmt.Println("stop \n") return    default: // default可以保证未停止前每次都走进来 incr := rand.Intn(5) n = incr fmt.Printf("我吃了 %d 个汉堡\n", n) } time.Sleep(time.Second) }}</code></pre>

输出:

<pre class="has"><code>我吃了 1 个汉堡我吃了 3 个汉堡我吃了 5 个汉堡我吃了 9 个汉堡我吃了 10 个汉堡我吃了 13 个汉堡我吃了 13 个汉堡我吃了 13 个汉堡我吃了 14 个汉堡我吃了 14 个汉堡stop</code></pre>

2.3 WithValue

实现源码:(源码看着烦的小伙伴,直接看实战)

<pre class="has"><code>func WithValue(parent Context, key, val interface{}) Context { if key == nil { panic("nil key") } if !reflect.TypeOf(key).Comparable() { panic("key is not comparable") } return &valueCtx{parent, key, val}}</code></pre>

实战场景:

携带关键信息,为全链路提供线索,比如接入elk等系统,需要来一个trace_id,那WithValue就非常适合做这个事。

实战代码:

<pre class="has"><code>func main() { ctx := context.WithValue(context.Background(), "trace_id", "88888888") // 携带session到后面的程序中去 ctx = context.WithValue(ctx, "session", 1) process(ctx)}func process(ctx context.Context) { session, ok := ctx.Value("session").(int)  fmt.Println(ok) // 读不到session信息,则报错 if !ok { fmt.Println("something wrong") return } // session信息和预想不同,则报错 if session != 1 { fmt.Println("session 未通过") return }  // 读取trace_id信息,和session信息 traceID := ctx.Value("trace_id").(string) fmt.Println("traceID:", traceID, "-session:", session)}</code></pre>

输出:

<pre class="has"><code>traceID: 88888888 -session: 1</code></pre>

3.context建议

不多就一个。

Context要是全链路函数的第一个参数

<pre class="has"><code>func myTest(ctx context.Context) { ...}</code></pre>

您的“在看”,对我很重要?

到此这篇关于“golang int 转 duration_一看就懂系列之Golang的context”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang int 转 duration_一看就懂系列之Golang的context
Golang-context
一看就懂系列之Golang的Map如何做到最省空间?
深入理解Golang之Context(可用于实现超时机制)
golang 扩展package 列表
理解 golang 中的 context(上下文) 包
golang error 包 使用 以及error类型原理
go mod使用
golang context源码学习
想系统学习GO语言(Golang

[关闭]
~ ~