协程在golang中相对比较廉价。在特别在做对比的时候特别有优势。在服务重构切换的时候往往需要实现对比机制。新服务调用旧服务做对比上报。
如果仅仅是用来上报数据。那么就可以异步化,不影响当前的流程。这时候开启协程无疑是最好的办法。
go compareFee(ctx)
在得到当前运费的结果之后,开启协程。让他在新的协程调用旧服务,获取了之后对比完,上报cat 或者其他服务。这个时候整体功能得以实现。但是协程有个问题就是如果出现panic, 会到导致整体服务的panic。
func safeCompareFee(ctx context.Context, goApiResult *EsfResult, data *schema.ApiNewCalculateEsfRequest, calculateData *sfdata.CalculateData, requestID string) {
// protect process
defer func() {
if err := recover(); err != nil {
stack := middleware.Stack(3)
logger.LogErrorf("[Recovery] %s safeCompareFee panic recovered:\n%s\n%s", time.Now().Format("2006/01/02 - 15:04:05"), err, stack, false)
}
}()
logger.SetLogId(requestID)
compareFee(ctx, goApiResult, data, calculateData, requestID)
}
这个时候除了任何的错误都能够恢复。不会影响其他协程。但是这个时候每写一种就需写一个safe函数去做处理,需要在抽象一层出来。否则就会充斥copy。要对defer函数的处理就会需要各个地方修改。
func SafeGoroutineWithRequestID(f func(), requestID string) {
defer func() {
if err := recover(); err != nil {
stack := Stack(3)
logger.LogErrorf("[Recovery] %s %s panic recovered:\n%s\n%s", time.Now().Format("2006/01/02 - 15:04:05"), err, stack, false)
}
}()
logger.SetLogId(requestID)
f()
}
把函数当成参数传入进来,并且传入requestID。这时候就把recover 这一层抽象出来了。
但是问题来了。没有带参数的函数能够很好的放入到这个函数中。比如
func testPanic(){
x, y := 100, 0
c := x/y
fmt.Println(c)
}
如果这个时候传入参数 y 是0 就会触发panic。但是这种函数没有入参。有入参又要怎么处理呢?
使用闭包的方式就可以处理了。
比如上面的comparefee 函数。
f := func() {
compareV2ShippingFee(ctx, currentResult, data, calculateData, requestID)
}
go apiutils.SafeGoroutineWithRequestID(f2, requestID)
使用这种方式就饿能够达到目的。
至此,就能够愉快的开协程了。