如何在Go中使用context实现请求并发控制

1. Go中的context简介

Go语言中的 context 包是用来在 Go 程序中承载 request-scoped 的数据、取消信号、及截止时间的。对于 HTTP 请求处理器而言,它们需要为每一个载入到服务器的请求生成一个处理 Context。

1.1 context的类型

context包中两种类型的context:

context.Background():代表整个请求生命期内存在的一个上下文,因为它是在运行一个方法或进入后台队列的时候,最基础且最初“含量”的 context。

context.WithCancel(parentCtx):父级上下文会传递给它的子进程,直到取消或在超时后关闭。

2. Go中使用context控制请求并发

在 Go 语言中使用 Context 可以轻松实现请求并发的控制。

2.1 实现方式

使用 context 包中的 WithDeadline、WithTimeout 或 WithCancel 函数将一个 Context 与一个单独的 goroutine 中的代码实例关联起来。这些函数返回的 Context 包含一个 Done 通道,它会在以下情况下关闭:

由于父级 Context 的 Done 通道已关闭,注意 Context 是可嵌套传递的。

在调用 WithCancel 时手动调用了 cancelFunc 函数。

在 WithDeadline 或 WithTimeout 中提供的截止时间到期。

并发请求控制是通过在协程中检查 Context 是否关闭来实现的:当 Context 已经关闭并且已经准备取消时,可以退出或者释放正在执行的资源。我们只需要用一个 mutex 来控制并行请求的数量:

 package main

import (

"context"

"fmt"

"sync"

"time"

)

func main() {

ctx, cancel := context.WithCancel(context.Background())

defer cancel()

reqNum := 100

var wg sync.WaitGroup // wait for all go-routines to complete

var mu sync.Mutex // protect counter

var counter int

limit := 3 // limit concurrency

for i := 0; i < reqNum; i++ {

wg.Add(1) // new go-routine to wait for

go func(i int) {

defer wg.Done() // signaled when finished

select {

case <-ctx.Done(): // balance cancellation signal with work performed

fmt.Println("cancelled:", i)

return

default:

}

if err := doSomething(ctx); err != nil { // pass along cancellation signal

fmt.Printf("Do something failed with %s\n", err)

}

// mutex to protect counter

mu.Lock()

defer mu.Unlock()

counter++

}(i)

for (i+1)%limit == 0 { // throttle limit concurrency

time.Sleep(time.Second * 1)

}

}

wg.Wait()

fmt.Println("completed:", counter)

}

func doSomething(ctx context.Context) error {

select {

case <-ctx.Done(): // check for cancellation

return ctx.Err()

default:

}

return nil

}

2.2 实例解析

以上为使用 context 控制并发的示例代码,示例代码中使用了 doSomething 函数。doSomething 函数只是检查当前 context 是否已经关闭,如果关闭,则立即返回 ctx.Err();同样的,main 函数也会调用 doSomething 函数,通过 select 语句来判断 context 是否已经关闭,并将当前 context 传递给 doSomething 函数以便它能检查当前 context 的状态。如果有一个 doSomething 函数正在执行,并且 context 被取消,则 select 语句中的 case 就会执行。

2.3 总结

由于 context 包可以轻松地实现请求并发控制,因此它应该经常用于编写 WebClient、gRPC Web 应用程序等网络请求处理代码,以确保应用程序在高负载情况下可靠地运行。

3. 总结

本文介绍了Go语言中的context模块的作用,以及如何使用它来控制请求的并发。通过代码示例的解析,我们可以看到,context包可以轻松地实现请求并发控制,因此在编写网络请求处理代码时,要充分利用此特性,以确保应用程序在高负载情况下可靠地运行。

后端开发标签