绝对的说法都是错误的。

The Hole (ETCD & BoltDB & Golang & Others)

#Work   #Misc

Preface

就这样开头吧…

以前做 SOA 框架里要搞各种东西:database、cache、message queue、service discovery and registry、service/api downgrading、circuit breaker、distributed tracing、load balance、metrics collection、logging、caller/callee white/black list、concurrency control、rate limiting、config managment、deployment、inspect/profile 等等,当然大部分功能对于任何服务来说都是很必要的,实际上主要工作是封装各种库整合各种配置对接公司的基础设施体系,然后花大把的时间做客服,回答很多 googleable 的问题,拷贝一下对应的文档,帮人检查代码有没写错使用姿势对不对,少部分时间做点有意思的 debug,必要的时候翻翻对应库的源码,极少的情况在分析性能上的瓶颈…

维护过 Python 的框架,也差不多是从零开始用 Golang 撸过一个完整的,大部分都是借鉴 Python 的… 最大感受就是 Golang 要写很多代码搞起来很麻烦,灵活性都是靠生成代码来解决的… 如果设计不良耦合太严重,要处理很多历史遗留问题,在 Python 里面就是某些看不懂的 maigc logic,各种 lazy import 和不知道在哪里初始化的(全局)变量等… 虽然做 Golang 框架的时候我的第一原则就是不要有全局变量特别是跨 package 的,尽量减少函数的副作用,但是搞到后面感觉参数泛滥或者不知道怎么传参要搞 fuckV1、fuckV2 也会非常蛋碎… 另外 Golang 里面的 context 不能断, error stack 得引用外部包然后把各种库都整一遍,写测试用例也很蛋碎特别是 mock ,另外要做很多容错相关的东西也很不爽,比如写个重试逻辑都得吐了,总之弄不好就得把每个方法/函数都撸一遍实在不爽了直接 vendor 改源码…

最大的收获可能就是学习了各种 code style guidlines 和 code review comments, 但是一旦功能变多贡献者变多就会有很多不可控的因素出现,比如一个不大的 PR 大几十个 comments 导致页面加载都变慢… 组织架构变动框架易手之后虽然也在做其它的项目,但是好像我的这些技能也就荒废了… 主要是进度上的问题和作为一个小兵推动力欠佳试图制定各种自以为是的规则没被打是万幸了 ^ _ ^,大家对这个东西好像也没有足够的重视渐渐也快要放弃了… 突然有点讨厌自己 O(∩_∩)O~

恩.. 目前做一个看起来 “高大上” 的分布式对象存储,啃各种 paper 的时候发现整体构架和 linkedin 的 ambry 神似… 当然在架构以及细节上肯定是有一些不同的地方的,我就不代表大佬发言了…

做存储系统大部分也是苦力活,不过有意思的部分相对较多。

ETCD

在这个存储系统里面,etcd 主要是存储集群拓扑(服务注册和发现)、业务无关的元信息、少量配置管理以及部分协调工作。 公司的生态都是 zookeeper 为啥选 etcd 是因为 Golangraft 也相对好理解一些…

此部分内容已移至 坑货 etcd

BoltDB

在这个存储系统里面,BoltDB 主要用来存索引和 replication log 及一点 replication 相关的元数据。

为啥选 BoltDBB-Tree 提供了比较稳定的查询效率,纯种 Golang 的嵌入式 k-v 存储的选择也很少,BoltDB 相对比较成熟,写入效率可以通过一层 in-memoryWAL 来解决,不是大问题,可以容忍一部分数据被丢,再不济就只能 cgo 了…

最大的坑可能就是我们使用了 mmapMAP_POPULATE ,导致启动的时候非常之慢,remmap 的时候磁盘 IO 也非常高,严重影响性能,这个 flag 貌似会尽可能的把所有的数据都 prefetchcache 里面,在数据量较大的时候慎重开启,不过相应的缺页置换肯定会多一点,目前看来性能还不错没有明显的影响。

还有一个坑是内存引用在 transaction 之外使用可能是 invalid 的,最好在 transaction 内解码或者进行一次拷贝,猜测是使用 mmap 相关的问题,得具体分析。

然后,不支持嵌套的 transaction,不利于代码抽象,不算啥。

最后,BoltDB 使用的是 serializablemvcc ,具体没研究过,根据介绍是不存在并发 read-modify-wrtie 导致丢失写的情况,当前没有并发的 read-modify-write 操作,当然由于 WAL 的存在,有额外的锁来保证 serializable write

TCP & Golang & Others

网上介绍过的 TCP 优化配置我们的线上服务器基本都具备,Put 请求非常快,但是 Get 请求很慢, 一行一行打日志(这应该不是我 Python 写多了…)追踪发现 GolangtcpConn.Write 非常慢,5M 的文件得几百毫秒… 通过调整 net.ipv4.tcp_wmem 同机房的 Get 请求时间降低到了几十毫秒,跨机房并没有很大的提升,根据理论计算TCPread buffer 不是瓶颈,调整配置看起来只是提升了 tcpConn.Write 系统调用的时间,并没有提高网络传输效率,结果还搞出了一个 巨坑

虽然对 Actor 模型不是很熟悉,但就个人看来,Erlang 的那套 OTP 真是让人羡慕,也不知道是不是真有那么爽…

初始化机器挂载磁盘的时候会出现重复的挂载点,但是 df 显示只有一个,具体原因未知,相关的是通过读取 mtab 来列出磁盘列表的,这个导致通过 fallocate 预先分配磁盘文件并计算预留空间的时候出现问题…

XXX

离成为伟大的喷子又近了一步…