go语言协程线程安全 golang线程安全

Go 优雅退出实现方法 amp; context原理1.通过endless包实现
2.通过shutdown实现
在go 1.8.x后,golang在http里加入了shutdown方法,用来控制优雅退出 。什么是优雅退出? 简单说就是不处理新请求 , 但是会处理正在进行的请求,把旧请求都处理完,也就是都response之后 , 那么就退出 。
shutdown通过context上下文实现。
社区里不少http graceful动态重启,平滑重启的库,大多是基于http.shutdown做的 。平滑启动的原理很简单,fork子进程 , 继承listen fd, 老进程优雅退出 。
3.context原理
context是Go并发编程中常用到一种编程模式 。
在并发程序中 , 由于超时、取消操作或者一些异常情况,往往需要进行抢占操作或者中断后续操作 。熟悉channel的朋友应该都见过使用done channel来处理此类问题 。比如以下这个例子:
上述例子中定义了一个buffer为0的channel done, 子协程运行着定时任务 。如果主协程需要在某个时刻发送消息通知子协程中断任务退出 , 那么就可以让子协程监听这个done channel,一旦主协程关闭done channel,那么子协程就可以推出了,这样就实现了主协程通知子协程的需求 。这很好,但是这也是有限的 。
如果我们可以在简单的通知上附加传递额外的信息来控制取消:为什么取消 , 或者有一个它必须要完成的最终期限,更或者有多个取消选项,我们需要根据额外的信息来判断选择执行哪个取消选项 。
考虑下面这种情况:假如主协程中有多个任务1, 2, …m,主协程对这些任务有超时控制;而其中任务1又有多个子任务1, 2, …n,任务1对这些子任务也有自己的超时控制,那么这些子任务既要感知主协程的取消信号 , 也需要感知任务1的取消信号 。
如果还是使用done channel的用法,我们需要定义两个done channel,子任务们需要同时监听这两个done channel。嗯,这样其实好像也还行哈 。但是如果层级更深,如果这些子任务还有子任务,那么使用done channel的方式将会变得非常繁琐且混乱 。
我们需要一种优雅的方案来实现这样一种机制:
这个时候context就派上用场了 。
我们首先看看context的结构设计和实现原理 。
先看Context接口结构,看起来非常简单 。
Context接口包含四个方法:
可以看到Done方法返回的channel正是用来传递结束信号以抢占并中断当前任务;Deadline方法指示一段时间后当前goroutine是否会被取消;以及一个Err方法,来解释goroutine被取消的原因;而Value则用于获取特定于当前任务树的额外信息 。而context所包含的额外信息键值对是如何存储的呢?其实可以想象一颗树,树的每个节点可能携带一组键值对,如果当前节点上无法找到key所对应的值,就会向上去父节点里找 , 直到根节点 , 具体后面会说到 。
emptyCtx是一个int类型的变量,但实现了context的接口 。emptyCtx没有超时时间,不能取消,也不能存储任何额外信息 , 所以emptyCtx用来作为context树的根节点 。
但我们一般不会直接使用emptyCtx,而是使用由emptyCtx实例化的两个变量,分别可以通过调用Background和TODO方法得到,但这两个context在实现上是一样的 。那么Background和TODO方法得到的context有什么区别呢?可以看一下官方的解释:
Background和TODO只是用于不同场景下:
Background通常被用于主函数、初始化以及测试中,作为一个顶层的context , 也就是说一般我们创建的context都是基于Background;
而TODO是在不确定使用什么context的时候才会使用 。
下面将介绍两种不同功能的基础context类型:valueCtx和cancelCtx。

推荐阅读