Golang 类型断言引发的一系列线上问题(协程通道有进不出,协程异常终止,interface断言(dotType方式)不匹配的类型会panic,)

问题引出场景

近期公司某个功能模块线上运行异常。异常情况:某个接口阻塞不通,但服务状态OK. heart-beat /ping 可以通;

代码背景:由于需要防止以避免服务器资源耗尽,接口内部使用了 全局的channel 缓冲通道 对该接口进行全局访问次数限制,此处贴代码
在这里插入图片描述
从执行顺序上看,似乎没有什么问题。但真实运行后问题出在了业务 fn()方法内部,重点 :fn方法若内部异常Panic时,被 recover 以后,是不会 走到 业务的出通道里,因此导致此次业务任务的通道一直被占用。重复70次同样类型数据的接口请求就把通道沾满了。。结果就导致线上该接口调用的心情求会一直阻塞,根本无法使用。

排查和定位异常问题

1.在上边描述了该问题导致的线上接口无法使用,但服务容器状态却又OK。毫无疑问,直接就定位到了 channel 的 output (出通道)阻塞。仔细debug一圈,发现接口在接受 特定参数 的时候,会引发fn()内部某处代码 panic,恢复后 就不会进行到预先码好的 <- runlimit 里
2.仔细想想,能导致golang 程序Panic的,其实不做recover()还好,直接就挂服务提示哪有问题,做了(好处就是不挂service)后便更难发现错误。。,也就那几种基本情况,编码时还须认真仔细做好规避。
这里贴一个例子

package main

import "fmt"

func main(){

    defer func(){ // 必须要先声明defer,否则不能捕获到panic异常
        fmt.Println("c")
        if err:=recover();err!=nil{
            fmt.Println(err) // 这里的err其实就是panic传入的内容,55
        }
        fmt.Println("d")
    }()

    f()
}

func f(){
    fmt.Println("a")
    panic(55)
    fmt.Println("b")
    fmt.Println("f")
}


输出结果:

a
c
d
exit code 0, process exited normally.//这个就是

查看fn()内部业务代码,与上述代码一样,执行到recover后就中断了当前协程的后续函数调用栈的执行,不会走到上边的 <-runlimit;recover代码贴下
在这里插入图片描述
因为 recover 主要用于无法预知的错误。例如:数组方面、map
例如:数组索引越界,map key不存在,interface 类型断言与指针所指向的值类型不符合时会 Panic 被recover捕获到
,围绕这个规律定位到了 fn()内部不严谨的代码
1.debug到这里
在这里插入图片描述

2.向上一个stack jump一下就发现了
在这里插入图片描述

图片内表述的 是 stateText 内部的 shareType 默认被json代码包转换为了 float64 并非 int,代码里断言该interface 为 int 时进入了 panic assert error: panicDotType Error。类型断言失败!!!

解决思路,在不确定Interface{}内部的类型是,需要用严谨的标准方式对其进行双返回值接受断言。代码如下:

if stateText, ok := params.Data.StateText.(map[string]interface{}); ok {
			}

这种方式可以有效避免类型错误而引发的panic.
另外,各位同学在编码习惯时,索引越界,Map key不存在,类型断言都需要注意一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值