go context上下文

1、context 的解释

在 API 之间或者方法调用之间,传递的除了业务参数之外的其他信息,比如 traceId 等。

比如,服务端接收到客户端的 HTTP 请求之后,可以把客户端的 IP 地址和端口、客户端的身份信息、请求接收的时间、Trace ID 等信息放入到上下文中,这个上下文可以在后端的方法调用中传递,后端的业务方法除了利用正常的参数做一些业务处理(如订单处理)之外,还可以从上下文读取到消息请求的时间、Trace ID 等信息,把服务处理的时间推送到 Trace 服务中。Trace 服务可以把同一 Trace ID 的不同方法的调用顺序和调用时间展示成流程图,方便跟踪。

2、context 的作用

  1. 上下文信息传递
  2. 控制子协程的运行
  3. 超时控制的方法调用
  4. 可以取消的方法调用

3、应用场景

上下文信息传递,WithValue 的应用

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	// 父上下文
	ctx := context.Background()
	setValue(ctx)
}

func process(ctx context.Context) {
	name, ok := ctx.Value("name").(string)
	if ok {
		fmt.Printf("process over. name=%s\n", name)
	} else {
		fmt.Printf("process over. no name\n")
	}
}

func setValue(ctx context.Context) {
	process(ctx)
	// 为上下文设置key value
	ctx = context.WithValue(ctx, "name", "liu")
	process(ctx)
}
go run context1.go
process over. no name
process over. name=liu

控制子协程的运行,WithCancel 应用

当系统中有耗时任务时,如果想控制子协程的生命周期可以在主协程调用 cancel 函数,结束子协程的运行。

前提是子协程中对 ctx.Done()做了监听

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx := context.Background()
	cancelGoroutine(ctx)
}

func cancelGoroutine(ctx context.Context) {
	go func() {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("主协程处理结束,子协程结束...")
				return
			case <-time.After(time.Second):
				fmt.Println("处理耗时任务...")
			}
		}
	}()

	ctx, cancel := context.WithCancel(ctx)
	defer cancel()
	fmt.Println("主协程处理逻辑...")
	time.Sleep(5 * time.Second)
	cancel()
	time.Sleep(1 * time.Second)
}
 go run context1.go
主协程处理逻辑...
处理耗时任务...
处理耗时任务...
处理耗时任务...
处理耗时任务...
主协程处理结束,子协程结束...

超时控制,WithTimeout 的应用

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
	ctx := context.Background()
	cancelGoroutine(ctx)
}

func cancelGoroutine(ctx context.Context) {
	go func() {
		for {
			select {
			case <-ctx.Done():
				fmt.Println("主协程处理超时,子协程结束...")
				return
			case <-time.After(time.Second):
			}
		}
	}()

	ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
	defer cancel()
	fmt.Println("主协程处理逻辑...")
	time.Sleep(5 * time.Second)
}
 go run context1.go
主协程处理逻辑...
主协程处理超时,子协程结束...