非协作式抢占详解

抢占抢占 # 从 Go 1.14 开始,通过使用信号,Go 语言实现了调度和 GC 过程中的真“抢占“。抢占流程由抢占的发起方向被抢占线程发送 SIGURG 信号。当被抢占线程收到信号后,进入 SIGURG 的处理流程,将 asyncPreempt 的调用强制插入到用户当前执行的代码位置。本节会对该过程进行详尽分析。抢占发起的时机 # 抢占会在下列时机发生: STW 期间 在 P 上执行 safe point 函数期间

关于静态分析的科普

看了看日历,现在已经是 2021 年了,偶尔还是能看到有人在发诸如 《http body 未关闭导致线上事故》,或者 《sql.Rows 未关闭半夜惊魂》类的文章,令人有一种梦回 2015 的感觉。 在这个 Go 的静态分析工具已经强到烂大街的时代,写这些文章除了暴露这些人所在的公司基础设施比较差,代码质量低以外,并不能体现出什么其它的意思了。毕竟哪怕是不懂怎么读源码,这样的问题你 Google 搜一下也知道是怎么回事了。 特别是有些人还挂着大公司的 title,让人更加不能理解。下面是简单的静态分析工具的科普,希望给那些还在水深火热的 Gopher 们送点解药。

换掉 gitalk

为了能和读者互动,之前 blog 一直是有 gitalk 的。 不过 gitalk 似乎是用了 github deprecated 的 API,现在频繁地往我邮箱发邮件,还是很烦的: 之前想换 gitalk 的时候发现并没有什么好的竞品,不过毕竟已经 2021 了,让我发现了这个东西:https://utteranc.es/,还有这个东西:https://giscus.app/。 utterances 和 gitlak 差不多,

为什么大公司里的垃圾系统也能保持稳定

从毕业至今,已经待过四家公司,算上实习期间三月游的公司差不多已经快十家了,时常会碰到一些有意思的言论。 在某司的时候就经常听到人说,“我们在 T 姓巨头的时候”,“我们在 A 姓巨头的时候”,“我们在 B 姓巨头的时候”,以任意一家巨头公司作为观点的前缀,显得观点就特别有分量。仔细想想,实习期间我也是在这些所谓的巨头公司待过的,我怎么就没有想到可以在自己编的故事前面加上这是 X 家的事情,所以还是他们比较机智。 大公司里的平均水平高,牛人多,并不代表进去的就一定是牛人,还有不少认不清是自己强还是公司机制强的混子。 即使是在大公司,也会有很多垃圾系统,他们内部没有限流,没有熔断,没有降级,也没有过载保护。但是他们却不会崩溃,

Go 语言中的一些非常规优化

这次去 Gopher China 和不少老朋友见了个面,还有不少在微信上认识已久,一直没见过面的网友。同时也和各个公司的一线开发们聊了聊,互相交流了彼此使用 Go 时的一些心得和痛点。 综合近期了解的一些相关分享,我把到目前为止见到的,不那么常见的,各方对 Go 的优化和 hack 集中在这篇文章里。因为考虑到一些公司情况比较特殊,所以本文中列出的点就不标记是哪个公司做的了。未来他们觉得时机成熟应该也会自己出来做一些分享。 网络方面 当前 Go 的网络抽象在效率上有些低,每一个连接至少要有一个 goroutine 来维护,有些协议实现可能有两个。因此 goroutine 总数 = 连接数

用 litmus 验证 x86 内存序

前置知识在这里。 在 stackoverflow 上有这么一个问题,问题的答案中有这么几段: At the same time, x86 defines quite a strict memory model, which bans most possible reorderings, roughly summarized as follows: Stores have a single global order of visibility,

用 MQ 解耦其实是骗你的

有一个观点已经被说烂了:使用 MQ 可以帮助业务系统解耦。 想法很简单,在业务状态流转时,如果没有 MQ,那么其它系统想要知道状态变了,那就需要核心流程系统去主动做通知。 比如电商系统里订单从创建到处理中状态切换了,客服系统需要知道,风控系统需要知道,用户系统也需要知道。 这里的通知通过 RPC 来进行,下游系统需要的数据可以在这次 RPC 里携带上,也可以在请求的时候让下游系统自己去查。 下游系统增加的时候,核心业务的代码也需要修改,比如新做了一个积分系统,现在订单状态流转积分系统也想知道。 核心系统需要不停地增加调用关系来迎合下游新增的业务方需求。这些边边角角的计算逻辑和订单系统本身没啥关系,但是因为下游需要拿到这些数据,我们就需要自己用 RPC 去调用下游的接口。这确实不太合理。 当下游系统发生事故时,

nocode 和 lowcode

今年不少业务开发像突然被开了光一样开始讲 nocode 和 lowcode,可以看出现今的互联网可能真的是编不出什么好故事了。 在之前的文章里,我们已经讲过自动化、平台化和中台化了,不管业务模式怎么变,企业内总还是有一些局部系统最终能够把开发模式沉淀下来,变成拖拖拽拽就可以进行变更的“网页制作大师”系统。 像阿里这样的公司,中台落地并迭代多年以后,核心业务的流程变化其实并不会有太多的编码任务,从内部资料来看,套用以往的业务模式,改改配置或者上上活动,基本不需要程序员去做开发了。 不管我们在哪个公司,只要我们按照《在业务系统中寻找技术含量》这篇文章的思路去做系统,最终一定能够将大部分繁琐的重复劳动做到自动化。二八定律,就是可以将那 80% 的重复劳动用界面化、系统化、流程化的手段完全消灭掉。剩下的 20%

Fail at Scale

Fail at Scale 是 Facebook 2015 年在 acm queue 上发表的一篇文章。主要写了常见的线上故障和应对方法,内容还是比较实在的。 "What Would You Do If You Weren't Afraid?" 和 "Fortune Favors the Bold." 是 FB 公司信条,挂墙上那种。

The Tail at Scale

The Tail at Scale,是 Google 2013 年发表的一篇论文,大规模在线服务的长尾延迟问题。 要知道怎么解决长尾问题,先要理解长尾延迟是个什么问题,在开发在线服务的时候,我们都知道要关注服务的 p99/p999 延迟,要让大部分用户都能够在预期的时间范围内获得响应。 下面是一个不同响应时间的请求数分布图: 大部分系统也都遵循这种分布规律,现在互联网的系统规模比较大,一个服务依赖几十上百个服务的情况都是有可能的。单一模块的长尾延迟会在有大量依赖的情况下,在服务粒度被放大,《The Tail at Scale》论文里给出了这样的例子。 考虑一个系统,大部分服务调用在 10ms 内响应,但

那些画图工具们

偶尔讲讲工具,放松一下。 现在写技术文章不但要写技术细节,图还得画的好看。对于表达思路和架构来说,图确实挺直观的,这篇文章介绍一下常见的绘图工具。大家可以看自己的喜好自行选择。 在早期写 golang-notes 的时候,想要向那些写 RFC 文档和早期的 unix 大神们致敬,所以比较喜欢 ascii 图,这种图的好处是你可以直接将图表内嵌在文档内部,不需要有附件。有利于单文件传播。 用来画 ascii 的图工具有不少。 textik textik 是一个在线项目:https://textik.com,可以直接在线绘制 ascii

《写作的逻辑》简单读书笔记

写作是单向沟通,读者读不懂,说明是作者没有写好。 作者自认为的前置知识,可能在读者角度是不懂的。 文章易懂,需要对读者的心智模型有一点了解。 用总分或总分总的结构去写文章和做演讲;因为大家时间有限,可以根据你的总论判断是否要读下去,工作汇报演讲结论先行。 观点不宜太多,如果太多,尽量划分到 3 类里。 用段落组织文章,而不是句子(一些娱乐性的文章,可以用句子来组织。 每段只写一个主题。 每段开头是一句概要描述,后面段落补充细节,每段大约 4 - 8 个句子,不要太长。这样读者可以跳跃阅读你的文章。 概要还要负责段落之间的承接功能,比如政府财政紧张,第二段是要提高效率,

大型系统在线问题诊断与定位

本文是武汉 gopher meetup 的分享内容整理而成,分享内容在“无人值守”的两篇和其它社区分享中亦有提及。(也就是说你看过那两篇,这个可以不用看了) 先来看看苦逼的开发人员 老板说: 队友说: 外组同事说: 底层团队说: 你: 业界的思路? 混口饭吃也是不容易,既然有问题了,我们还是要解决的。要先看看有没有现成的思路可以借鉴? Google 在这篇论文里提到过其内部的线上 profile 流程: 架构图已经比较简单了,线上应用暴露 profile 接口,collector 定时将 profile 信息采集到二进制存储,由统一的在线平台展示。

随便说说 living documentation

几年前在 oreilly 看到一本叫 《living documentation》的书,可惜当时烂尾了。 最近图灵出版了该书的中文翻译版,才想起来有这么回事。。。正好最近有时间,把这个坑了几年的东西给填上了。 简单来说,这本书基本可以叫 living java doc(误。我们可以先看看日常开发中会涉及到哪些文档: 需求文档 接口文档 业务词汇表文档 模块业务流程文档 系统间交互文档 系统架构文档 系统依赖文档 发布文档 示例文档(tutorial) 项目进度文档 案例文档 汇报文档 要写的类型文档还是不少的,无论哪种文档,

一种持锁被调度的情况

在给某个项目做长时间极限压测的时候,经常会出现压几小时不出问题,突然就崩了的情况。 查看监控发现崩的时候 goroutine 突然涨起来了,那么可以用我之前开发的问题诊断工具了,配置下面的策略:若 goroutine 突然开始暴涨,则将 goroutine 文本 dump 下来。对这个诊断工具不了解的,可以看看我之前写的 无人值守的自动 dump-2 和 无人值守的自动 dump。 代码集成好之后再压,发现崩溃时,总是有很多卡在锁上的 goroutine: 10760 @ 0x42f81f 0x4401d9 0x4401af 0x43ff4d 0x474df9

Go 应用的性能优化

为什么要做优化 这是一个速度决定一切的年代,只要我们的生活还在继续数字化,线下的流程与系统就在持续向线上转移,在这个转移过程中,我们会碰到持续的性能问题。 互联网公司本质是将用户共通的行为流程进行了集中化管理,通过中心化的信息交换达到效率提升的目的,同时用规模效应降低了数据交换的成本。 用人话来讲,公司希望的是用尽量少的机器成本来赚取尽量多的利润。利润的提升与业务逻辑本身相关,与技术关系不大。而降低成本则是与业务无关,纯粹的技术话题。这里面最重要的主题就是“性能优化”。 如果业务的后端服务规模足够大,那么一个程序员通过优化帮公司节省的成本,或许就可以负担他十年的工资了。 优化的前置知识 从资源视角出发来对一台服务器进行审视的话,CPU、内存、磁盘与网络是后端服务最需要关注的四种资源类型。 对于计算密集型的程序来说,优化的主要精力会放在 CPU 上,要知道 CPU 基本的流水线概念,知道怎么样在使用少的

在业务系统中寻找技术含量

从进入互联网公司开始工作起,每个人都在问自己,CRUD 到底有什么技术含量? 别觉得 CRUD 只是业务工程师的问题,无论你在写什么程序,基本上都是在和数据打交道,除了读就是写。只不过读写的时候还会附带一些领域相关的行为。比如: 编译器:读文本,做 parse 网络程序:读连接,decode,encode,写连接 数据库:从磁盘上读,从内存里读,向磁盘里写,向内存里写 工程上,谁也没有权力鄙视谁,你觉得领域的技术含量高,只不过是领域内的工作做的人少罢了。不管门槛多么高的工程领域,只要工程师红利们蜂拥而至,总有一款内卷适合你。

一些 eink 设备

工程师是一个需要大量阅读的岗位,读书、读文档、读论文、读技术新闻,现在大部分电子设备都是 LCD 或 OLED 屏幕,对眼睛的伤害非常大。 因为行业的特殊原因,有时我们会一天花 13-15 小时使用各种电子设备,工作时间长的人很容易眼睛疲劳,严重的可能还会患上干眼症。 能够缓解这个问题的是市面上用户比较少的 eink 设备,比如 kindle: 目前 kindle 最新的设备是 oasis 3,7 寸屏幕 300 PPI,如果只做纯文字阅读,kindle

go mod 之痛

从 rsc 力排众议设计并将 go mod 集成在 Go 语言中,已经两年过去了,时至今日,广大 Gopher 还是经常被 go mod 相关的问题折磨。 本文会列举一些我和我的同事使用 go mod 时碰到的问题,有些问题是 go mod 本身的问题,有些可能是第三方 goproxy 实现的问题。 如果你做过比较大型的 go 项目开发,相信总会有那么几个让你会心一笑。 Go 命令的副作用

why do we need generics?

Go 社区之父早期提到过 less is more 的哲学,可惜社区里有不少人被带偏了。 每次 Go 增加语法上的新特性、新功能,就会有原教旨主义者跳出来扔出一句 less is more(或者也可能是大道至简),扬长而去,留下风中凌乱的你。 即使到了官方已经确定要增加泛型功能的 2020 年,依然有人煞有介事地写文章说为什么 go doesn't need generics,作为理智的 Gopher,最好不要对别人的结论尽信,至少要看看其它语言社区怎么看待这件事情。 Java 社区是怎么理解泛型的必要性的呢? 简而言之,

开源说线上分享

当前互联网公司的后端架构都是微服务化的,服务彼此使用 RPC 通信,与业务无关的功能部分会从业务代码中抽离为框架。 框架提供了基础的 RPC 功能,同时需要对稳定性负责,一个微服务框架包含但不限于以下功能: 路由 限流 熔断 负载均衡 服务注册与发现 tracing 链路加密 这些功能看起来也是通用且稳定,所以我们对一个框架的印象往往是成型了之后便很少再升级了。但是云原生时代将我们的假设击得粉碎。底层基础设施也开始迅速发生变化,例如: 物理机集群 -> k8s 集群,破坏了我们的服务实例 ip 不会变的假设 基础设施给服务的资源分配方式发生变化,超卖使以往未经审慎设计的代码在恶劣环境下更易崩溃 服务数量的爆炸式增长使每个内部的微服务都要面对

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,
京ICP备15065353号-1