谷歌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)