下面这段代码,你能看出什么问题吗?
伪代码
package main
import (
"context"
"net/http"
)
func main() {
// 模拟创建订单接口
http.HandleFunc("/createOrder", func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
// 创建订单
orderDo := createOrder(ctx, reqParams)
// 异步推送订单到供应链
go SubmitToSupplier(ctx, orderDo)
// 响应用户下单成功
w.WriteHeader(http.StatusOK)
w.Write([]byte(`{"code":"ok"}`))
})
http.ListenAndServe(":8080", nil)
}
问题 1. SubmitToSupplier 运行失败
- 原因:客户端请求取消、超时,或服务端响应结束时,服务端的ctx会被cancel掉,此时SubmitToSupplier中支持ctx相关的操作就不会执行,如一些支持ctx的db、http请求等。同理,GRPC也有相同的问题,一些http框架可自行验证,以防吃BUG。
- 解决:封闭CopyContext方法来复制ctx(保留Value,重写Done方法不返回值,这样就永远不受外部取消了),除了用于异步ctx的传递外,一些不支持整体事务的方法也需要copy,防止数据不一致。
问题 2: 协程执行中断
- 原因:未被封装启动的协程,服务重启时不知道有正在运行的任务,无法做到平滑关闭。
- 解决:封装统一启动协程的方法,程序收到退出信号后,先检查是否有协程未处理完。还有个好处是可以纯一包装recover的处理,防止协程panic导致主进程退出。