设为首页 加入收藏

TOP

golang 杂思(一)
2019-03-31 18:08:16 】 浏览:177
Tags:golang 杂思

正文

这里给大家总结一些 Go player 开发小技巧. 欢迎批评和交流, 望大家喜欢. 

1. 配置管理

推荐一种简单粗暴的配置管理方式 [配置 映射 内部结构]. 
例如有个配置文件 config.online.yaml
# 常量
pi: 3.14159265358

# 即表示网址属性值
uri: https://www.google.com

# 即表示 server.host 属性的值
server:
    host: http://www.youtube.com

# 数组, 即表示 server 为 [a, b, c]
host:
    - 172.217.161.132
    - 216.58.220.206
    - 8.8.8.8
我们可以在代码直接写映射规则.
var C = struct {
    PI float64 `yaml:"pi"`
    URL `yaml:"uri"`
    Server struct {
        Host `yaml:"host"`
    } `yaml:"server"`
    Host []string `yaml:"host"`
}{}
程序启动时候, 通过 func init() {} 初始化. 使用时只需要使用 config.C.PI, 
是不是很方便. 再补充一个更好的配置文件协议 toml.

toml

如果换用 toml 配置(config.online.toml)的内容更好理解
pi  = 3.14159265358
uri = https://www.google.com

[server]
host = http://www.youtube.com

host = [
    "172.217.161.132",
    "216.58.220.206",
    "8.8.8.8"
]
真的, 看见 toml 的第一眼就喜欢上了. 好舒服 ~ 让人觉得好舒服, 就应该这样的雕琢.

2. fmt.Sprintf

有时候我们看见这样的代码片段
    if len(v) > 0 {
        errMessage = fmt.Sprintf(t, v...)
    } else {
        errMessage = t
    }
其实对于 fmt.Sprintf 是画蛇添足, 可以直接
    errMessage = fmt.Sprintf(t, v...)

3. 乒乓结构

(说的很轻巧, 推荐有所思考) 普通的读写操作代码有
var lastMd5sLock   = sync.RWMutex{}
var lastMd5s map[string]map[string]string

func ClearCache() {
    lastMd5sLock.Lock()
    defer lastMd5sLock.Unlock()
    lastMd5s = make(map[string]map[string]string)
}
这里分享个干掉 RWMutex 的无锁技巧. 运用新旧两份配置, 使用空间换时间技巧.
var nowIndex uint32
var dataConf [2]map[string]map[string]string

// ClearCache conf map clear
func ClearCache() {
    lastConf := make(map[string]map[string]string)
    lastIndex := 1 - atomic.LoadUint32(&nowIndex)
    dataConf[lastIndex] = lastConf
    atomic.StoreUint32(&nowIndex, lastIndex)
}
我们来讲解代码, 原先的 ClearCache 那段代码加了写锁. 写锁能够做到两件事情
1' 临界情况有人在单条读取, 清除会让其等待
2' 临界情况有人在单条写入, 清除会让其等待

假如我们不对 ClearCache 加写锁, 采用原子交换技巧.

由于此刻内存中存在 dataConf[1] new 和 dataConf[0] old 两个配置对象.
临界情况指读取和写入都在进行, 但此刻触发清除操作
1' 临界情况有人在单条读取, 写方将 nowIndex 指向了 1, 但读取的仍然是 dataConf[0] old
2' 临界情况有人在单条写入, 写入的还是 dataConf[0] old

上面行为和加锁后产出结果一样. 因而清除函数, 可以用原子技巧替代锁.

通过这个原理, 我们做配置更新或者同步时候可以采用下面步骤获取最优性能
1' 解析配置, 生成一个新的配置对象 map 填充到 dataConf[lastIndex]
2' 新的配置对象读取索引原子赋值给当前的读取索引 lastIndex = lastIndex

为什么说这么多呢. 因为锁是一个我们需要慎重对待的点.

而对于那些不加锁, 也没有原子操作的乒乓结构, 可以自行利用 go -race 分析. 
其读写一致性无法保证(读写撕裂, 脏读), 而且无法保证编译器不做优化. 有时候那种写法线上居然
不出问题, 但是一旦出了问题就是莫名其妙, 很难追查. 这里就不表那种错误的乒乓写法, 来污染同
行代码.

4. 配置库解析

说起配置库, 我看有的同学通过这样代码做配置文件内容提取和分割.
content, err := ioutil.ReadFile(file)
if err != nil {
    // ...
}

for _, line := range strings.Split(string(content), "\n") {
    // ...
}
上面代码存在两个潜在问题
1' 大文件内存会炸
2' 不同平台换行符不统一 mac \r linux \n windows \r\n

一个稳健漂亮代码模板推荐用下面
    fin, err := os.Open(path)
    if err != nil {
        // Error ...
    }
    defer fin.Close()

    // create a Reader
    var buf bytes.Buffer
    reader := bufio.NewReader(fin)
    for {
        line, isPrefix, err := reader.ReadLine()
        if len(line) > 0 {
            buf.Write(line)
            if !isPrefix {
                // 完整的行并且不带 \r\n, 运行独立的业务代码 ~
                lins := string(buf.Bytes())

                buf.Reset()
            }
        }

        if err != nil {
            break
        }
    }
强烈推荐!! 各位保存这个套路模板.

5. Go MD5

这种高频出现代码片段, 强烈建议统一封装. 保证出口统一. 这里带大家封装两个.
// MD5String md5 hash
func MD5String(str string) string {
    data := md5.Sum([]byte(str))
    return fmt.Sprintf("%x", data)
}
// MD5File 文件 MD5
func MD5File(path string) (string, error) {
    fin, err := os.Open(path)
    if err != nil {
        return "", err
    }
    defer fin.Close()

    m := md5.New()

    // 文件读取解析, 并设置缓冲缓冲大小
    const blockS  
		
编程开发网
首页 上一页 1 2 3 下一页 尾页 1/3/3
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇WaitGroup 下一篇Golang设计模式—简单工厂模式(Si..