日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

曹大带我学 Go(7)—— 如何优雅地指定配置项

發(fā)布時(shí)間:2024/4/11 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 曹大带我学 Go(7)—— 如何优雅地指定配置项 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

你好,我是小X。

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

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

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

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

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

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

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

因?yàn)樽枞?wù)的啟動(dòng)風(fēng)險(xiǎn)太高,所以當(dāng)前的狀態(tài)是把 panic recover 住了,但是之后這個(gè)配置也就一直沒(méi)有更新的機(jī)會(huì)了。而陳年庫(kù)其實(shí)是可以在后臺(tái)靜默更新數(shù)據(jù)的。

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

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

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

func?Init(a?int)

變成:

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

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

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

type?optionFunc?func(*options)

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

type?options?struct?{PanicWhenInitFail?bool }

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

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

初始時(shí),Init 函數(shù)的實(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)用方式都可以編譯成功,不會(huì)影響現(xiàn)有的用戶,完美。

為什么這篇文章和曹大扯上關(guān)系,因?yàn)樵诓艽髮懙?mosn/homels[1] 這個(gè)庫(kù)里也有類似的代碼。當(dāng)然,本文這種形式很常見(jiàn),可以算作標(biāo)配了。不過(guò),有一點(diǎn)點(diǎn)不同之處,曹大定義了一個(gè) interface,不過(guò)看起來(lái)感覺(jué)有點(diǎn)更難懂了。????

//?Option?holmes?option?type. type?Option?interface?{apply(*options)?error }type?optionFunc?func(*options)?errorfunc?(f?optionFunc)?apply(opts?*options)?error?{return?f(opts) }

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

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

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


歡迎關(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

總結(jié)

以上是生活随笔為你收集整理的曹大带我学 Go(7)—— 如何优雅地指定配置项的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。