无人值守的自动 dump(一)

Go 项目做的比较大(主要说代码多,参与人多)之后,可能会遇到类似下面这样的问题: 程序老是半夜崩,崩了以后就重启了,我也醒不来,现场早就丢了,不知道怎么定位 这压测开压之后,随机出问题,可能两小时,也可能五小时以后才出问题,这我蹲点蹲出痔疮都不一定能等到崩溃的那个时间点啊 有些级联失败,最后留下现场并不能帮助我们准确地判断问题的根因,我们需要出问题时第一时间的现场 Go 内置的 pprof 虽然是问题定位的神器,但是没有办法让你恰好在出问题的那个时间点,把相应的现场保存下来进行分析。特别是一些随机出现的内存泄露、CPU 抖动,等你发现有泄露的时候,可能程序已经 OOM 被 kill

Go context

填坑。。 https://github.com/cch123/golang-notes/blob/master/context.md ascii 图太多,blog 上没法看。就这样

一些连接池相关的总结

http 标准库 服务端 请求处理 package main import ( "io" "log" "net/http" ) func sayhello(wr http.ResponseWriter, r *http.Request) { wr.Header()["Content-Type"] = []string{"application/

reviewdog

几年前写过一篇 如何使你的代码达到 awesome-go 的入选标准。几年过去了,标准和工具都有所变化。 linter 已经换了两代,刚开始是散乱的 golint,go vet,staticcheck,后来出现了 gometalinter,把散乱的工具集集成到一起。 gometalinter 时代,我把这个工具集成到了当时组内所有项目 Makefile 里,合并代码前要求提交者自行执行 make lint 来检查不符合规范的代码并修正,可惜 Makefile 里的命令没有强制约束,某些没有追求的程序员根本不把君子之约当回事,所以难以执行下去。没办法,只要一个人不在乎,就会影响到后来加入的工程师,

fasthttp 快在哪里

坊间传言 fasthttp 在某些场景下比 nginx 还要快,说明 fasthttp 中应该是做足了优化。我们来做一些相关的验证工作。 先是简单的 hello server 压测。下面的结果是在 mac 上得到的,linux 下可能会有差异。 fasthttp: ~ ❯❯❯ wrk -c36 -t12 -d 5s http://127.0.0.1:8080 Running 5s test

架构的腐化是必然的

架构的腐化是必然的,不以人的意志为转移。 我们先从一个故事开始,从前有一个公司,这个公司有一个部门,这个部门里有两个组。 两个组做的项目比较类似,都是策略类项目。 其中一个组做需求基本靠堆人,业务和 PM 的所有需求,能找到人,并且让这个人在各种场景,各种模块,各种分支里加 if else 就可以搞定,代码膨胀飞快。很快没人能说得清项目内的细节,但是公司业务涉及的策略又很多,需求做不过来,所以疯狂堆人,小组规模迅速膨胀到几十个人。大家都很忙碌,充实,每天都在加班,就是代码稍微有点看不懂,但这不重要。重要的是大家都很充实且周报饱满。小组 leader

为什么提升 Go 项目的测试覆盖率有点难

注意,这里讨论的内容可能有争议。如果不同意,欢迎讨论。 awesome-go 要求项目测试覆盖率达到 80% 以上才符合入选标准。有一些公司也会要求项目有相对合理的测试覆盖率(如 70% 以上才符合代码准入条件等等)。 但有时,我们的逻辑代码却挺难做到这么高的覆盖率,主要还是因为目前 Go 的错误处理逻辑: func Register(req RegisterReq) error{ if len(req.Username) == 0 { return errors.New("length of

go mod 的智障版本选择

之前 go mod 用的比较少,而且一直听社区有各种抱怨,所以也兴趣寥寥。新公司的项目直接使用了 go mod,本来觉得无非是个简单的工具,不需要学习,结果在一个简单的依赖上却浪费了很多时间。 先来看看我碰到的例子: package main import "fmt" import registry "github.com/apache/dubbo-go/registry" import zk "github.com/

工程师应该怎么学习

只要一日自诩工程师,就没有办法放弃学习。本文不算是技术文,只是介绍一些个人的学习方法和经验。如果很多点你已经做到并且做好,一笑了之便可。 阅读书籍 对于工程师来说,从书籍得来的知识是必不可少的。现在很多年轻的程序员会从网络博客来学习技术,但博客内容大多缺乏体系(主要说总结性质的博客内容),不系统。很多博主为了掩饰自己的未知,遇到不知道的关键点就一笔带过,进而导致缺失。即使原作者非常努力,内容上没有缺失,你能从中获取的也只是别人总结好的知识,没有自己的主动思考,这中间便缺少过程式的沉淀,一味地满足于背诵别人总结好的知识,最后也只不过沦为他人的复读机而已。 对于工程师来说,书籍依然是最重要的知识获取媒介。即使只是通过目录概览,也能获取某个领域的大致蓝图。 目前大部分优秀的技术书籍依然以英文为主,能够读懂英文技术书籍是工程师的硬实力。英语阅读能力怎么训练呢?如果不是为了应试,可以尝试逼迫自己去翻译一些英文文档/文章来进行专门训练。

slice 类型内存泄露的逻辑

Go 101 总结了几个可能导致内存泄露的场景: https://gfw.go101.org/article/memory-leaking.html goroutine 阻塞在 channel,time.Ticker 不使用但未 stop,以及 for 循环里用 defer 导致泄露,这三个场景其实已经比较常见了,这里就不说了。 我们来看看子切片截取为什么会导致内存泄露。 因为 Go 是一门带 GC 的语言,虽然官方宣传 GC stw

生于非阻塞,死于日志

metrics 上报在高并发时会带来性能问题,为了解决问题,有时反而又会带来别的问题。 举个例子,一般的 metrics 上报代码可能是下面这样: // when request in metrics.Req(caller, count, latency) 内部实现一般也就是个 UDP 调用,可能碰到的是 fd 锁的问题,在 这篇 里已经写过了。 为了优化这个锁带来的阻塞问题,有些系统会把 metrics 上报改为非阻塞逻辑: func Req(caller string,

map 并发崩溃一例

某系统中有类似下面这样的代码: package main import ( "sync" "time" ) type resp struct { k string v string } func main() { res := fetchData() log.Print(res) } func rpcwork() resp { // do some rpc work return resp{

一个空 struct 的“坑”

https://www.zhihu.com/question/364687707 在知乎上已经说了原因,不过只是讲官方的 spec,我们再来看看实现上具体为什么会是这样的。 package main import "fmt" func main() { a := new(struct{}) b := new(struct{}) println(a, b, a == b) c := new(struct{}) d

MQ 正在变成臭水沟

MQ 对于业务系统建模非常重要,是解决分离关注点、依赖反转、CQRS、最终一致等业务问题的重要法宝。 然而企业对于 MQ 中的数据管理却并不重视。从互联网企业发展的历程来看这个问题,最初 MQ 不是很可靠,大家不会把让特别重要的业务依赖 MQ,所以接入到 MQ 的业务事件并不多。总共也就两三个 topic,开发相应的系统对这些内容进行管理看起来没什么必要,甚至可能连详尽的业务信息都要从生产者的代码注释中去寻找。公司规模不大,这些都是可以接受的。 经历过 1Mb 小水管的朋友大概还记得当初火爆的 Flashget 的 Slogan: 下载的最大问题是什么——速度,其次是什么—

ACL 和俄罗斯套娃

逻辑复杂的系统中,我们经常能看到“规则引擎”和“决策树”之类的东西。 在数据准备好的前提下,规则引擎和决策树都可以理解成在大宽表上进行决策的过程,两者进行逻辑运算需要较为统一的数据格式,即需要对不同来源的数据内容进行统一抽象。 在之前的文章中,我写过 一劳永逸接入所有下游数据系统,讲的便是复杂系统中对准备数据过程的一种抽象。这种抽象在方法论大湿们发明的概念中早有总结:Anti Corruption Layer。你可以把它叫做 adapter,也可以把它称为 facade,或者把它贯名 anti corruption layer,你的听众听不懂哪一个名词,你就尽量用那种叫法,可以显得自己博学多才出世高人。 数据准备过程是典型的对外部系统进行适配的过程,在一个中大型规模的公司,没有进行过数据治理的前提下,必然会产生大量的像下图架构的技术产品:

依赖反转相关

依赖反转即 OOP 中的 SOLID 原则中的 D(Dependency Inversion Principle),最早么,是 Uncle Bob 提出的: The dependency inversion principle was postulated by Robert C. Martin and described in several publications including the paper Object

从 nginx 切换到 caddy

几个月前网友建议我切换到 https,当时比较懒,假期给各种烂尾楼扫尾想起来这件事情,看了看 caddy,确实非常简单。 caddy 的宣传口号是 “Caddy is the HTTP/2 web server with automatic HTTPS.”,会使用 Let's Encrypt 的服务自动帮你把 https 相关的证书流程自动搞定。颇有各种“一键干坏事脚本”风范。 个人用户用起来确实方便不少。 ghost blog 切换 ghost

为什么 Go 模块在下游服务抖动恢复后,CPU 占用无法恢复

某团圆节日公司服务到达历史峰值 10w+ QPS,而之前没有预料到营销系统又在峰值期间搞事情,雪上加霜,流量增长到 11w+ QPS,本组服务差点被打挂(汗 所幸命大虽然 CPU idle 一度跌至 30 以下,最终还是幸存下来,没有背上过节大锅。与我们的服务代码写的好不无关系(拍飞 事后回顾现场,发现服务恢复之后整体的 CPU idle 和正常情况下比多消耗了几个百分点,感觉十分惊诧。恰好又祸不单行,工作日午后碰到下游系统抖动,虽然短时间恢复,我们的系统相比恢复前还是多消耗了两个百分点。如下图: 确实不太符合直觉,cpu

查看 Go 的代码优化过程

之前有人在某群里询问 Go 的编译器是怎么识别下面的代码始终为 false,并进行优化的: package main func main() { var a = 1 if a != 1 { println("oh no") } } 先说是不是,再说为什么。先看看他的结论对不对: TEXT main.main(SB) /Users/xargin/test/com.go com.

Go 1.13 defer 的变化

1.13 正式发布了,Release notes 上说 defer 现在大多数情况下可以提升 30% 的性能。这 30% 的性能怎么来的呢? 我们知道,以前的 defer func 会被翻译成 deferproc 和 deferreturn 两个过程,这里 现在 deferproc 这一步增加了 deferprocStack 这个新过程,由编译器来选择使用 deferproc 还是 deferprocStack,当然了,

一劳永逸接入所有下游数据系统

虽然公司内的存储和数据系统名目繁多,但一般难以满足领域(就是 DDD 里说的那个 domain)内或子域(subdomain)内的所有需求,在业务部门内部还有着花样繁多的各种数据系统。这样的现状会导致业务接入数据服务的开发成本非常高。 这么说可能比较抽象,举个例子,如果你们公司有 LBS 相关的业务,一般一定会有一个坐标相关的服务,可以使用用户 id 获取瞬时坐标信息。也可能用一个订单或者配送 id 获得某件事情的发生轨迹。坐标轨迹相关的服务和地图类的数据强相关,所以这种服务一般是由单独的部门提供。 再比如说,公司内有公共的订单服务,但主服务不可能为各种相对独立的支撑域(supporting domain)的业务需求,无穷无尽地加字段,这种时候一般支持域会在其内部构建自己业务强相关的业务数据服务。

talent-plan tidb 部分个人题解-week 2

Week 2 的问题是 map reduce 优化,说实话,这周的示例代码写的不怎么样,不知道为什么都是同一个人的代码,同一个参数的名字还换来换去,读起来会浪费一些时间。一些缩写也让人比较困惑,比如 fs,bs,别人要稍微思考一下才能看懂这是什么东西,用 fileList 或者 bufferList 显然更好啊,可能样例代码的人是写 acm 出身,比较喜欢赶时间。虽然 Go 的 runtime 代码里也经常这么干,汗。 吐槽就到这里。题目是想让你先把

talent-plan tidb 部分个人题解-week 1

第一周是给定了函数签名实现多路归并排序。这里。 为啥要考这个问题呢?个人认为多路归并在分布式数据库、分布式搜索引擎领域是挺常见的算法。用我相对熟悉的 es 来举例,在搜索数据时,我们会指定 bool 表达式来对数据进行筛选,同时需要指定每个查询 sort by 字段以及 size,但分布式环境下,我们没有办法确定这 size 个元素要在每个 shard 里取几个元素。所以最基本的思路: 每个 shard 里都取出 size 个元素 拉到中心节点来进行多路归并,输出前 size 个元素

一些问题的答案

知乎上有一个问题,有哪些优秀的 Go 面试题,其中有一个高赞答案很[敏感词],从大多数问题上可以看出作者想要问别人什么,对其它应聘者的要求是对常见的性能指标要敏感。 不过我觉得,即使是做优化,很多时候也应该是去做整体优化,而不是在所有地方全部抠细节,那我们程序全用汇编+优化指令写得了。 当然这么说也很空洞,我简单整理一下这些问题的答案,可能其中有些疏漏,如果读到这篇文章的你在某些问题的答案上有不同的见解,欢迎在评论里讨论和补充~ 1.9/1.10中,time.Now()返回的是什么时间?这样做的决定因素是什么? 是想考 monotonic time 和 wall time

微服务的灾难-康威定律和 KPI 冲突

架构师们常讲的设计定律之中最为重要的是康威定律,康威定律的定义: Conway's law is an adage named after computer programmer Melvin Conway, who introduced the idea in 1967. It states that. organizations which design systems ... are constrained to produce designs which are

微服务的灾难-最终一致

现在的架构师总喜欢把最终一致挂在嘴上,好像最终一致是解决分布式场景下数据一致问题的金科玉律。事实上又怎么样呢? 事实上的这些人嘴里的最终一致,往往都是最终不一致。在多个系统之间进行数据传递时,无非通过 RPC 或者异步消息。RPC 能保证一致性么?当 B 系统需要 A 系统提供数据时,想要达到一致的效果,那么在 A call B 时发生失败,那么必须让 A 中的逻辑终止。这样才能够使 B 中的状态或数据与 A 中的完全一致。这样实际上需要让 A 和

微服务的灾难-依赖地狱

微服务模式下,我们的系统中往往需要集成进各种各样的 SDK,这些 SDK 部分来自于非功能性的业务需求,例如 bool 表达式解析,http router,日期时间解析;一部分来自于对公司内基础设施的绑定,如 MQ Client,配置下发 Client,其它服务的调用 SDK 等等。 一般的观点会认为公司内的 SDK 是较为可靠的,而开源库的稳定性不可控,所以人们在升级公司内部库时往往较为激进,开源库版本升级较为保守。具体到 Go 语言,公司内的库,我们可能会直接指定依赖的版本为 master(

微服务的灾难-拆分

在之前写事故驱动开发的时候,提到过,在企业中的项目进行开发时,只要是自己方便,一个人可以用拆分和收敛同时作为自己的标准。所以大家都是双标狗。 目前业界的微服务方法论一般也没有固定的套路,比如在 《Building Microservice》 一书中,作者也讲到了服务之间协作的时候,可以选择编排(orchestration)和协同(choreography)这两种方式来对服务进行架构。所以在拆分阶段,就没有什么硬性的标准了,每个公司可能风格都有差别,并且都可以阐述出自己的条条以支持自己的架构是“正确”的。 显然,这件事情没有绝对正确的解法。无论哪种拆分方式,都会遇到业务边界的问题。在大企业中,顶着“架构师”头衔的这些架构师们根本就不会管任何实现上的细节。相对较大的业务需求,一般也是一线的

微服务的灾难-技术栈

微服务的布道师们特别喜欢鼓吹一个观点:拆分微服务之后,我们可以随意地对小模块进行重构,选择最合适的技术栈,并且如果写失败了随时对这个模块拿其它语言进行重写。这一点被大多数布道师当作微服务的重点优势。 但是布道师们有意地把这样做所带来的问题忽略了,或者更恶意的是,他们明知道有问题,但是不说?啊哈哈。 一个公司业务上有多种语言的话,理论上可以吸引到各种“语言的人才”。这确实不假,并且可以提供给各种语言大佬一个互相掐架的优秀竞技场,只要干掉对手(其它语言的大佬)了,我就可以扩张团队,让团队把所有其它语言的模块用我们擅长的语言重写一遍,善哉善哉。小伙子们一年的事情就都安排上了。 但是显然这种说辞是有问题的。在现行的微服务架构下,除了业务本身的研发人力投入之外,在业务之外的支持系统的研发工作也有很大的工作量,比如典型的,服务发现,熔断,优雅重启,存储系统 client,
京ICP备15065353号-1