golang 并发控制设计中的“流式模型”
Good artists copy; great artists steal
-- Steve Jobs
并发控制设计的目的
1 高效利用 I/O
2 高效利用多核
附加的还有:处理失败的方法应该干净清晰
有哪些问题要注意
1 上下游之间传递数据,会发生阻塞
2 下游不想继续上游信息怎么办
3 怎么把上下游快速关闭
并发的基本单元是 goroutine, 如何管理好大量的 goroutine,有序的完成任务,高效的使用资源,不发生死锁,资源泄露等问题。那么协调大量 goroutine 的工作要交给 channel 来完成。因为 go 语言的并发设计的初衷就是用通信来实现信息共享。
Don't communicate by sharing memory; share memory by communicating
那么反过来,就是传统语言的并发模型设计的基本思想。
那 golang 是如何做的呢?
高效的处理大量的数据和请求,golang 采用了一种类似大数据计算框架的思维,他把大量的数据和请求想象成了无穷无尽的 stream. 这些 stream 在一段一段的 pipeline 中间流过,每一个 pipelin 中完成一定的操作,并且通过 channel 把数据发送到下游的步骤的 pipelin 当中,而在一个 stage 当中完成的操作,会有大量的 goroutine 来完成,这样任务数据可以被多个 function 可以从 channel 读数据,就可分摊到多核上来计算完成,这也就达成了高效利用 cpu/IO 的目标。这就是所谓的扇出(fan-out,不好懂,魔法打败魔法的感觉)
也可以一个 funciton 从多个 input 的 channel 读取数据,这样就可以用一个 channel,通过多路复用的方式读取多个 channel 的输入,是不是联想到了 netty 的 NIO 模型。无独有偶,这叫扇入(fan-in)。
至此,设计目标里的前 2 个已经大致实现了。
goroutine 资源回收
处理数据传递异常的话,开启了那么多 goroutine 的资源怎么回收回来?
比如下游 goroutine 在等数据,数据由于上游的原因,一等不来二等不来,怎么都不来,自己怎么自生自灭。或者下游消费不了,上游到下游传递数据的通路阻塞,怎么以失败退出,自行了结。golang 使用了 context 中的 done channel,用来发送结束信号,做关闭 goroutine 的操作。
Go 语言的并发模型之 context
处理请求的一组 goroutine 通常需要访问特定于请求的值,例如最终用户的身份、授权令牌和请求的截止日期。当请求被取消或超时时,所有处理该请求的 goroutines 都应该快速退出,以便系统可以回收它们正在使用的任何资源。
Go 程序员将Context
参数作为第一个参数传递给传入和传出请求之间的调用路径上的每个函数。这使得许多不同团队开发的 Go 代码能够很好地互操作。它提供了对超时和取消的简单控制,并确保安全凭证等关键值正确传输 Go 程序
Background
is the root of anyContext
tree,it is never canceled:
context 支持将现有 context 派生出新的 context,形成一颗树,一旦根 context 关闭,派生出来的 context 也会关闭。但是一棵树上的 goroutine 想要结束的时间是不一致的,子节点的生命周期可能会短于父亲节点,那么如何来控制派生子几点的生命周期 context 呢?这边有两个参数:
WithCancel is also useful for canceling redundant requests when using multiple replicas.
WithTimeout is useful for setting a deadline on requests to backend servers:
一个用来关掉多余的请求,一个用来关闭超时的请求
版权声明: 本文为 InfoQ 作者【Luke】的原创文章。
原文链接:【http://xie.infoq.cn/article/3ee147762740ce672439391a6】。文章转载请联系作者。
评论