Xargin

If you don't keep moving, you'll quickly fall behind

open coded defer 是怎么实现的

1.14 defer 正常处理流程 在 Go 1.14 中,增加了一种新的 defer 实现:open coded defer。当函数内 defer 不超过 8 个时,则会使用这种实现。 形如下面这样的代码: defer f1(a) if cond { defer f2(b) } body... 会被翻译为: deferBits

用 subsetting 限制连接池中的连接数量

内网使用服务发现后,服务与其它服务的实例之间使用一条 TCP 长连接进行通信。这种情况下常见的做法是按照 registry 下发的 host:port 列表来直接建连。 简单来说就是下图这样: 每一个服务实例都需要和它依赖的服务的每一个实例都把连接给建上。如果各个服务的规模不大,这样没什么问题。 互联网公司的核心服务规模都比较大,几千/万台机器(或几千/万个实例)的单一服务并不少见,这时候 client 要和所有 server 实例建连,会导致 client 端的 conn pool 里有大量连接,当然,server

packetdrill 简介

本文内容是 2013 年 Google 对 packetdrill 的论文翻译。 网络协议测试很麻烦,线上的网络问题往往都是偶发的,难以捕捉。 packetdrill 是一个跨平台的脚本工具,可以用来测试整个 TCP/UDP/IP 网络栈实现的正确性和性能,从系统调用一直到硬件网络接口,从 IPv4 到 IPv6。 该工具对 Google 工程师研发 Linux TCP 中的 Early Retransmit,Fast Open,Loss

无人值守的自动 dump(二)

之前在这篇 无人值守(一) 简单介绍了我们针对线上抖动问题定位的工具的设计思路,思路很简单,技术含量很低,是个人都可以想得到,但是它确实帮我们查到了很多很难定位的问题。 在本篇里,我们重点讲一讲这个工具在生产环境帮我们发现了哪些问题。 OOM 类问题 RPC decode 未做防御性编程,导致 OOM 应用侧的编解码可能是非官方实现(如 node 之类的无官方 SDK 的项目),在一些私有协议 decode 工程中会读诸如 list len 之类的字段,如果外部编码实现有问题,发生了字节错位,就可能会读出一个很大的值。 非标准

10 个让微服务完全失败的 tips

这是三年前伦敦一个技术大会上的一场非常独特的分享,没想到一场技术大会上能有这么幽默的另类架构师,作者以反讽的形式举出了 10 个微服务环境下对系统搞破坏的 tips。我看了很多遍,其中的案例其实日常研发中大部分也都遇到过了,之前也总结过各式各样的吐槽,比如之前写的《中台的末路》《微服务的灾难》《MQ 正在变成臭水沟》等等。希望日后自己也能做类似风格的演讲。 下面是演讲的内容: 开场白,先是免责声明,让人对号入座就不好了: 然后是听众收益: 老板想要搞事情,让公司系统更现代化,你不想这么干,得找点理由反驳他;或者你想破坏你们现在的系统;学习从实践中总结的血淋淋教训;具体案例做过了匿名化处理,让罪犯可以稍微心安理得一些。 老板从网上学到了新词汇,比如工业4.0(哪个营销号说德国人不讲工业4.

极端情况下收缩 Go 的线程数

在 Go 的 runtime 里有一些创建了就没法回收的东西。 之前在 这篇 里讲过 allgs 没法回收的问题。 除了 allgs 之外,当前 Go 创建的线程也是没法退出的,比如这个来自 xiaorui.cc 的例子,我简单做了个修改,能从网页看到线程: package main /* #include <stdio.h> #include <stdlib.h&

在 Go 语言中 Patch 非导出函数

TLDR; 使用 https://github.com/cch123/supermonkey 可以 patch 任意导出/非导出函数。 Update: 目前在 Linux 直接运行 go test 生成的二进制文件没有符号表,所以如果在 test 中使用需要先用 go test -c 生成带符号表的二进制文件,然后再运行,略麻烦。 目前在 Go 语言里写测试还是比较麻烦的。 除了传统的 test double,

无人值守的自动 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)的业务需求,无穷无尽地加字段,这种时候一般支持域会在其内部构建自己业务强相关的业务数据服务。
京ICP备15065353号-1