MoreRSS

site iconJoway修改

工程师,新加坡字节跳动,喜欢摄影、旅行。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Joway的 RSS 预览

重新思考 Go:了解程序在线上是如何运行的

2024-12-10 08:00:00

重新思考 Go 系列:这个系列希望结合工作中在 Go 编程与性能优化中遇到过的问题,探讨 Go 在语言哲学、底层实现和现实需求三者之间关系与矛盾。 前言 过去一段时间,在大量的线上服务 case study 过程中,逐步深入了解了如今的业务 Go 进程是如何在一系列繁杂的基础设施之上运行的。有些表现在意料之中,也有一些出乎意料的发现。 我很喜欢一个叫做「 机械同理心/Mechanical Sympathy 」的概念,大体的意思是你必须深刻了解你的程序/机械装置是在一种怎么样的环境下运行的,设身处地地在这个运行环境下去思考,才能帮助你写出更好的程序,或是解答一些奇怪现象的原因。 本文希望达到的目的,也是构建这份「机械同理心」。 程序能使用多少计算资源? 对于很多人来说,这个问题似乎非常简单,大多的计算平台都会要求你建立服务的时候就指定好「需要的计算资源」,但这与你的「能使用的计算资源」是两件事情。实际上这个问题的复杂性远远超出大部分人现象,甚至都不是一个常量,也无法使用公式简单计算。抽象地讲,这取决于天时地利人和。 「天时」指的是内核 Cgroups 以及容器调度平台的基本原理和参数设置。这是硬指标,大部分时候也是常量。 「地利」指的是部署的实际物理机的繁忙程度。 「人和」指的是写程序的人得在能够有更多计算资源可用的时候真的让自己程序用上这些计算资源。 接下来我们逐步分节来详细探究这个问题。 被封装的 CPU 谎言 在 Oncall 中常见的一个误解是,研发人员在容器平台上申请了 4 核 CPU 的容器,然后自然而然认为自己的程序最多只能使用 4 个 CPU,于是按照这个计算能力去估算需要的容器数量,以及对自己程序套上这个假设进行参数调优。 上线后,进到容器用 top 一看,各项指标确实是按照 4 核的标准在进行。甚至用 cat /proc/cpuinfo 一看,不多不少刚好看到有 4 个 CPU。。。 但实际上,这一切都只是容器平台为你封装出来的一个美好的假象。之所以要把这个假象做的这么逼真,只是为了让你摆脱编程时的心智负担,顺便再让那些传统的 Linux 观测工具在容器环境中也能正常运行。 但是假象终究不是真相,如果有一天你遇到一些疑难问题或是想要去编写极致性能的代码,你会发现这些封装出来的抽象把你的理解带的有多偏。 我到底能同时使用多少个 CPU ? 在 K8s 的环境里,CPU 是一个时间单位而非数量。正常情况下,一个拥有 64 核心的机器,你最多能同时使用的理论 CPU 个数是 64 ,即便你创建容器的时候申请的是 4 核。

欧游散记 —— 环勃朗峰之旅

2024-10-18 08:00:00

TMB - Tour du Mont Blanc 很多年前就听说勃朗峰周边有一条多日徒步线路,从霞慕尼出发,环绕勃朗峰徒步一整圈最后回到霞慕尼。但是由于很难请到这么长时间的假期,所以一直没能如愿。甚至四年前的时候还去到过霞慕尼,但是那时候只是把它作为一个景点,简单去了下南针峰从山顶观看勃朗峰。那时甚至都霞慕尼这个地方大头来头,也没来得及在当地住一晚细细品味它的户外氛围。 今年十月,终于得愿完成了这条此生必去的徒步线路。出于安全和方便的考虑,我在网上订了一个包含了住宿餐食和山地向导的国人徒步团,基本把除了走路之外的麻烦事都帮我解决了,每天就是到点徒步,到站睡觉。唯一的遗憾是,这次走的是所谓的精华路线而非完整路线,大概只覆盖了完整线路的一半左右。希望未来有一天可以以个人的形式重新完整走一次这条线路。 行前准备 身体准备 在 2024 年前半年的时间里,颈椎病让我的身体状态特别糟糕,别说徒步,感觉长时间的伏案工作都已经很成问题。所以我上半年的大部分业余时间都花在了锻炼身体和用各种手段治疗颈椎病上。这也让我的身体素质得以迅速提升,刚好在九月已经恢复到几年前的水平。 由于长期居住在新加坡,所以很难找到一些较大强度的多日运动能够来测试我的身体状态,这次,我也是拿 TMB 来为我半年的身体训练做一次压力测试。说实话,在去之前,我是做好了不行就撤退的准备。所幸的是,我不仅没有撤退,甚至走的还算是在队伍的前列,这可能是整趟旅程最让我高兴的事情。 装备准备 十月 TMB 的气温大概在 5 度到 20 度左右,我最后真正用到的装备有: 防护层 1 件 Patagonia Boulder Fork Rain Jacket 1 条 Outdoor Research Ferrosi Convertible Pants 1 条 KAILAS 徒步裤 中间层 1 件 Patagonia Down Sweater 1 件 Patagonia R1 贴身层 1 件 Decathlon 美丽奴羊毛 T 恤 1 件 Smartwool 美利奴羊毛衣 1 条 Icebreaker 美利奴羊毛裤 2 双 Smartwool Full Cushion 美利奴羊毛袜 4 条 Smartwool 美利奴羊毛内裤 徒步装备 Hanchor MARL X 轻量化背包 Salomon X Ultra 4 Mid GTX 徒步鞋 Black Diamond Distance Carbon Z 登山杖 其中,Patagonia 的 Boulder Fork Rain Jacket 配合 Hanchor MARL X 防雨背包,绝对是提升我整段徒步旅程体验最大的装备。在风雨雪交加的情况下,我没有一次用到雨衣和背包防雨罩。在降水量最大的一天,也只是导致了我中间层羽绒个别地方略微湿润,背包里非常潮湿,但是都没有明显进水的迹象。考虑到我非常讨厌行动受限的感觉,这两款装备的互相配合基本能让我在中等程度降雨量的情况下,都维持自由行走的状态。

欧游散记 —— 特摩索斯古城

2024-04-07 08:00:00

特摩索斯(Termessos)位于土耳其南部城市安塔利亚的北面大约 30 公里,是一座起源神秘,消亡也神秘的深山古城。它第一次被历史提到是在公元前 333 年亚历山大大帝包围这座城市时,但即便是征服了希腊世界的亚历山大大帝也并未能成功征服特摩索斯。 特摩索斯建于海拔 1000 多米的山口位置,由于特殊的地理位置,加上完善的水利设施,所以即便被包围也只需要一小只军队便可守卫,这帮助这座城市在那个群雄逐鹿的希腊化时期和罗马帝国时期得以一直以独立城邦的状态存活下来。一直到公元 5 世纪的一场地震摧毁了这座城市的蓄水库,导致居民不得已陆续搬迁,最终荒废并被人们所遗忘。一直到 19 世纪,欧洲的探险家才重新发现了这座城市,但即便到今天,特摩索斯一直是一个非常冷门的旅行目的地。 我知道特摩索斯,是机缘巧合在 YouTube 搜索安塔利亚的视频时,偶然间看到了一个特摩索斯徒步视频,即便画质极其粗糙,依然还是被这座山顶古城给震撼到了。更因此将安塔利亚作为了土耳其旅行的其中一站。 到达安塔利亚,我们在 Airbnb 上约了一个本地向导 Onder 带我们开车到达北部的居呂克山-特摩索斯国家公园,从国家公园徒步前往特摩索斯所在的山顶。Onder 的本职工作是老师,但十分热爱特摩索斯甚至他的硕士论文写的课题便是特摩索斯,所以业余时间也通过做特摩索斯的向导赚点外快。 虽然特摩索斯的城市部分主要集中在半山腰和山顶,但是山底相当于这座城市的郊区,和现代城市的郊区一样,往往是平民的生活聚居地。 首先看到的是城市的宗教区域,哈德良庙,但如今只剩下了一扇门孤零零地在山脚下伫立,既神圣又凄凉: 在哈德良庙的另一边,是居民的墓地区。特摩索斯的墓十分有特色,都是以露天石棺的形式“展览”在地表之上。石棺的雕刻也别有一番讲究。 普通平民的石棺是以两个太阳加中间方块的形式,太阳是特摩索斯的标志,这里也被称之为太阳城: 而士兵的石棺会在太阳上增加武器的标识,寓意太阳的捍卫者: 无论是平民还是士兵的石棺都略显单调和同质,特别是人死后,棺材还会被摆在地表被长久展示,这导致石棺本身变成了一个人生命价值的化身。如果没有钱请不起好的雕刻工匠,就靠战斗获得荣誉来装点自己的石棺。如果有钱,就极尽工匠精湛的工艺让自己的石棺变成华丽的艺术品: 又或者是更为有权势的领袖,可以直接在岩壁开凿更为壮阔的仰望式墓藏: 考虑到两千年后,我们还依然在为他们的石棺赞叹,当初他们的「虚荣」如今也被岁月洗涤成了「实荣」。 沿着山路,陆陆续续会走过众多城墙和古罗马式引水渠,以及如今已不知为何物的废墟: 在半山腰,还能看到一个保存地非常好的体育馆遗址: 以及曾经的祭坛: 接着便来到了特摩索斯城市最最核心的基础设施 —— 蓄水库: 由于地震已经毁坏了蓄水库很大一部分,所以今天的蓄水库为了维持稳定,能够看到有很多人工固定的痕迹,但是能够两千年前在一个山顶开凿一个如此巨大的地下空间还是非常令人震撼的。 大约花了一小时的路程,便可抵达特摩索斯的山顶,在一个转身间,看到了我在土耳其见过的最震撼的一幕: 特摩索斯在人类历史上,只是一个非常短暂,也没有名气,更没有对任何历史节点产生重要影响的小城市。特摩索斯的统治者也并非历史上赫赫有名的王侯将相,甚至这座城市更像是纯粹的自治军事城邦而非君主制的王国。但是如此小的城市居然会为了其居民建立如此恢弘的一个剧场。更何况这里不靠近雅典也不靠近罗马,地处希腊世界的边陲。 站在特摩索斯的山顶上,我眼望着希腊文明的「边际产物」,如同看到了一个外星文明一般陌生。从山脚下看到的自我价值实现和山顶上对自然与人类文明极致的审美表达。特摩索斯的居民在群雄逐鹿中维持了独立,依靠工程学知识建立起了少见的防卫居住一体的水利设施,甚至还在这种山城里建立起了不输其他同等规模但地处丰饶平地的希腊城邦的剧院。 我在之后的土耳其境内的希腊化古城邦旅途中,也遇到了更多比特摩索斯大得多,恢弘得多的城邦,但是特摩索斯一直是最特别的那个,也是我记忆最深的一个。以特摩索斯作为我在古希腊-罗马世界的游历起点,很难想到比这更好的开始了。

重新思考 Go:Channel 不是「消息队列」

2024-03-31 08:00:00

重新思考 Go 系列:这个系列希望结合工作中在 Go 编程与性能优化中遇到过的问题,探讨 Go 在语言哲学、底层实现和现实需求三者之间关系与矛盾。 Go 语言是一门为实现 CSP 并发模型而设计的语言,这也是它区别于其他语言最大的特色。而为了实现这一点,Go 在语法上就内置了 chan 的数据结构来作为不同协程间通信的载体。 Go 的 channel 提供 input 和 output 两种操作语法。input 一个已经 full 的 channel ,或是 output 一个 empty 的 channel 都会引发整个协程的阻塞。这个协程阻塞性能代价很低,也是协程让渡执行权的主要方法。 ch := make(chan int, 1024) // input ch <- 1 // output <-ch 然而 channel 的实现恰好和进程内消息队列的大部分需求是吻合的,所以这个结构时常被用来作为生产者消费者模型的实现,甚至还作为 channel 的主流应用场景而推广。 但事实上,如果真的把该数据结构用来作为系统内核心链路的生产消费者模型底层实现,一不留神就会遇到雪崩级别的问题,且这些问题都不是简单的代码修改便能解决的。 Input 失败导致阻塞 当 channel 满的时候,<- 操作会导致整个 goroutine 阻塞。显然这并不总是编程者希望的,所以 Go 提供了 select case 的方法来判断 <- 是否成功: select { case ch <- 1: default: // input failed } 但问题是,当 channel input 失败时,编程者还能怎么做?除非队列的消息是可以被丢弃的,否则我们可能只再去创建一个类似 queue 的结构,将这部分消息缓存下来。但是这个 queue 的结构可能又要和这个 ch 本身的队列顺序处理好并发关系。

重新思考 Go:Slice 只是「操作视图」

2024-03-30 08:00:00

重新思考 Go 系列:这个系列希望结合工作中在 Go 编程与性能优化中遇到过的问题,探讨 Go 在语言哲学、底层实现和现实需求三者之间关系与矛盾。 Go 在语法级别上提供了 Slice 类型作为对底层内存的一个「操作视图」: var sh []any // ==> internal struct of []any type SliceHeader struct { Data uintptr Len int Cap int } 编程者可以使用一些近似 Python 的语法来表达对底层内存边界的控制: var buf = make([]byte, 1000) tmp := buf[:100] // {Len=100, Cap=1000} tmp = buf[100:] // {Len=900, Cap=900} tmp = buf[100:200:200] // {Len=100, Cap=100} 虽然 Slice 的语法看似简单,但编程者需要时刻记住一点就是 Slice 只是一个对底层内存的「操作视图」,而非底层「内存表示」,Slice 的各种语法本身并不改变底层内存。绝大部分 Slice 有关的编程陷阱根源就在于两者的差异。 Slice 陷阱:持有内存被「放大」 以最简单的从连接中读取一段数据为例,由于我们事先并不知道将会读取到多少数据,所以会预先创建 1024 字节的 buffer ,然而如果此时我们只读取到了 n bytes, n 远小于 1024,并返回了一个 len=n 的 slice,此时这个 slice 的真实内存大小依然是 1024。

那一天,我决定踏出一步

2022-05-10 08:00:00

四月与五月之交,我完成了人生中迄今为止最惊心动魄的一次冒险。与通常的冒险经历不同的是,一般的冒险人们总愿意在往后的岁月里反复回忆甚至加工,但我的这次冒险我希望此生永远的忘记,但我又明白,之所以我如此迫切地想要忘记,是因为这段经历将会不可逆地影响我一生。 过去的那个少年已经被这场冒险所杀死,我只是作为一个旁观者,在叙述一个已经去世了的人在那几天的经历。 这场冒险的源头还要从 3 月开始说起。 3 月初,我在北京出差,在返回上海的前两天,当时因为北京有人在乌克兰大使馆门口献花,导致三里屯的使馆区莫名其妙被政府封了。我和朋友那天晚上临时起意,打算去现场看看,于是就绕着乌克兰大使馆走出了一个方形的圈。这件事情与后面发生的事情并没有什么直接的联系,但它确是我在中国境内拥有的最后一次自由行走的经历,而我在三里屯画下的这个圈,也成为了后面发生的事情的一个隐喻。 2022 年 3 月 4 日,我从北京回到上海,而这一天北京办公室却检测出了一例阳性,导致我回到上海后被判定为所谓的「高危筛查人员」,于是就开始了原定于 14 天的居家隔离。然而从 3 月 16 号开始,整个打浦桥街道开始出现了非常多核酸异常的情况,我小区在内的相当一部分小区变成了所谓的封闭管理。每天都需要做一次核酸,但是小区里的居民此时并不知道问题的严重性,甚至我怀疑连居委会都不见得知道真实的核酸异常数据,所以在做核酸时,大家有说有笑,许多老头老太甚至在排队时都不戴口罩。 当时所有人的预期是,所谓的 2+12 封闭管理就是只需要小区被封闭两天而已。这对于年轻人来说,可能平常出小区的频率也就两天一次,所以几乎没有任何影响。并且此时我们依然可以在小区内自由活动,去门口取外卖。 3 月 18 号,这是原定小区解除封闭管理的一天,而我个人也早已满足了 14 天的居家隔离要求。那天中午,我兴冲冲地跑下楼,想要去商场里面吃一顿好的,结果发现小区并没有如期的解封,反而是又贴了一份继续 +2 的通知。此时我的愤怒在于它打破了我们原先的预期,导致原先的欢喜落空,但我依然相信只要再坚持两天就能解封。 3 月 20 号的时候,我楼道门突然在毫无事先通知的情况下被一道铁链锁住。试图去推开一道推不开的铁门,这种屈辱感和无力感永生难忘。 20 日晚上等了 2 个小时,才吃到已经在外面冻冰了的鳗鱼饭,21 日中午又等了 2 个小时拿不到外卖然后吃的泡面的时候,而当天晚上彻底外卖就没送过来了。那时候才第一次知道原来饿久了,眼睛会花手会抖。不知道明天会发生什么,但当天晚上我就把恒生指数的基金清仓了。 23 日已经彻底放弃了外卖这件事情,中午八宝粥,晚上吃泡面加饼干。接下来的每一天都接近于此。点外卖几乎只能靠金钱加上运气,偶尔会有一些商贩“违规”在外卖平台上上线一些熟食之类的东西,那时候偶尔才能够有一点加餐。甚至有一次还抢到了能够让我吃上两天的两桶麦当劳全家桶。 再接着就是大家所熟知的 4 月开始上海进入事实上的全面封城状态。 我所在的黄浦区,在整个上海属于重灾区,而我的小区又是黄浦区的重灾区,我所在的楼栋又属于小区里的重灾区,先于上海绝大部分小区率先封闭管理。上海发布每过几天通报一次我楼栋有了新的阳性。截止我离开的时候,楼栋里阳性户数至少也有 30%,悲观估计可能多达 50%。 4 月 18 号的时候,我明白自己不可能继续这种生活,开始规划逃离上海的行程,于是给居委打了电话咨询了开出门证的要求,让我意外的是,虽然我的楼栋形势严峻,但是给我的要求却还算合理(当然事实证明我天真了): 楼栋 7 天无阳性 48 小时核酸阴性 全程闭环车运输 前序航班到达 其中其他几项都可以通过花钱解决,唯独所在楼栋七天无阳性这一项对我来说充满了不确定性。对于在上海的绝大部分人来说,这个要求确实并不过分,也很容易达到。但对于我这栋重灾楼来说,就需要时刻算着一个 7 天的窗口期,到了窗口期立马走。 我们楼上一个阳性是 4 月 14 号出现的,所以下一个 7 天窗口期是 4 月 22 号,但是好巧不巧 21 号又出了阳性,所以需要等到 4 月 29 号。