GoLang Context
介绍
在开发大型应用是,尤其是服务器软件中,有时除了函数自身工作所需的信息之外,了解更多关于它正在执行的环境的信息是有帮助的。例如,web服务器器函数正在处理特定客户端的HTTP请求,则该函数可能只需要知道客户端请求那个URL来提供相应。该函数可能只将该url作为参数,但在服务请求的过程中难免会发生一些意外,例如在客户端收到响应之前断开连接。如果提供相应的函数不知道客户端已断开连接,则服务器软件最终可能会花费比我们期望的时间还要长的时间
在这种情况下,可以通过判断服务端的连接状态,实现服务器在客户端断开后终止掉该连接,可以保证在繁忙的服务器中节省掉部分资源,对u有数据库的连接也同样适用。
这里会首先创建一个在函数中是用上下文的Go程序,然后更新该程序在Context中存储的数据并在另外一个函数中检索它,最后,使用Context完成部分停止的操作
创建上下文
Go中的需要包中都会是用Context,将这些变量作为参数或直接的函数可以是用该变量获取他们正在执行的环境相关的附加信息,一般是在main函数开始创建Context,一直到最下层的函数调用,通过将context的值传递到一个函数中,在sql.DB
中调用QueryContext
,这样就实现了当客户端断开连接时停止还在运行的会话
这里我们会先介绍两种比较简单的context,context.TODO
和context.BackGroup
将会创建一个context,然后在函数dosomething
中是用这个参数
package main
import (
"context"
"fmt"
)
func doSomething(ctx context.Context){
fmt.Println("Do something")
}
func main() {
ctx := context.TODO()
doSomething(ctx)
}
Output
Doing something!
在main
函数中,创建了ctx
并在doSomething
中传入了这个参数,是用context.TODO
的意思是创建一个空的context.Context
,通常用于你不太清楚流程中是否需要context时。
上面实际上只传递了这个ctx的参数,并没有实际是用这个值,并且它是作为函数的第一个参数,名字叫做ctx,标准库中也是这么使用的
当然也可以是用context.Background
这个也是空的,和context.TODO
是一样功能
下面会介绍一些更加复杂的context用于实际使用中
携带数据的context
使用context.WithValue禁止赋值,该函数返回一个新的context,使用ctx.Value("key")获取存储的数据
func doSomething(ctx context.Context) {
fmt.Printf("doSomething: myKey's value is %s\n", ctx.Value("myKey"))
}
func main() {
ctx := context.Background()
ctx = context.WithValue(ctx, "myKey", "myValue")
doSomething(ctx)
}
Output
doSomething: myKey's value is myValue
结束一个context
对于需要考虑到何时结束和完成的场景,context能被以信号的形式使用。通过标识context已经完成,我们知道合适停止对应的函数执行
package main
import (
"context"
"fmt"
"time"
)
func doSomething(ctx context.Context) {
ctx, cancelCtx := context.WithCancel(ctx)
printCh := make(chan int)
go doAnother(ctx, printCh)
for num := 1; num <= 3; num++ {
printCh <- num
}
cancelCtx()
time.Sleep(100 * time.Millisecond)
fmt.Printf("doSomething: finished\n")
}
func doAnother(ctx context.Context, printCh <-chan int) {
for {
select {
case <-ctx.Done():
if err := ctx.Err(); err != nil {
fmt.Printf("doAnother err: %s\n", err)
}
fmt.Printf("doAnother: finished\n")
return
case num := <-printCh:
fmt.Printf("doAnother: %d\n", num)
}
}
}
Output
doAnother: 1
doAnother: 2
doAnother: 3
doAnother err: context canceled
doAnother: finished
doSomething: finished
其他的方法比如说context.WithDeadLine``WithTimeOut
总结
本文中,样例中介绍了context
包的几种使用方式,首先创建一个能够接受context.Context
的函数,使用context.TODO
,context.Backgroupd
来创建对应的空的 context
,使用context.WithValue
天骄不同的值到context
中,使用Value
函数来获取他们,最后使用context.Done()
这个channle来判断ctx是否完成,对应的函数包括WithCancel,WithDeadline,WithTimeOut
三个函数