<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>

          曹大帶我學(xué) Go(7)—— 如何優(yōu)雅地指定配置項

          共 2719字,需瀏覽 6分鐘

           ·

          2021-07-17 07:55

          你好,我是小X。

          曹大最近開 Go 課程了,小X 正在和曹大學(xué) Go。

          這個系列會講一些從課程中學(xué)到的讓人醍醐灌頂?shù)臇|西,撥云見日,帶你重新認(rèn)識 Go。

          最近一個年久失修的庫導(dǎo)致了線上事故,不得不去做一些改進。

          這個陳年庫的作用是調(diào)用第三方的 RPC 拿一些比較重要的配置,業(yè)務(wù)代碼中有段邏輯會根據(jù)讀到的配置調(diào)用不同端的下游。如果沒拿到配置,就會默認(rèn)地調(diào)一個兜底下游。恰好這個兜底下游最近新上了一些邏輯,不兼容這種跨端調(diào)用,直接把它打掛了。

          先拋開這個下游不健壯不談,假設(shè)它是健壯的。

          陳年庫的問題在于:進程啟動時它會去調(diào)一個下游拿數(shù)據(jù),之后會定時更新。但如果啟動時調(diào)用失敗就直接 panic 了,所以之后也不會定時更新。理論上這個也沒什么問題,服務(wù)在初始化時如果檢測到了庫的 panic,進程退出,重啟就好了。

          但是阻塞啟動是比較危險的,所以有些服務(wù)就會吞掉 panic。于是,整個進程生命周期內(nèi)這個配置就一直是缺失的狀態(tài)。

          因為阻塞服務(wù)的啟動風(fēng)險太高,所以當(dāng)前的狀態(tài)是把 panic recover 住了,但是之后這個配置也就一直沒有更新的機會了。而陳年庫其實是可以在后臺靜默更新數(shù)據(jù)的。

          因此我要對陳年庫要做一點改進:如果初始化時拉取配置失敗,不 panic,后臺靜默修復(fù)。這個設(shè)置要在調(diào)用 Init 函數(shù)時設(shè)置,因為庫就暴露了 Init 和 Get 函數(shù)。

          但因為這個庫有很多使用方,所以不可能更改函數(shù)簽名和現(xiàn)在的行為,否則影響其他人使用。萬一有業(yè)務(wù)都對這個是強依賴,就是要感知 panic,初始化失敗就進程退出,你改了不就 gg 了。

          我們知道,Go 語言里面有可變參數(shù),調(diào)用它的時候可以不傳實參,或者傳多個實參。向陳年庫函數(shù)的 Init 函數(shù)簽名后加一個可變參數(shù):

          func Init(a int)

          變成:

          func Init(a int, opts ...optionFunc)

          這樣就不影響已有的用戶了,并且我可以增加更多的設(shè)置項。這里的關(guān)鍵是 optionFunc 的實現(xiàn)原理是什么?

          它其實是一個函數(shù)類型,它接受 options 結(jié)構(gòu)體指針:

          type optionFunc func(*options)

          再定義一個 options 結(jié)構(gòu)體用于放 bool 型變量 PanicWhenInitFail,表示 Init 失敗后是否 panic:

          type options struct {
           PanicWhenInitFail bool
          }

          再來定義一個導(dǎo)出的函數(shù),用戶傳入 bool 型變量就可以設(shè)置 options,而不用定義 options 對象。這種方法美妙的地方就在這里,要多次回味才能感受到:

          func WithPanicWhenInitFail() optionFunc {
           return func(o *options) {
            o.PanicWhenInitFail = true
           }
          }

          初始時,Init 函數(shù)的實現(xiàn)如下:

          func Init(a int) {
           fmt.Println(a)
          }

          修改后:

          func Init(a int, opts ...optionFunc) {
           fmt.Println(a)

           var gOpt = &options{PanicWhenInitFail: false}

           for _, opt := range opts {
            opt(gOpt)
           }

           fmt.Println(gOpt)

          }

          這樣,main 函數(shù)就可以非常優(yōu)雅地設(shè)置 PanicWhenInitFail 了:

          func main() {
           Init(8)
           Init(8, WithPanicWhenInitFail())
          }

          不管加不加后面的配置,兩種調(diào)用方式都可以編譯成功,不會影響現(xiàn)有的用戶,完美。

          為什么這篇文章和曹大扯上關(guān)系,因為在曹大寫的 mosn/homels[1] 這個庫里也有類似的代碼。當(dāng)然,本文這種形式很常見,可以算作標(biāo)配了。不過,有一點點不同之處,曹大定義了一個 interface,不過看起來感覺有點更難懂了。??

          // Option holmes option type.
          type Option interface {
           apply(*options) error
          }

          type optionFunc func(*options) error

          func (f optionFunc) apply(opts *options) error {
           return f(opts)
          }

          去 Google 上一查,其實這種形式,叫 Functional Options Pattern,早在 2014 年 Rob Pike 就寫過一篇博文[2]來說這個事,沒幾行代碼,但是真的很優(yōu)雅。

          總結(jié)一下,當(dāng)我們要修改已有的函數(shù)時,為了不破壞原有的簽名和行為,可以使用 Functional Options Pattern 的形式增加可變參數(shù),即可以增加設(shè)置項,又能兼容已有的代碼。

          好了,這就是今天全部的內(nèi)容了~ 我是小X,我們下期再見~


          歡迎關(guān)注曹大的 TechPaper 以及碼農(nóng)桃花源~


          參考資料

          [1]

          mosn/homels: https://github.com/mosn/holmes

          [2]

          博文: https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html

          瀏覽 21
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  国产大尺度在线观看 | 国产乱子伦精品久久 | 亚洲黄片在线看 | 一道本无码在线视频 | 西西444www无码精品 |