<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 4322字,需瀏覽 9分鐘

           ·

          2021-12-18 14:12

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

          下面直接看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)

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

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

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

          剛開始研讀我便明白了一個道理, “當(dāng)上帝關(guān)了這扇門,一定會為你打開另一扇門”這句話是騙人的。老許只覺得上帝不僅關(guān)上了所有的門甚至還關(guān)上了所有的窗。下面我們看看他到底是怎么關(guā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棧知道是因為空指針造成了panic,而(*frozenConfig).Marshal函數(shù)內(nèi)部已經(jīng)做了非空判斷。到此,老許真的已經(jīng)別無他法只得戰(zhàn)略性放棄解決此次panic。畢竟,這個影響也沒那么大,而且程序員哪有修的完的bug呢。經(jīng)過這樣一番安慰,心里確實容易接受多了。

          事實上,在較長一段時間內(nèi)我都有意識地忽略這個問題,畢竟沒有找到問題的根因。這個問題在線上一直持續(xù)到一個說不上來什么日子的日子,總而言之就是興致來了,我再次看了兩眼,而這兩眼很關(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ā)起請求和其他操作
          }

          注:

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

          上面代碼中paramsPoolsync.Pool類型的變量,而sync.Pool想必大家都很熟悉。sync.Pool是為了復(fù)用已經(jīng)使用過的對象(協(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ù)用對象。此時再回顧上面的doReq函數(shù)就很容易發(fā)現(xiàn)問題的根因。

          defer 2defer 1順序反了!!!

          defer 2defer 1順序反了!!!

          defer 2defer 1順序反了!!!

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

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

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


          推薦閱讀


          福利

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

          瀏覽 77
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产久久久精品1000部视频 | 99久草热 | 91无码国产成人精品 | 日韩欧美A电影免费在线观看 | 色播在线永久免费视频 |