1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        終于解決了這個(gè)線上偶現(xiàn)的panic問(wèn)題

        共 4322字,需瀏覽 9分鐘

         ·

        2021-12-18 14:12

        不知道其他人是不是這樣,反正老許最怕聽(tīng)到的詞就是“偶現(xiàn)”,至于原因我不多說(shuō),懂的都懂。

        下面直接看panic信息。

        runtime?error:?invalid?memory?address?or?nil?pointer?dereference

        panic(0xbd1c80,?0x1271710)
        ????????/root/.go/src/runtime/panic.go:969?+0x175
        github.com/json-iterator/go.(*Stream).WriteStringWithHTMLEscaped(0xc00b0c6000,?0x0,?0x24)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/stream_str.go:227?+0x7b
        github.com/json-iterator/go.(*htmlEscapedStringEncoder).Encode(0x12b9250,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/config.go:263?+0x45
        github.com/json-iterator/go.(*structFieldEncoder).Encode(0xc002e9c8d0,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect_struct_encoder.go:110?+0x78
        github.com/json-iterator/go.(*structEncoder).Encode(0xc002e9c9c0,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect_struct_encoder.go:158?+0x3f4
        github.com/json-iterator/go.(*structFieldEncoder).Encode(0xc002eac990,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect_struct_encoder.go:110?+0x78
        github.com/json-iterator/go.(*structEncoder).Encode(0xc002eacba0,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect_struct_encoder.go:158?+0x3f4
        github.com/json-iterator/go.(*OptionalEncoder).Encode(0xc002e9f570,?0xc006b18b38,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect_optional.go:70?+0xf4
        github.com/json-iterator/go.(*onePtrEncoder).Encode(0xc002e9f580,?0xc0096c4c00,?0xc00b0c6000)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect.go:219?+0x68
        github.com/json-iterator/go.(*Stream).WriteVal(0xc00b0c6000,?0xb78d60,?0xc0096c4c00)
        ????????/go/pkg/mod/github.com/json-iterator/[email protected]/reflect.go:98?+0x150
        github.com/json-iterator/go.(*frozenConfig).Marshal(0xc00012c640,?0xb78d60,?0xc0096c4c00,?0x0,?0x0,?0x0,?0x0,?0x0)

        首先我堅(jiān)信一條,開(kāi)源的力量值得信賴。因此老許第一波操作就是,分析業(yè)務(wù)代碼是否有邏輯漏洞。很明顯,同事也是值得信賴的,因此果斷猜測(cè)是某些未曾設(shè)想到的數(shù)據(jù)觸發(fā)了邊界條件。接下來(lái)就是保存現(xiàn)場(chǎng)的常規(guī)操作。

        如標(biāo)題所說(shuō),這是偶現(xiàn)的panic問(wèn)題,因此按照上面的分類采用符合當(dāng)前技術(shù)棧的方法保存現(xiàn)場(chǎng)即可。接下來(lái)就是坐等收獲的季節(jié),而這一等就是好多天。中間數(shù)次收到告警,卻沒(méi)有符合預(yù)期的現(xiàn)場(chǎng)。

        這個(gè)時(shí)候我不僅不慌,甚至還有點(diǎn)小激動(dòng)。某某曾曰:“要敢于質(zhì)疑,敢于挑戰(zhàn)權(quán)威”,一念至此便一發(fā)不可收拾,我老許又要為開(kāi)源事業(yè)做出貢獻(xiàn)了嘛!說(shuō)干就敢干,懷著小心思開(kāi)始閱讀json-iterator的源碼。

        剛開(kāi)始研讀我便明白了一個(gè)道理, “當(dāng)上帝關(guān)了這扇門(mén),一定會(huì)為你打開(kāi)另一扇門(mén)”這句話是騙人的。老許只覺(jué)得上帝不僅關(guān)上了所有的門(mén)甚至還關(guān)上了所有的窗。下面我們看看他到底是怎么關(guān)門(mén)的。

        func?(cfg?*frozenConfig)?Marshal(v?interface{})?([]byte,?error)?{
        ?stream?:=?cfg.BorrowStream(nil)
        ?defer?cfg.ReturnStream(stream)
        ?stream.WriteVal(v)
        ?if?stream.Error?!=?nil?{
        ??return?nil,?stream.Error
        ?}
        ?result?:=?stream.Buffer()
        ?copied?:=?make([]byte,?len(result))
        ?copy(copied,?result)
        ?return?copied,?nil
        }


        //?WriteVal?copy?the?go?interface?into?underlying?JSON,?same?as?json.Marshal
        func?(stream?*Stream)?WriteVal(val?interface{})?{
        ?if?nil?==?val?{
        ??stream.WriteNil()
        ??return
        ?}
        ?//?省略其他代碼
        }

        根據(jù)panic棧知道是因?yàn)榭罩羔樤斐闪藀anic,而(*frozenConfig).Marshal函數(shù)內(nèi)部已經(jīng)做了非空判斷。到此,老許真的已經(jīng)別無(wú)他法只得戰(zhàn)略性放棄解決此次panic。畢竟,這個(gè)影響也沒(méi)那么大,而且程序員哪有修的完的bug呢。經(jīng)過(guò)這樣一番安慰,心里確實(shí)容易接受多了。

        事實(shí)上,在較長(zhǎng)一段時(shí)間內(nèi)我都有意識(shí)地忽略這個(gè)問(wèn)題,畢竟沒(méi)有找到問(wèn)題的根因。這個(gè)問(wèn)題在線上一直持續(xù)到一個(gè)說(shuō)不上來(lái)什么日子的日子,總而言之就是興致來(lái)了,我再次看了兩眼,而這兩眼很關(guān)鍵!

        func?doReq()?{
        ????req?:=?paramsPool.Get().(*model.Params)
        ????//?defer?1
        ????defer?func()?{
        ?????reqBytes,?_?:=?json.Marshal(req)
        ?????//?省略其他打印日志的代碼
        ????}()
        ????//?defer?2
        ????defer?paramsPool.Put(req)
        ????//?req初始化以及發(fā)起請(qǐng)求和其他操作
        }

        注:

        1. 上述代碼變量命名已經(jīng)被老許通用化處理。
        2. 項(xiàng)目中實(shí)際代碼遠(yuǎn)比上述復(fù)雜,但上述代碼依舊是造成本次問(wèn)題的最小原型。

        上面代碼中paramsPoolsync.Pool類型的變量,而sync.Pool想必大家都很熟悉。sync.Pool是為了復(fù)用已經(jīng)使用過(guò)的對(duì)象(協(xié)程安全),減少內(nèi)存分配和降低GC壓力。

        type?test?struct?{
        ?a?string
        }

        var?sp?=?sync.Pool{
        ?New:?func()?interface{}?{
        ??return?new(test)
        ?},
        }

        func?main()?{
        ?t?:=?sp.Get().(*test)
        ?fmt.Println(unsafe.Pointer(t))
        ?sp.Put(t)
        ?t1?:=?sp.Get().(*test)
        ?t2?:=?sp.Get().(*test)
        ?fmt.Println(unsafe.Pointer(t1),?unsafe.Pointer(t2))
        }

        根據(jù)上述代碼和輸出結(jié)果知,t1變量和t變量地址一致,因此他們是復(fù)用對(duì)象。此時(shí)再回顧上面的doReq函數(shù)就很容易發(fā)現(xiàn)問(wèn)題的根因。

        defer 2defer 1順序反了!??!

        defer 2defer 1順序反了?。?!

        defer 2defer 1順序反了!?。?/p>

        sync.Pool提供的GetPut方法是協(xié)程安全的,但是高并發(fā)調(diào)用doReq函數(shù)時(shí)json.Marshal(req)和請(qǐng)求初始化會(huì)存在并發(fā)問(wèn)題,極有可能引起panic的并發(fā)調(diào)用時(shí)間線如下圖所示。

        既然已經(jīng)找到原因,解決起來(lái)就容易多了,只需調(diào)整defer 2defer 1的調(diào)用順序即可。老許將修改后的代碼發(fā)布到線上后也確實(shí)再?zèng)]有出現(xiàn)panic。造成這次事故的根本原因是一個(gè)微乎其微的細(xì)節(jié),所以我們平時(shí)在開(kāi)發(fā)中還是要謹(jǐn)慎加謹(jǐn)慎,避免因?yàn)檫@種小白錯(cuò)誤造成不可挽回的損失。另外一個(gè)經(jīng)驗(yàn)之談就是,開(kāi)發(fā)和查問(wèn)題時(shí)盡量不要鉆牛角尖,適當(dāng)?shù)耐nD可能會(huì)有意想不到的奇效。

        最后,衷心希望本文能夠?qū)Ω魑蛔x者有一定的幫助。


        推薦閱讀


        福利

        我為大家整理了一份從入門(mén)到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門(mén)看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

        瀏覽 77
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            欧美在线日本 | 快色网站 | 久久天天综合桃花久久 | 在线天堂v | 最近中文字幕mv第一季歌词免费 | 人人玩人人操 | 国产乱妇交换做爰XXXⅩ麻豆 | 日韩xxx视频 | 久久精品成人 | 俺去也av |