服务器开发利器golang context用法详解
2017-06-29 17:12
525 查看
背景
Context结构
继承的Context
BackGround
WithCancel
withDeadline withTimeout
搜索测试程序
WithValue
go doc中的信息
后记
本文主要基于官方文档Go Concurrency Patterns: Context以及视频Advanced Go Concurrency Patterns的学习而得。
Done 方法在Context被取消或超时时返回一个close的channel,close的channel可以作为广播通知,告诉给context相关的函数要停止当前工作然后返回。
当一个父operation启动一个goroutine用于子operation,这些子operation不能够取消父operation。下面描述的WithCancel函数提供一种方式可以取消新创建的Context.
Context可以安全的被多个goroutine使用。开发者可以把一个Context传递给任意多个goroutine然后cancel这个context的时候就能够通知到所有的goroutine。
Err方法返回context为什么被取消。
Deadline返回context何时会超时。
Value返回context相关的数据。
BackGound是所有Context的root,不能够被cancel。
WithCancel返回一个继承的Context,这个Context在父Context的Done被关闭时关闭自己的Done通道,或者在自己被Cancel的时候关闭自己的Done。
WithCancel同时还返回一个取消函数cancel,这个cancel用于取消当前的Context。
视频Advanced Go Concurrency Patterns中的第一个关于WithCancel的样例代码,本人有所修改
结果
WithTimeout 等价于 WithDeadline(parent, time.Now().Add(timeout)).
对上面的样例代码进行修改
输出
可以看到doStuff在context超时的时候被取消了,ctx.Done()被关闭。
将context.WithDeadline替换为context.WithTimeout
输出
根据视频Advanced Go Concurrency Patterns5分48秒处的代码编写doTimeOutStuff替换doStuff
输出:
context deadline exceeded就是ctx超时的时候ctx.Err的错误消息。
httpDo关键的地方在于
要么ctx被取消,要么request请求出错。
参见搜索程序userip中的代码
关键的代码如下:
里面需要注意的就是 调用CancelFunc会取消child以及child生成的context,取出父context对这个child的引用,停止相关的计数器。
Context结构
继承的Context
BackGround
WithCancel
withDeadline withTimeout
搜索测试程序
WithValue
go doc中的信息
后记
本文主要基于官方文档Go Concurrency Patterns: Context以及视频Advanced Go Concurrency Patterns的学习而得。
背景
在go服务器中,对于每个请求的request都是在单独的goroutine中进行的,处理一个request也可能设计多个goroutine之间的交互, 使用context可以使开发者方便的在这些goroutine里传递request相关的数据、取消goroutine的signal或截止日期。Context结构
// A Context carries a deadline, cancelation signal, and request-scoped values // across API boundaries. Its methods are safe for simultaneous use by multiple // goroutines. type Context interface { // Done returns a channel that is closed when this Context is canceled // or times out. Done() <-chan struct{} // Err indicates why this context was canceled, after the Done channel // is closed. Err() error // Deadline returns the time when this Context will be canceled, if any. Deadline() (deadline time.Time, ok bool) // Value returns the value associated with key or nil if none. Value(key interface{}) interface{} }
Done 方法在Context被取消或超时时返回一个close的channel,close的channel可以作为广播通知,告诉给context相关的函数要停止当前工作然后返回。
当一个父operation启动一个goroutine用于子operation,这些子operation不能够取消父operation。下面描述的WithCancel函数提供一种方式可以取消新创建的Context.
Context可以安全的被多个goroutine使用。开发者可以把一个Context传递给任意多个goroutine然后cancel这个context的时候就能够通知到所有的goroutine。
Err方法返回context为什么被取消。
Deadline返回context何时会超时。
Value返回context相关的数据。
继承的Context
BackGround
// Background returns an empty Context. It is never canceled, has no deadline, // and has no values. Background is typically used in main, init, and tests, // and as the top-level Context for incoming requests. func Background() Context
BackGound是所有Context的root,不能够被cancel。
WithCancel
// WithCancel returns a copy of parent whose Done channel is closed as soon as // parent.Done is closed or cancel is called. func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
WithCancel返回一个继承的Context,这个Context在父Context的Done被关闭时关闭自己的Done通道,或者在自己被Cancel的时候关闭自己的Done。
WithCancel同时还返回一个取消函数cancel,这个cancel用于取消当前的Context。
视频Advanced Go Concurrency Patterns中的第一个关于WithCancel的样例代码,本人有所修改
package main import ( "context" "log" "os" "time" ) var logg *log.Logger func someHandler() { ctx, cancel := context.WithCancel(context.Background()) go doStuff(ctx) //10秒后取消doStuff time.Sleep(10 * time.Second) cancel() } //每1秒work一下,同时会判断ctx是否被取消了,如果是就退出 func doStuff(ctx context.Context) { for { time.Sleep(1 * time.Second) select { case <-ctx.Done(): logg.Printf("done") return default: logg.Printf("work") } } } func main() { logg = log.New(os.Stdout, "", log.Ltime) someHandler() logg.Printf("down") }
结果
E:\wdy\goproject>go run context_learn.go 15:06:44 work 15:06:45 work 15:06:46 work 15:06:47 work 15:06:48 work 15:06:49 work 15:06:50 work 15:06:51 work 15:06:52 work 15:06:53 down
withDeadline withTimeout
WithTimeout func(parent Context, timeout time.Duration) (Context, CancelFunc) WithTimeout returns WithDeadline(parent, time.Now().Add(timeout)).
WithTimeout 等价于 WithDeadline(parent, time.Now().Add(timeout)).
对上面的样例代码进行修改
func timeoutHandler() { // ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) // go doTimeOutStuff(ctx) go doStuff(ctx) time.Sleep(10 * time.Second) cancel() } func main() { logg = log.New(os.Stdout, "", log.Ltime) timeoutHandler() logg.Printf("end") }
输出
15:59:22 work 15:59:24 work 15:59:25 work 15:59:26 work 15:59:27 done 15:59:31 end
可以看到doStuff在context超时的时候被取消了,ctx.Done()被关闭。
将context.WithDeadline替换为context.WithTimeout
func timeoutHandler() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) // go doTimeOutStuff(ctx) go doStuff(ctx) time.Sleep(10 * time.Second) cancel() }
输出
16:02:47 work 16:02:49 work 16:02:50 work 16:02:51 work 16:02:52 done 16:02:56 end
根据视频Advanced Go Concurrency Patterns5分48秒处的代码编写doTimeOutStuff替换doStuff
func doTimeOutStuff(ctx context.Context) { for { time.Sleep(1 * time.Second) if deadline, ok := ctx.Deadline(); ok { //设置了deadl logg.Printf("deadline set") if time.Now().After(deadline) { logg.Printf(ctx.Err().Error()) return } } select { case <-ctx.Done(): logg.Printf("done") return default: logg.Printf("work") } } } func timeoutHandler() { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second)) go doTimeOutStuff(ctx) // go doStuff(ctx) time.Sleep(10 * time.Second) cancel() }
输出:
16:03:55 deadline set 16:03:55 work 16:03:56 deadline set 16:03:56 work 16:03:57 deadline set 16:03:57 work 16:03:58 deadline set 16:03:58 work 16:03:59 deadline set 16:03:59 context deadline exceeded 16:04:04 end
context deadline exceeded就是ctx超时的时候ctx.Err的错误消息。
搜索测试程序
完整代码参见官方文档Go Concurrency Patterns: Context,其中关键的地方在于函数httpDofunc httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error { // Run the HTTP request in a goroutine and pass the response to f. tr := &http.Transport{} client := &http.Client{Transport: tr} c := make(chan error, 1) go func() { c <- f(client.Do(req)) }() select { case <-ctx.Done(): tr.CancelRequest(req) <-c // Wait for f to return. return ctx.Err() case err := <-c: return err } }
httpDo关键的地方在于
select { case <-ctx.Done(): tr.CancelRequest(req) <-c // Wait for f to return. return ctx.Err() case err := <-c: return err }
要么ctx被取消,要么request请求出错。
WithValue
func WithValue(parent Context, key interface{}, val interface{}) Context
参见搜索程序userip中的代码
关键的代码如下:
// NewContext returns a new Context carrying userIP. func NewContext(ctx context.Context, userIP net.IP) context.Context { return context.WithValue(ctx, userIPKey, userIP) } // FromContext extracts the user IP address from ctx, if present. func FromContext(ctx context.Context) (net.IP, bool) { // ctx.Value returns nil if ctx has no value for the key; // the net.IP type assertion returns ok=false for nil. userIP, ok := ctx.Value(userIPKey).(net.IP) return userIP, ok }
go doc中的信息
The WithCancel, WithDeadline, and WithTimeout functions take a Context (the parent) and return a derived Context (the child) and a CancelFunc. Calling the CancelFunc cancels the child and its children, removes the parent's reference to the child, and stops any associated timers.
里面需要注意的就是 调用CancelFunc会取消child以及child生成的context,取出父context对这个child的引用,停止相关的计数器。
后记
之前一直在CSDN上写文章,后面会逐步转换到简书上,还请大家多多支持。相关文章推荐
- 深入Golang之context的用法详解
- 团队开发利器,图文详解windows下创建svn服务器,visualSVN服务端;tortoisesvn;传了30多遍还不知道这次可能传上去
- JBoss4 应用服务器Web开发人员参考手册(6):设置Web应用的Context Root
- Firebug高级用法 - Web开发的利器
- android开发之AIDL用法_进程间通信原理详解
- js - 关于部分浏览器内置函数console详解(用开发调试的利器)
- js - 关于部分浏览器内置函数console详解(用开发调试的利器)
- ASP.NET开发服务器命令行用法
- myeclipse+apache服务器开发javaEE时出现的404,500等错误详解
- 移动平台php服务器开发global.session.php 配置详解
- 读书笔记:Django 模板开发(三)Context详解
- 什么是ACE?为什么它说是开发游戏服务器的利器?
- rails开发利器:简单的push服务器 faye
- Firebug高级用法 - Web开发的利器
- js - 关于部分浏览器内置函数console详解(用开发调试的利器)
- 在equinox环境开发web应用的"利器" -- registerResources()方法 详解
- I/O复用模型之select函数用法——服务器开发
- Android游戏开发教程之三:View类用法详解
- 【Android 应用开发】Android - TabHost 选项卡功能用法详解
- iPhone开发中的nonatomic, retain,weak,strong用法详解