优雅退出
监听系统信号
服务器难免要遇到重启、升级的问题。那么当我们的服务器关闭的时候,我们需要考虑:
- 拒绝新的请求
- 等待当前的所有请求处理完毕
- 释放资源
- 关闭服务器
- 如果这中间超时了, 我们要强制关闭了
基础语法 —— goroutine
- 一段异步执行的代码
- 用关键字 go 启动
channel 与 select
基础语法—— channel
- 使用 make 创建 channel
- 带缓冲和不带缓冲的channel
- 用 <- 符号来表达收发
基础语法 —— channel 带缓冲
- 放满阻塞生产者
- 空了阻塞消费者
基础语法—— channel 不带缓冲
- 缺了任何一边都要阻塞另外一边
基础语法 —— select
- 等待多个 channel
- select 比较常见和 for 循环一起使用
基础语法 —— Go源文件命名与平台
- 模式:name_platform_arch.go
- 一般最多就是根据 OS 来分。因为到了区分架构那个地步,就太底层了
Hook(钩子函数)设计
Http Server —— 注册 Hook
- 我们这 WaitForShutdown 啥都没干,而我们是期望它能够完成拒绝新请求,等待旧请求,然后再我们改造一下 WaitForShutdown 让它可以接收Hook。
- 考虑到我们的 Hook 可能写出 BUG 无法退出,所以我们还需要超时机制。
- 我们利用 context 包来达成目标
Http Server —— 关闭动作
按照我们的设计,我们需要:
- 拒绝新的请求:需要一个开关
- 等待当前的所有请求处理完毕:维持请求计数
- 释放资源:用户可能需要自己释放一些资源
- 关闭服务器:把所有启动的 Server 都关闭了
- 如果这中间超时了, 我们要强制关闭了
Http Server —— 拒绝请求并记录
我们这 WaitForShutdown 啥都没干,按照我们的设计,我们需要:
- 拒绝新的请求:需要一个开关
- 等待当前的所有请求处理完毕:需要维持请求计数
我们需要注册一个 Hook,在收到信号之后关闭开关,然后停下来等待
这是一个很典型的 AOP 场景,所以我们可以考虑使用 filter 来判断要不要拒绝请求,并且维持请求计数
context 与 atomic
基础语法 —— context 包
context.Context 是 Go 提供的线程安全工具,称为上下文。
方法:
• WithTimeout:一般用户控制超时
• WithCancel:用于取消整条链上的任务
• WithDeadline:控制时间
• WithValue:往里面塞入 key-value
• Backgroud:返回一个空的 context.Context
• ToDo:返回一个空的 context.Context,但是这个标记着你也不知道传什么
基础语法 —— context 与 thread-local
- Go 官方没有支持 thread-local(或者说 goroutine-local)
- 第三方有人搞出来了类似的东西,但是一般不建议使用,因为实现太奇诡,大部分人都没胆子用在自己的
项目上 - 因为缺乏 thread-local,所以很多时候我们要实现类似的功能,都只能依赖于 context 在方法直接传递。
因此一般建议自己的方法签名,都把 context.Context 作为第一个参数
基础语法 —— atomic 包
方法分成这几类:
• AddXXX:操作一个数字类型,加上一个数字
• LoadXXX:读取一个值
• CompareAndSwapXXX:大名鼎鼎的 CAS 操作
• StoreXXX:写入一个值
• SwapXXX:写入一个值,并且返回旧的值。
它和 CompareAndSwap 的区别在于它不关心旧的值是什么
• unsafepointer 相关方法,不建议使用。
难写也难读,不到逼不得已不要去用。尤其是不要为了优化而故意用 unsafepoint