FCon7折倒计时最后一周:日程已上线70%!查看详情>>> 了解详情
写点什么

Go 上下文取消操作

  • 2019-11-24
  • 本文字数:2912 字

    阅读完需:约 10 分钟

Go 上下文取消操作

本篇文章将解释我们如何利用上下文库的取消特性,并通过一些模式和最佳实践来使用取消,使你的程序更快、更健壮。


许多使用 Go 的人,都会用到它的上下文库。大多数使用 context 进行下游操作,比如发出 HTTP 调用,或者从数据库获取数据,或者在协程中执行异步操作。最常见的用法是传递可由所有下游操作使用的公共数据。然而,一个不太为人所知,但非常有用的上下文特性是,它能够在中途取消或停止一个操作。


本篇文章将解释我们如何利用上下文库的取消特性,并通过一些模式和最佳实践来使用取消,使你的程序更快、更健壮。

为什么需要取消?

简而言之,我们需要取消,以防止我们的系统做不不需要的工作。


考虑 HTTP 服务器对数据库的调用的常见情况,并将查询的数据返回给客户端:



时间图,如果一切都很完美,就会是这样的:



但是,如果客户端取消了中间的请求,会发生什么呢?例如,如果客户端关闭了他们的浏览器,这可能会发生。如果没有取消,应用服务器和数据库将继续执行它们的工作,即使工作的结果将被浪费:



理想情况下,如果我们知道进程(在本例中是 HTTP 请求)停止了,我们希望流程的所有下游组件停止工作:


1 上下文取消

现在我们知道了为什么需要取消,让我们来看看如何实现它。因为“取消”的事件与交易或正在执行的操作高度相关,所以它与上下文捆绑在一起是很自然的。


取消的有两个方面,你可能想要实现:


1.监听取消事件


2.提交取消事件

2 监听取消事件

上下文类型提供了 Done() 方法,每当上下文收到取消事件时,它都会返回接收空 struct{} 类型的通道。监听取消事件就像等待 <-ctx.done() 一样简单。


例如,让我们考虑一个 HTTP 服务器,它需要两秒钟来处理一个事件。如果在此之前请求被取消,我们希望立即返回:


func main() {        // Create an HTTP server that listens on port 8000  http.ListenAndServe(":8000", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {    ctx := r.Context()    // This prints to STDOUT to show that processing has started    fmt.Fprint(os.Stdout, "processing request\n")    // We use `select` to execute a peice of code depending on which    // channel receives a message first    select {             case <-time.After(2 * time.Second):       // If we receive a message after 2 seconds      // that means the request has been processed      // We then write this as the response      w.Write([]byte("request processed"))             case <-ctx.Done():       // If the request gets cancelled, log it      // to STDERR      fmt.Fprint(os.Stderr, "request cancelled\n")    }  }))}
复制代码


你可以通过运行服务器并在浏览器上打开 localhost:8000 来测试。如果你在 2 秒前关闭浏览器,你应该会看到在终端窗口上打印的“请求取消”。

3 提交取消事件

如果你有一个可以被取消的操作,你将不得不通过上下文发出取消事件。这可以通过 context 包中的 WithCancel 函数来完成,它返回一个上下文对象和一个函数。这个函数没有参数,也不返回任何东西,当你想要取消上下文时调用。


考虑两个从属操作的情况。在这里,“依赖”意味着如果一个失败了,另一个就没有意义了。在这种情况下,如果我们在早期就知道其中一个操作失败了,我们想要取消所有的依赖操作。


func operation1(ctx context.Context) error {  // Let's assume that this operation failed for some reason  // We use time.Sleep to simulate a resource intensive operation  time.Sleep(100 * time.Millisecond)   return errors.New("failed")}
func operation2(ctx context.Context) { // We use a similar pattern to the HTTP server // that we saw in the earlier example select { case <-time.After(500 * time.Millisecond): fmt.Println("done") case <-ctx.Done(): fmt.Println("halted operation2") }}
func main() { // Create a new context ctx := context.Background()
// Create a new context, with its cancellation function // from the original context ctx, cancel := context.WithCancel(ctx)
// Run two operations: one in a different go routine go func() { err := operation1(ctx) // If this operation returns an error // cancel all operations using this context if err != nil { cancel() } }() // Run operation2 with the same context we use for operation1 operation2(ctx)}
复制代码

4 基于时间取消

任何需要在请求的最大持续时间内维护 SLA(服务水平协议)的应用程序都应该使用基于时间的取消。该 API 几乎与前面的示例相同,并添加了一些内容:


// The context will be cancelled after 3 seconds// If it needs to be cancelled earlier, the `cancel` function can// be used, like before
ctx, cancel := context.WithTimeout(ctx, 3*time.Second)
// The context will be cancelled on 2009-11-10 23:00:00ctx, cancel := context.WithDeadline(ctx, time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC))
复制代码


例如,考虑对外部服务进行 HTTP API 调用。如果服务花费的时间太长,最好是尽早失败并取消请求:


func main() {  // Create a new context  // With a deadline of 100 milliseconds  ctx := context.Background()  ctx, _ = context.WithTimeout(ctx, 100*time.Millisecond)  // Make a request, that will call the google homepage  req, _ := http.NewRequest(http.MethodGet, "http://google.com", nil)  // Associate the cancellable context we just created to the request  req = req.WithContext(ctx)
// Create a new HTTP client and execute the request client := &http.Client{} res, err := client.Do(req)
// If the request failed, log to STDOUT if err != nil { fmt.Println("Request failed:", err) return }
// Print the statuscode if the request succeeds fmt.Println("Response received, status code:", res.StatusCode)}
复制代码


根据谷歌主页对你的请求的响应速度,你将收到:


Response received, status code: 200
复制代码


或者


Request failed: Get http://google.com: context deadline exceeded
复制代码


你可以使用超时来实现上述两个结果。

陷阱和警告

尽管 Go 的上下文取消是一个通用的工具,但是在继续之前,有一些事情是你应该记住的。其中最重要的一点是,上下文只能被取消一次。


如果你想在同一个操作中提出多个错误,那么使用上下文取消可能不是最好的选择。使用取消的最惯用的方法是,当你真正想要取消某些东西时,而不仅仅是通知下游进程,错误已经发生了。


你需要记住的另一件事是,相同的上下文实例应该传递给所有你可能想要取消的功能和例程。用 WithTimeout 或 WithCancel 来包装已经可取消的上下文将会导致多种可能性,你的上下文可以被取消,并且应该避免。


本文转载自公众号 360 云计算(ID:hulktalk)。


原文链接:


https://mp.weixin.qq.com/s/U3nZPNytE2VnU9vsfSk4Sw


2019-11-24 19:451683

评论 1 条评论

发布
用户头像
对于应用程序发起的数据库操作或者相似的异步|同步IO操作是取消不了的,所以局限性挺大,目前来看积极意义只有取消通知应用和超时返回
2020-04-21 10:20
回复
没有更多了
发现更多内容

迎战大厂!“金三银四”和春招通过率达95%的Java面试要点集锦

Java 程序员 架构 面试

(28DW-S8-Day13) 在线教育班型和角色

mtfelix

28天写作

windows 搭建ftp服务

xiezhr

vsftpd ftp ftp服务 文件服务 3月日更

世界经济论坛:四大区块链趋势将在今年绽放异彩

CECBC

区块链

智慧党建平台解决方案--高效开展党建工作

13530558032

git 教程 --git cherry-pick 命令

生之欢愉,时间同行

git 程序员 git cherry-pick

11.react concurrent mode(并发模式是什么样的)

全栈潇晨

React React Hooks

Promise原理及常用操作

花明

Python学习心得

张鹤羽

28天写作 3月日更

【回溯算法】经典题:求目标和的组合方案 ...

宫水三叶的刷题日记

面试 LeetCode 数据结构与算法

三十而已

ES_her0

28天写作 3月日更

科学的互联网思想 指引我国网络强国建设稳步前行

CECBC

网络安全

10.scheduler&lane模型(来看看react是暂停、继续和插队的)

全栈潇晨

源码分析 React React Hooks

2021程序员春招必备:Java面试知识点+答案(7大分类 5000字解析)

比伯

Java 编程 架构 面试 程序人生

无线网络的用户隔离功能

区块链产业革命:解决融资租赁之谜

CECBC

区块链

Docker的三言两语-基础篇

一个大红包

Docker 28天写作 3月日更

Mysql安装

Sakura

程序员成长第十九篇:要不要转管理岗?

石云升

程序员 28天写作 职场经验 管理经验 3月日更

Kubelet从入门到放弃系列:GPU加持

DCOS

AI gpu Kubernetes 云原生

区块链电子合同--电子合同区块链签约平台

13530558032

12.手写迷你react(短小精悍就是我)

全栈潇晨

源码分析 React React Hooks

9.hooks源码(想知道Function Component是怎样保存状态的嘛)

全栈潇晨

源码分析 React React Hooks

通俗易懂!看了不会忘的网络面试知识点

编程 架构 面试

程序员之禅(二)

每天读本书

读书笔记 每天读本书

dubbo 源码 v2.7 分析:通信过程及序列化协议

程序员架构进阶

架构 RPC 七日更 dubbo源码 3月日更

项目汇报会复盘

Geek_XOXO

一桶食用油的数字化

吴俊宇

数字化转型 鲁花

白话Go内存模型&Happen-Before

Gopher指北

Go 语言

一场关于代码注释的争执,引发的三点思考

架构精进之路

编码 经验分享 七日更 3月日更

Wireshark数据包分析学习笔记Day1

穿过生命散发芬芳

Wireshark 数据包分析 3月日更

  • 扫码添加小助手
    领取最新资料包
Go 上下文取消操作_文化 & 方法_360云计算_InfoQ精选文章