golang project exp02

谷歌SRE那本書(shū)中所講到一點(diǎn),就是搞業(yè)務(wù)運(yùn)維或說(shuō)SRE,最頭疼的就是工作量隨著服務(wù)規(guī)模的增長(zhǎng)線性增加。其實(shí)個(gè)人覺(jué)得這一點(diǎn)放到開(kāi)發(fā)中也是一樣的,隨著需求的增加,services、views目錄下的文件越來(lái)越多,編寫(xiě)它們的時(shí)候都是copy&paste,維護(hù)它們的時(shí)候也是一樣的任務(wù)量復(fù)制。
就其根源,個(gè)人覺(jué)得就是我們所謂“開(kāi)發(fā)”對(duì)工程規(guī)范的漠視,導(dǎo)致維護(hù)過(guò)程中問(wèn)題的累積。這里也記錄一點(diǎn)過(guò)去積累的好的編碼習(xí)慣(針對(duì)golang而言)

1,創(chuàng)建一個(gè)較為復(fù)雜的對(duì)象時(shí),把New函數(shù)參數(shù)可選
比如說(shuō)

type MyOperation struct {
    keyone string
    valone int
    someConfig bool
    someConfig2 bool
    storage *db.Connection
}

type Opt interface {
    apply(*MyOperation)
}

type optFunc func(o *MyOperation) 

func (f optFunc) apply(o *MyOperation) {
    f(o)
}

func WithStorage(src *db.Connection) Opt {
    return optFunc(func(o *MyOperation) {
        o.storage = src
    }
}

func WithConfig(conf bool) Opt {
    ...
}

func NewOperation(mustKey string, ... Opt) (*MyOperation, error) {
    op := &MyOperation {
        keyone : mustKey,
    }
    for _, optFunc := range Opt {
        optFunc(op)
    }
    return op, nil
}

我剛開(kāi)始的時(shí)候很不理解,把一個(gè)NewOperation寫(xiě)這么有什么好處。但是后來(lái)發(fā)現(xiàn),代碼結(jié)構(gòu)、可讀性是真的非常重要。特別運(yùn)維開(kāi)發(fā)這塊,國(guó)內(nèi)現(xiàn)在就是一個(gè)起步過(guò)程,運(yùn)維開(kāi)發(fā)存在很多任務(wù)都是對(duì)之前代碼的重構(gòu)與功能維護(hù)。
我相信干過(guò)一段時(shí)間你就知道閱讀別人代碼的痛苦吧,可能搞懂邏輯就要花好幾天,但是寫(xiě)代碼不過(guò)一兩天、兩三天而已。

2,多寫(xiě)注釋?zhuān)绻粋€(gè)函數(shù)參數(shù)很多,甚至可以給參數(shù)加注釋?zhuān)貏e是我們一個(gè)操作對(duì)象,可能有n多個(gè)配置,如
func someOperation(true, true, false, false, true,...)
并且這個(gè)函數(shù)可能會(huì)在一個(gè)代碼文件中出現(xiàn)多次,可能每次這些布爾配置參數(shù)還不一樣,所以有必要加一點(diǎn)說(shuō)明
func someOperation(true /* 允許重試/, true, false, false / 不記失敗日志等*/,...)

3,減少作用域

... 上文中已經(jīng)存在err 錯(cuò)誤對(duì)象, 這個(gè)err可能并不希望被覆蓋掉

if f, err := someOpt(); err != nil {
    return err
}
f.xxx()

4,開(kāi)關(guān)策略(這條是摘抄來(lái)的)
生產(chǎn)環(huán)境上的所有操作都是需要嚴(yán)格按照策略進(jìn)行,那么就需要添加一個(gè)開(kāi)關(guān)機(jī)制,我之前項(xiàng)目并未應(yīng)用這種機(jī)制,確實(shí)帶來(lái)了不少麻煩,所以這里可以摘錄一下

package switches


var (
    xxxSwitchManager = SwitchManager{switches: make(map[string]*Switch)}
    
   AsyncProcedure = &Switch{Name: "xxx.msg.procedure.async", On: true}

    // 使能音視頻
    EnableRealTimeVideo = &Switch{Name: "xxx.real.time.video", On: true}

)

func init() {
    xxxSwitchManager.Register(AsyncProcedure, 
    EnableRealTimeVideo)
}


// 具體實(shí)現(xiàn)結(jié)構(gòu)和實(shí)現(xiàn)方法
type Switch struct {
    Name      string
    On        bool
    listeners []ChangeListener
}

func (s *Switch) TurnOn() {
    s.On = true
    s.notifyListeners()
}

func (s *Switch) notifyListeners() {
    if len(s.listeners) > 0 {
        for _, l := range s.listeners {
            l.OnChange(s.Name, s.On)
        }
    }
}

func (s *Switch) TurnOff() {
    s.On = false
    s.notifyListeners()
}

func (s *Switch) IsOn() bool {
    return s.On
}

func (s *Switch) IsOff() bool {
    return !s.On
}

func (s *Switch) AddChangeListener(l ChangeListener) {
    if l == nil {
        return
    }
    s.listeners = append(s.listeners, l)
}

type SwitchManager struct {
    switches map[string]*Switch
}

func (m SwitchManager) Register(switches ...*Switch) {
    for _, s := range switches {
        m.switches[s.Name] = s
    }
}

func (m SwitchManager) Unregister(name string) {
    delete(m.switches, name)
}

func (m SwitchManager) TurnOn(name string) (bool, error) {
    if s, ok := m.switches[name]; ok {
        s.TurnOn()
        return true, nil
    } else {
        return false, errors.New("switch " + name + " is not registered")
    }
}

func (m SwitchManager) TurnOff(name string) (bool, error) {
    if s, ok := m.switches[name]; ok {
        s.TurnOff()
        return true, nil
    } else {
        return false, errors.New("switch " + name + " is not registered")
    }
}

func (m SwitchManager) IsOn(name string) (bool, error) {
    if s, ok := m.switches[name]; ok {
        return s.IsOn(), nil
    } else {
        return false, errors.New("switch " + name + " is not registered")
    }
}

func (m SwitchManager) List() map[string]bool {
    switches := make(map[string]bool)
    for name, switcher := range m.switches {
        switches[name] = switcher.On
    }
    return switches
}

type ChangeListener interface {
    OnChange(name string, isOn bool)
}


// 這里開(kāi)始調(diào)用
if switches.AsyncProcedure.IsOn() {
    // do sth
}else{
    // do other sth
}

并且這里可以看到的,switch對(duì)象中的name是通過(guò)點(diǎn)進(jìn)行分隔的,這也是SRE中所說(shuō)的,所有配置信息都應(yīng)有清晰的命名標(biāo)準(zhǔn),并且所有成員都可以了解到這些配置項(xiàng)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Lua 5.1 參考手冊(cè) by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 14,243評(píng)論 0 38
  • 寫(xiě)在前面的話 代碼中的# > 表示的是輸出結(jié)果 輸入 使用input()函數(shù) 用法 注意input函數(shù)輸出的均是字...
    FlyingLittlePG閱讀 3,208評(píng)論 0 9
  • mean to add the formatted="false" attribute?.[ 46% 47325/...
    ProZoom閱讀 3,165評(píng)論 0 3
  • 官網(wǎng) 中文版本 好的網(wǎng)站 Content-type: text/htmlBASH Section: User ...
    不排版閱讀 4,707評(píng)論 0 5
  • 1、隨機(jī)數(shù) 不需要隨機(jī)數(shù)種子 arc4random()%N + begin:產(chǎn)生begin~begin+N的隨機(jī)數(shù)...
    我是小胡胡123閱讀 4,407評(píng)論 0 2

友情鏈接更多精彩內(nèi)容