标签归档:翻译

【翻译】如何快速给 MacBook 预热

前言

文章来源:z3ugma.github.io

原文标题:Warm up your MacBook

原文发布时间:2019-11-18

原文链接:https://z3ugma.github.io/2019/11/18/warm-up-your-macbook/

本文根据原文内容翻译整理,并补充了适用范围与使用注意事项,译文由 AI 辅助完成。


这是一篇带点玩笑意味的实用短文:如果你刚从冬天室外、冷车厢,或者空调很足的环境里拿出 MacBook,金属机身摸起来冰凉,想让它快一点“热起来”,最直接的方法其实不是等,而是让 CPU 开始工作

如何快速给 MacBook 预热

如果你只是想粗暴地让机器升温,可以打开终端,执行下面这条命令:

yes > /dev/null

这个命令会持续输出 y,再把输出丢进黑洞设备 /dev/null,从而让一个 CPU 核心持续忙碌起来。机器开始算东西,温度自然就会上去,机身也会慢慢暖和。

不过,如果你想要一个更可控的方式,原文更推荐使用 stress 这个小工具。先用 Homebrew 安装:

brew install stress

然后运行:

stress -c 6 -m 2 -t 300

这条命令的意思是:

  • -c 6:启动 6 个 CPU 压力任务;
  • -m 2:再启动 2 个不断做内存分配/释放的任务;
  • -t 300:持续 300 秒,也就是 5 分钟后自动停止。

这样做的好处是,你不用自己记得去停掉进程,时间到了它会自动结束。

如果你觉得这个操作以后还会经常用,原文建议顺手在 shell 配置里加一个别名,例如:

alias warm='stress -c 6 -m 2 -t 300'

以后只要输入:

warm

MacBook 就会开始“自我发热”。

补充说明

原文写于 2019 年,更贴近当时的 Intel MacBook 使用场景。放到今天,有几点值得补充:

1. yes > /dev/null 只会打满一个核心

在多核机器上,这条命令并不会让整台机器瞬间满载,它只是让一个核心一直忙。想要更明显地升温,还是 stress 这类可以并发压测的工具更直接。

2. 不要把“预热”变成“过热”

这类做法本质上是在主动制造负载。短时间内问题不大,但如果你把任务开得太猛、时间拉得太长,风扇噪音、电池消耗、表面温度都会明显上升。放在腿上、被子上或者不通风的地方跑,就更不推荐了。

3. Apple Silicon 时代通常没那么需要

现在的 M 系列 MacBook 在能耗和发热控制上比当年的 Intel 机型好很多。多数情况下,你可能根本不需要专门“预热”它;如果只是嫌机身太凉,降低空调、换个环境,往往比专门压 CPU 更自然。

4. 想停下来就直接中断

如果你是手动运行 yesstress,发现已经够热了,直接按 Ctrl+C 即可结束。不要让它在后台一直跑着忘了关。

小结

这篇原文其实就讲了一件事:想让冷冰冰的 MacBook 快点变暖,最简单的方法就是让 CPU 忙起来。

2019 年看,这是一个略带黑色幽默的 MacBook 小技巧;今天再看,更像是一篇属于 Intel 时代的轻松小品。拿来了解一下无妨,真要使用时,记得适可而止。

【翻译】这篇博客在 Ubuntu 16.04 上跑了 10 年,我把它迁移到了 FreeBSD

前言

文章来源:CrociDB

原文作者:CrociDB(Antonio Vivace)

原文标题:This blog ran on Ubuntu 16.04 for 10 years. I migrated it to FreeBSD

原文发布时间:2026-05-21

原文链接:https://crocidb.com/post/this-blog-ran-on-ubuntu-16-04-for-10-years-i-migrated-it-to-freebsd/

本文为全文翻译,译文由 AI 辅助整理,若有理解偏差欢迎指正。


这篇博客已经在一台 DigitalOcean VPS 上运行了十多年。那是一台托管在纽约、运行着 Ubuntu 16.04 LTS 的机器。而这个 LTS 版本至少已经有 5 年不再受支持了。是时候做些改变了。经过一番权衡,我把它迁移到了一台 Hetzner 的虚拟机上:配置比我旧的 Ubuntu 机器强得多,价格还不到以前的一半,而且机房就在我所在国家的另一端。不仅如此,我还顺便挑战了一下自己,把整套栈迁到了 FreeBSD。这篇文章会有点长,但如果你愿意读下去,会看到一个关于 FreeBSD JailsBastille,以及一些有意思的网站负载测试结果的故事。

动机

如果你了解 Ubuntu 的发布和支持周期(我自己其实也不算熟),就会知道一旦某个版本结束支持,apt 对应的软件仓库也就没了,你再也拿不到更新。运行这样一套过时系统会带来不少问题,其中最显而易见的一点就是:你的服务器不再那么安全了。互联网上肯定有各种机器人,专门扫描存在漏洞的节点,然后想办法塞进去恶意内容。幸运的是(我想大概算幸运),我这里一直没出过事。反正机器里也没什么特别重要的东西可偷。但我记得很久以前,我还有一个 WordPress 博客,也是跑在一台老旧 VPS 上,结果某天文章正文里突然被插满了可疑的赌场和赌博链接。

我原本就有一台 Hetzner VPS,当作远程开发机来用,可以随时 SSH 上去,考虑到价格,它一直都很可靠。所以我决定先从对比配置开始。下面这台就是原来跑我的博客和其它网站的 droplet:

(原文此处有旧 DigitalOcean 服务器截图)

它有 2GB 内存、1 个 vCPU、50GB 磁盘、每月 2TB 流量,运行的是 Ubuntu 16.04 x64。机房位于纽约,这大概也是它比较贵的原因:我每个月要为它支付 13 美元。

Hetzner 是一家老牌欧洲 IT 公司,在德国有大型数据中心,而我也住在德国。它最便宜的一款服务器,只要 3.56 欧元,配置就已经比我原来那台更好了:

(原文此处有 Hetzner 入门套餐截图)

内存和 CPU 都翻倍,存储稍微少一点,但月流量多了十倍。不过我最后还是选了一台更“豪华”的配置,每月不到 6 欧元:

(原文此处有更高配套餐截图)

对我的这些站点来说,这配置甚至有点过头了,不过为什么不呢?

旧的部署方式

我那套旧环境托管的不只是这个博客,还有几个别的网站。它们都不算热门;在所有站点里,这个博客流量算最高的了,但每个月也不过几千次页面浏览。除了偶尔有几篇文章在 Hacker News 上爆一下,平时根本没多少流量。说到底,这台机器基本就是在提供静态网站服务,没有复杂 CGI,也没有什么自定义后端代码在跑。整套栈很简单:所有内容都通过 nginx/1.10.3 以静态方式提供。我基本上就是在 /etc/nginx/sites-available 里给每个站点放一份配置文件。像静态站点生成器、LaTeX 套件之类的附加程序(比如这个博客就是用 Hugo 生成的)则通过 aptsnap 安装。事实上,我更新博客的流程一直是这样:

  • 在本地写文章
  • 提交并推送到仓库
  • SSH 登录服务器
  • 拉取仓库更新
  • 运行 hugo

这台 VPS 刚开始用的那些年里,我还会拿它做一些测试和编程实验。所以机器里积累了很多已经不再使用的老旧软件,显得很臃肿。但它就是能跑,而且跑得还挺好。

(原文此处有 Linux 4.4 截图)

它甚至好到什么程度呢?我关机前,它的 uptime 达到了 1491 天,也就是差不多连续 4 年没中断过。

为什么选 FreeBSD

说实话,我的主要动机之一就是想亲手折腾点不一样的东西。我最近读了很多、也看了很多关于 BSD 家族的内容,而我之前也曾短暂用过 FreeBSD,所以我觉得这是一个把它放到真实生产场景里试一试的好机会。FreeBSD 常常因为它的一体化设计、安全性以及 Jails 而被人称赞。后面我会详细展开。

我不想装得好像自己一开始就完全知道该怎么做,但当我读到 Jails 时,我立刻就知道自己想怎么搭了。Jails 是一种虚拟化/容器化机制,已经存在于 FreeBSD 里超过 25 年了,比 Docker 诞生还早得多。乍一看,它实现的效果和你对 Docker 容器的预期很像:在系统里面隔离出一个“小系统”,你可以在里面运行一些拿不到宿主机权限的东西。但在我看来,Docker 以及其它容器方案更适合“打包程序”——它们强调短生命周期和不可变;容器内处理的数据来自外部,但容器内部本身最好始终保持一致。而 Jails 更像真正的子系统,几乎可以把它看作迷你虚拟机,只不过底层仍然共享同一个内核。

除此之外,FreeBSD 的文件系统 ZFS 对服务器场景也确实非常好用。如果你来自 Linux 世界,大概听说过 Btrfs,它是近几年越来越多发行版开始采用的新文件系统。它和 ZFS 有一些相似点,比如数据完整性和快照。不过 ZFS 大概比 Btrfs 要成熟得多。如果我能频繁地给系统做快照,就不需要依赖 VPS 提供商的快照或备份服务,而那些通常都要额外付费。

我的想法是:每个网站一个 Jail,里面装上构建这个网站所需的工具(比如博客需要 Hugo),再在 Jail 里运行一个 nginx 实例来提供服务;另外再单独放一个主 Web 服务器 Jail,通过反向代理把外部世界和这些站点连起来。这样一来,哪怕某个 Jail 被攻破了,我也可以把它直接销毁,再建一个新的。后面我会更具体地介绍配置过程。

Hetzner VPS

先给所有喜欢看系统信息的人一点细节:

(原文此处有 fastfetch 截图)

搭建过程

我会快速讲一下自己是如何把这台服务器搭起来的,以及其中最关键的几个点。平时我会记开发日志,所以这次也是边做边记。不过不要把这篇文章当作教程,因为我可能漏掉了一些步骤。更准确地说,这是一篇“在 Hetzner 上用 FreeBSD 搭 Web 服务器是什么感觉”的记录。

安装 FreeBSD

Hetzner 在创建虚拟机时会提供一些镜像,但选择其实非常有限:

(原文此处有 “No BSD” 截图)

不过我在 FreeBSD 官方 YouTube 频道 上看到了一份讲如何在 Hetzner 上安装 FreeBSD 的指南。顺带一提,这个频道非常推荐。

关键在于:Hetzner 其实提供了 FreeBSD 镜像,只是藏得比较深,需要多点几步。它是以 ISO 镜像的形式提供的。所以在创建虚拟机、选择操作系统镜像的时候,你先随便选一个,反正稍后都要把它抹掉。创建完虚拟机后,只需要去控制台里的 ISO Images 标签页,挂载你想要的镜像即可:

(原文此处有 FreeBSD ISO 镜像截图)

我选的是 14.3。然后重启,基本上就按安装器一路走下去。印象里我没有改什么默认选项,当时是跟着官方 FreeBSD 频道里的安装视频一步步做的。很快,系统就装好并跑起来了。

Bastille

前面我已经提了好几次 Jails。但我用的不只是 Jails,还用了 Bastille,这是一个用来管理 Jails 的系统。原因很简单:手动创建 Jail 这件事比我想象中复杂,而且每新建一个 Jail,都有一堆独立步骤需要自己处理。Bastille 把这些都简化了,很多事情只差一条 bastille 命令。比如 bastille list 可以列出系统里的所有 Jail,bastille create 用来创建新 Jail,bastille console 则可以直接打开某个 Jail 的 shell,等等。

安装并启用它(几乎)就像 官方文档 里说的这么简单:

pkg install bastille
sysrc bastille_enable="YES"

当然也有别的 Jail 管理器,我只是选了一个名字最酷的。

整体架构

整套思路是:先有一个运行 Caddy 的 Jail,负责对外提供所有站点,并处理域名和 SSL 证书;然后每个站点再单独有自己的 Jail,里面安装各自构建和服务所需要的软件。这个“服务器 Jail”会把所有流量反向代理到对应的站点 Jail。所以第一步就是配置一个内部虚拟网卡。你可以把这套结构理解成:一组跑在同一个宿主机里的小型虚拟机网络。

配置虚拟网络接口的方式如下:

sudo sysrc cloned_interfaces+="lo1"
sudo sysrc ifconfig_lo1_name="bastille0"
sudo service netif cloneup
sudo sysrc ifconfig_bastille0="inet 10.0.0.1 netmask 255.255.255.0"

这几条命令本质上就是克隆一个回环接口,把它命名为 bastille0,再给它分配网络参数。所有 Jail 只会跑在这张网络接口上。至于 Caddy 所在的 Jail,因为它需要监听外部请求,就必须具备访问外网的能力。为此,我们要用 PF(Packet Filter,也就是 FreeBSD 的 防火墙)配置一条出网规则。

我们需要编辑 /etc/pf.conf

# the main interface `vtnet0` and the virtual one we created `bastille0`
ext_if = "vtnet0"
int_if = "bastille0"
vpn_if = "tailscale1"

# skip internal traffic
set skip on $int_if
set skip on $vpn_if

# allow outbound access to the internet from the jails
nat on $ext_if from 10.0.0.0/24 to any -> ($ext_if)

# redirect HTTP/HTTPS traffic from the web to the server jail (10.0.0.5)
rdr pass on $ext_if proto tcp from any to any port {80, 443} -> 10.0.0.5

# block everything else
block all

# allows outbound traffic from host
pass out quick on $ext_if keep state

正如配置里的注释所写,这段规则主要做了几件事:把 bastille0tailscale1 上的内部流量跳过处理;允许 Jail 和宿主机出网;再把所有 80443(HTTP/HTTPS)流量重定向到内部 IP 10.0.0.5,也就是我们的 Caddy 服务器。

然后启用 PF:

sysrc pf_enable="YES"
service pf start
sysrc gateway_enable="YES"

至此,网络层的基础就完成了。接下来开始创建 Caddy 服务器 Jail。

创建第一个 Jail:Caddy Server

我的旧服务器一直在跑老朋友 nginx。除了早年短暂用过一点 Apache,我人生的大多数时间都在用 nginx。不过 nginx 有个烦人的点,而 Caddy 正好能漂亮地解决:SSL 证书。以前在旧服务器上,我得隔三差五跑一遍 certbot 去续域名证书,而且续签忘掉的次数比我愿意承认的要多。Caddy 会自动处理这些事情。

要创建一个新 Jail,先得为当前要使用的 FreeBSD 版本做 bootstrap,在我的场景里是 14.3-RELEASE

bastille bootstrap 14.3-RELEASE

然后创建真正的 Jail:

bastille create caddy 14.3-RELEASE 10.0.0.5 bastille0
bastille start caddy

这里我们创建了一个名为 caddy 的 Jail,使用 14.3-RELEASE,IP 为 10.0.0.5,挂在 bastille0 接口上。

检查 Jail 状态:

# bastille list
 JID  Name      Boot  Prio  State  Type   IP Address  Published Ports  Release       Tags
 3    caddy     on    99    Up     thin   10.0.0.5    -                14.3-RELEASE  -

这样它就已经跑起来了!要记住,Jails 并不是像 Docker 容器那样追求 ephemeral 的东西。你不会先把容器完全定义好,然后指望它永远都保持一模一样。这里的感觉更接近虚拟机。你随时都可以通过下面的命令进到它的 shell:

# bastille console caddy

[caddy]:
root@caddy:~ #

配置 Caddy Server

既然我们已经在 caddy 这个 Jail 里了,那只需要把 Caddy 装上:

pkg install caddy
sysrc caddy_enable="YES"
service caddy start

Caddy 的所有配置都放在 Jail 里的 /usr/local/etc/caddy/Caddyfile。如果我们想在宿主机上也能直接访问这份配置文件,就需要把宿主机上的一个目录挂载到 Jail 里。甚至还可以用只读模式挂载,这样 Jail 自己就无法修改配置。大概像这样:

bastille mount caddy /usr/local/etc/my-caddy-config /usr/local/etc/caddy nullfs ro 0 0

好了,服务器差不多配完了,但我们还没有一个真正的站点。所以先建一个站点 Jail,然后再回来配置 Caddy。

第一个站点:es.cro.to

疫情期间,巴西总统选择把 Covid-19 当作感冒来对待,没有采取任何紧急封锁政策。他不断强调国家不能停摆,承担不起代价,哪怕有人因此死掉也无所谓。这完全无视科学,甚至无视常识。历史已经证明他有多错。对此我很不爽,于是我想做一点“抗议”。

所以我做了一个小页面 es.cro.to。它在葡萄牙语里字面上是“阴囊”的意思,但更常被用来形容一个人是个 混蛋烂人,或者别的类似意思。我买下了 cro.to 这个域名(现在回头看,这域名其实挺酷的,英文里听起来也不错),然后加了一个 es 子域名。它刚好把这个词按字典式的音节拆开,于是我就在页面里放上了这个词的词义和对应生物的图片:

(原文此处有 escroto 页面截图)

它当然也是跑在我的旧服务器上的,而且在封锁那段时间还获得了不小的流量。我决定先从它开始迁,因为它本质上只是一个带着一张压得很惨照片的单页站点,而且现在也没那么重要了。

这个站点和我其它站一样,也是一个 Git 仓库。所以我准备把所有网站都放在宿主机的 /usr/local/www 下,然后把各个站点对应的目录以只读方式挂载到对应 Jail 里。于是,当我把这个站点放到 /usr/local/www/escroto 后,就开始用 Bastille 创建 Jail。这次我直接用了一个用于 nginx 的 bastille 模板:www/nginx。模板其实就是预装一些软件的小脚本。其实不用也行,但既然有,为什么不用呢?

bastille bootstrap https://github.com/bastillebsd/templates
bastille create escroto 14.3-RELEASE 10.0.0.11 bastille0
bastille template escroto www/nginx

我给它分配的 IP 是 10.0.0.11,等会会用到。此时,这个 Jail 里的 nginx 已经在提供默认页面了。它的站点目录位于 /usr/local/www/nginx(注意,FreeBSD 几乎什么都放在 /usr/local 下)。

接着,我把宿主机上的站点目录挂到 Jail 里:

bastille mount escroto /usr/local/www/escroto /usr/local/www/escroto nullfs ro 0 0

这样一来,我在 Jail 内就能访问 /usr/local/www/escroto 这个站点目录了。因为它是个 Git 仓库,里面带有 .git 之类不该被直接提供出去的文件,所以我写了一个 deploy.sh 脚本,把 /usr/local/www/escroto/* 复制到 /usr/local/www/nginx/*,再删掉 .git 目录:

rm -fr /usr/local/www/nginx/*
cp -R /usr/local/www/escroto/* /usr/local/www/nginx/
rm -fr /usr/local/www/nginx/.git

这样以后每次要部署这个站点的新版本,只需要登录宿主机,更新仓库,然后执行这个脚本:

cd /usr/local/www/escroto
git pull
bastille cmd escroto /root/deploy.sh

后来我发现这个思路挺好,于是也给其它站点都放了一份 /root/deploy.sh

让 Caddy 把域名指向这个 Jail

回到 Caddy 配置:

cro.to {
        redir https://es.cro.to{uri} permanent
}

es.cro.to {
        reverse_proxy 10.0.0.11
}

配置非常简单:主域名重定向到 es.cro.to,再由后者反向代理到 10.0.0.11,也就是 escroto Jail。

配置好 DNS 记录后,这个站点就上线了。

部署这个博客

这个博客本身是用 Hugo 写的,仓库放在 GitHub 上。所以我把它克隆到了 /usr/local/www/blog,然后像 escroto 一样创建了一个新的 Jail:

bastille create blog 14.3-RELEASE 10.0.0.12 bastille0
bastille template blog www/nginx
bastille mount blog /usr/local/www/blog /usr/local/www/blog nullfs ro 0 0

然后在这个 Jail 里安装 Hugo:

bastille pkg blog update
bastille pkg blog install gohugo

部署脚本也很类似:

rm -fr /usr/local/www/nginx/*
cd /usr/local/www/blog
hugo -d /usr/local/www/nginx

在正式把站点迁离旧服务器前,我想先做一些基准测试,所以先把博客绑定到了一个旧域名上:

crocidb.cro.to {
        reverse_proxy 10.0.0.12
}

就这么简单,博客已经在新服务器上跑起来了。

给我的服务器做基准测试

在正式更新 DNS 记录前,我想确认这台新服务器到底能不能扛住流量。理论上看它当然行,但万一我哪里配错了呢?于是我开始琢磨,怎么比较旧服务器上的 crocidb.com 和新服务器上的 crocidb.cro.to

一开始我先想清楚,到底该测什么。测延迟没什么意义,因为我知道,对大多数访问我博客的人来说,新服务器的延迟本来就会稍微更长一些。这个站点的大部分流量通常来自北美,其次是欧洲,再然后是南美。不过那点差距其实不至于影响体验。所以更值得测的是:服务能力和高负载表现。

市面上有一些免费的在线工具,可以做比较通用的测试,比如 GTMetrixPingdomWebPageTest。但老实说,这两台服务器在这些工具里的差异几乎只体现在延迟上,其它方面基本差不多。所以我决定往更深一点挖。

我发现了 wrkhey 这两个 HTTP 压测工具。它们本质上就是对网站发起成千上万的并发请求,然后收集诸如请求延迟、错误响应、每秒传输量等信息。比如,我在另一台 Hetzner VPS 上跑了下面这个 wrk 命令:

wrk -t4 -c100 -d30s --latency https://crocidb.com/

这表示让 wrk4 个线程、维持 100 个并发连接、持续压测 30 秒。这个数字其实也不算特别离谱。结果如下:

Running 30s test @ https://crocidb.com/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    89.63ms   18.31ms 189.09ms   95.29%
    Req/Sec   211.74     38.44   313.00     87.69%
Requests/sec:    833.41
Transfer/sec:      8.29MB

而对 crocidb.cro.to 测出来是:

Running 30s test @ https://crocidb.cro.to/
  4 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     6.75ms    3.59ms  51.98ms   74.41%
    Req/Sec     3.08k   260.11     4.34k    81.83%
Requests/sec:  12260.10
Transfer/sec:    130.80MB

旧服务器每秒能处理 833 个请求,而新服务器则达到 12,260。平均延迟则是 89ms6ms。但这当然不公平:压测机器和新服务器在同一个数据中心。所以我想,要不试试挂 VPN,从不同地区去测?

我用的是 Proton VPN,于是写了一个小脚本,从一组地点里选一个,连上 VPN,再跑 wrk。结果非常平淡:旧服务器平均大概 300 req/s,新服务器大概 800 req/s。测试地点如下:

LOCATIONS=(
    "US//New York|US - New York"
    "US//Los Angeles|US - Los Angeles"
    "BR//São Paulo|BR - São Paulo"
    "GB//London|GB - London"
    "DE//Frankfurt|DE - Frankfurt"
    "FR//Paris|FR - Paris"
    "SE//Stockholm|SE - Stockholm"
    "CH//Zurich|CH - Zurich"
    "IN//Mumbai|IN - Mumbai"
    "JP//Tokyo|JP - Tokyo"
    "KR//Seoul|KR - Seoul"
    "SG//Singapore|SG - Singapore"
    "AU//Sydney|AU - Sydney"
    "ZA//Johannesburg|ZA - Johannesburg"
    "AR//Buenos Aires|AR - Buenos Aires"
)

于是我放弃了“用终端用户 VPN 视角来测”的思路,转而采用一种更硬核的办法:直接在世界各地的数据中心创建真正的 VPS 节点来跑测试。

Vultr VPS 基准测试

我需要一家不同于 DigitalOcean 和 Hetzner 的 VPS 提供商,以免底层基础设施带来额外优势。除了这两家,我唯一用过的 VPS 服务商就是 Vultr。于是我决定在不同地区各开一台机器跑测试。因为整个过程是手工完成的,所以最后只选了四个地区:伦敦圣保罗硅谷东京。我在这些地区各创建了一台最便宜的 Fedora 虚拟机。

经过几轮测试后,我发现 hey 更符合我的需求。于是我的流程就变成:SSH 登录测试机,执行脚本,然后把结果拷出来。第一个测试点是圣保罗:

dnf install -y tmux vim htop
wget https://storage.googleapis.com/hey-releases/hey_linux_amd64
chmod +x hey_linux_amd64
./hey_linux_amd64 -n 1000000 -c 10000 -t 10 -z 5m -h2 https://crocidb.com/ > crocidb.com.log
./hey_linux_amd64 -n 1000000 -c 10000 -t 10 -z 5m -h2 https://crocidb.cro.to/ > crocidb.cro.to.log

这会下载 hey,然后以一种非常夸张的配置运行:总请求数 1M,并发 10k,超时 10s(也就是任意单个请求超过 10 秒就算失败),总时长 5m

第一次跑的时候,我很快就发现新 FreeBSD 服务器确实有个问题:它很早就开始失败,因为它根本扛不住 1 万并发连接。查了很多资料后,我发现可以用 netstat -Lan 查看 socket 队列大小,而我的队列值全都是 128。继续追下来才发现,原来 kern.ipc.somaxconn 的默认值就是这个数字。于是我把它调高了:

sysctl kern.ipc.somaxconn=16384

差不多十分钟后,日志跑完了,我开始看结果:

(原文此处有圣保罗 VPS 上 hey 输出对比图)

左边是旧服务器,右边是新服务器,差异简直夸张。

虽然两台机器都返回了不少错误,但 FreeBSD 这边至少完成了预期的 1M 请求,而 Ubuntu 那边连 20k 都没撑住!这个差距实在是太大了

基准结果分析

(原文此处有成功率图表)

旧服务器 crocidb.com 只能完成极少一部分请求,这一点真的很惊人。实际上,它只完成了大约 7% 的请求,而 FreeBSD 服务器完成了 94%。东京地区里新服务器的成功率略微低一点,但我觉得还不至于值得担心。

从每秒请求数来看,新服务器最少也有 3 倍提升,最高甚至能到 11 倍。

(原文此处有 Requests per second 图表)

这里的差距看起来更夸张,而我猜测东京那边的延迟可能确实更高一些。

(原文此处有延迟百分位图表)

如果看延迟百分位(例如 p50 表示 50% 的请求都比这个值更快返回),会发现两台服务器呈现出很不一样的曲线形态。新服务器直到大约 90% 之前都保持比较 线性 的增长,所以表现更可预测;而旧服务器的增长曲线则明显更不稳定。

这说明,即使在高负载下,全球范围内 90% 的访问者在尝试加载我博客首页时,仍然能在 3.5 秒 内拿到内容。我觉得这已经相当不错了。

我没有再继续深挖东京地区的问题,不过目前我也没那么担心。看 hey 的请求阶段分解(也就是它会拆分 DNS、连接、等待响应、读取响应等耗时),确实能看出日本方向的流量更慢一些:

(原文此处有请求阶段耗时分解图)

不过这些数据在我看来又有点怪。首先,第二个域名的 DNS 拨号和查询时间低得离谱,也许是因为它是个 CNAME 记录?其次,resp wait(基本可以理解成首字节时间)和 resp read(传输时间)在这里高得有点异常。但这也可能是因为它统计的只是成功请求:也就是说,旧服务器可能一开始响应很快,但很快就把新请求都拒掉了。

结论

我并不认为这个差距是因为我选的这套栈本身就有多神奇。更有可能的原因是:旧 Ubuntu 系统本身就有一些配置问题,再加上我选的 Hetzner VPS 有 4 个 CPU 核,而旧 DigitalOcean 机器只有 1 个,自然能同时处理更多请求。我也不觉得这在实际意义上真的有多重要,因为这些服务器几乎不可能真的遇到这么大的并发量。也许跑一遍 WebPageTest 这样的 Web 基准工具就已经够了。

真正切换

虽然这些基准测试还留下了很多未解答的问题,但我对结果已经很满意了。所以我直接更新了 DNS 记录。现在,这个博客已经正式跑在那台新机器上了。

说到底,经过很多个小时的实验、折腾、构建、推倒重来之后,我意识到:搭一台用 FreeBSD 托管网站的机器,其实并没有我想象中那么复杂。满足我需求的 Web 托管服务其实也有不少,我本来也完全可以用别的方案。或者装一个 Proxmox,用图形化仪表盘来管理容器和系统。甚至也可以试试它在 FreeBSD 世界里的对应物 Sylve。但我还是很喜欢自己这次走过的这条路,因为整个过程让我学到了很多。

其中几个最主要的收获是:

  • 那台 Ubuntu 服务器真的非常皮实。十年来,它一直把我这些站点的负载扛得很好。最后四年甚至一次都没重启过。而这一切并不需要多复杂的配置。
  • 配置 FreeBSD 比我原本想象的容易。我很喜欢把系统配置集中在一个地方管理的感觉,而且它的在线文档也真的非常好。
  • 想自己搭一台托管博客的机器,往往需要大量网络知识,而这些远远超出了一个游戏开发者通常具备的范围。
  • 学习另一套系统真的很有趣。也许下次我会试试 OpenBSD 或 NetBSD。

不过说到底,这一切也没什么意义,因为我的大部分流量现在反正都来自各种 AI 抓取器……

Refs

【翻译】Programming Still Sucks:编程依然很糟糕

前言

文章来源:stvn.sh / Writing

原文作者:Jerome Choo

原文标题:Programming Still Sucks

原文链接:https://www.stvn.sh/writing/programming-still-sucks-fqffhyp

本文为全文翻译,译文由 AI 辅助整理,若有理解偏差欢迎指正。


2026 年 4 月 19 日 · 阅读约 8 分钟

编程依然很糟糕

编程 · 领导力 · AI

抱歉,Peter。

——

我在一个生日派对上。来的人大多数也都在科技行业工作,但这种场合里总会有那么一个“有正经工作的人”。你知道的,那种做实体工作的,真的在造某些人们需要的东西的人。而这个人总会问出某种变体的同一个问题:你不担心 AI 会抢走你的工作吗?我朝四周瞥了一眼,看见几张脸微微转过来,轻轻翻了个白眼,然后又回到他们原本的聊天里。对,又是这个问题。

他们有个外甥在帮人搭 Shopify 商店。他们听不懂那孩子嘴里一半的词,但他说自己麻烦大了,而且科技行业里所有人都一样。他的外甥是不是得去学一门“手艺”?我们是不是都得这样?

如果酒喝得够多,我就会认真回答,因为那时候我已经不在乎别人觉得我说的话有没有意思、是不是真的了。但通常我只会叹口气,说一句:“当然,会有一点。我们大多数人都会。不担心才奇怪吧,对吧?”他们点点头,然后就转去聊个更轻松的话题,比如我们会不会去核平伊朗。

但事实是,在科技行业工作一直都很糟糕,而且从来都不是他们以为的那个样子。

有些人以为,我的工作就是坐在一张干净的桌子前,待在一间角落办公室里,外面是开放式办公区,摆满长桌,上头放着一排排 MacBook 或 ThinkPad。在我的角落办公室里,我制定完美的计划,我那群完美的员工会为我鼓掌喝彩。没有任何东西能逃过我的目光,每一个决定都由我完美地做出,每一分钱、每一分钟都被精确记录。

当掌声散去,我的员工——或者叫下属,或者在我心情好的时候叫“我的团队”——就开始疯狂敲键盘。敲啊敲,敲啊敲。不久之后,完美的软件就诞生了。它从集体流水线上滚下来,像第一个孩子一样,绝不会出错。

但现实根本不是这么回事。是的,我确实也很不爽自己从来没混到一间角落办公室,但我更忙着陷入恐慌,因为我根本不知道自己在干什么,没人知道,而现在整辆车的轮子都已经飞出去了。CEO 说 AI 让他那位朋友 Jared 的团队生产力高得惊人,高到他能裁掉一半人;但他说这话时像是在炫耀,不是在威胁?我不知道,反正我是感到威胁了,不过这大概只是我的焦虑症又犯了。没事,我总能从那几个在厕所里哭的员工那里借一片 Xanax 吧。

想象一下,你接了一份船长的工作。第一天,你骑车来到港口,兴冲冲地准备见你的船员。你注意到船并不在那儿,但那个你之前聊过、情绪特别亢奋的招聘人员 Greg 朝你挥手,跟你保证这不是问题。接着你被绑到一台投石机上,竟然奇迹般地被发射到了船上。上一任船长在 Captainpalooza 2025 上听另一个船长给他解释了内燃机的概念,然后就想“朝着这个方向开始迭代”。于是他把船点着了。他后来被人从船上推了下去,但顺手把使用手册也一起带走了。这本来也不该是问题,偏偏整艘船都是专门为他定制打造的。船上倒是还有帆,可它们没连在桅杆上;而那台半固定在船尾的内燃机,零件还散落在甲板各处。

你走进甲板下方,想搞明白这艘船到底怎么运作,以及你们究竟要去哪儿。但当你沿着楼梯往下层走时,你却莫名其妙地走进了桅杆里?你问一个水手这是怎么回事。他卡顿了一下,然后说:“你说得完全对!我之前的方法有问题,不过这里有一个更好的楼梯实现方案。”接着桅杆啪地一下倒转过来,你又回到了甲板上,回到一开始站着的地方。帆现在也上下颠倒了,而你的“水手”还兴奋地等着你夸他干得多好。

你又去问另一个人:“等等,我们到底是要去哪儿?”太好了,是个人类!没有卡顿,没有热情却没帮助的回答,是真正的人。她已经一周没睡了。她几乎没看你,只是说:“去问领航员。”
“领航员?”你问。
她抬手一指。那个领航员,是一个按下背后按钮就会说“勇往直前,步步高升”的娃娃。

那娃娃着火了。

这就是现在的工作。你站在一艘着火的船上,手里拿着一张地图,试图搞明白我们他妈到底要去哪里,以及我们到底要怎么去。

你知道这艘船。有些人曾经就在一艘一模一样的船上当工程师。有些人曾经就是那个离开的船长。我不是写给生日派对上的那个“有正经工作的人”看的。我是写给你的。

你曾经也是工程师。你记得代码评审原本是用来做什么的。你记得自己也曾是那个新人,第一份 PR 被某个资深工程师撕得粉碎,但对方愿意花时间解释“为什么”。你不是在 2024 年某天早上醒来后,突然决定要废掉这一切的。

真正发生的是:跑道被砍掉了。董事会会议里压根没有出现“价值观”这个词。CFO 手里拿着一张电子表格。CEO 则刚从一次线下管理层 retreat 回来,有人在那里给他看了一个 agent 在十四分钟里写完一整个功能的演示,而他竟然信了——就像人们在想相信某件事的时候,总是特别容易相信那样——然后他告诉董事会,到第二季度之前,他可以把工程团队裁掉 30%。现在,你的工作就是想办法把这件事做成。

你告诉自己,新人不会有事的。他们会适应,会重新学习技能,会找到别的出路。你告诉自己,资深工程师可以吸收掉少掉的人手,agent 会把缺口补上。你告诉自己,下个季度再回头看看。你在那张名单上签了字。你回了家。你比平时多喝了一点。然后你上床睡觉。

你知道的。

你知道,因为你也曾经是那个工程师——那个不得不去收拾上一位被“简单答案”忽悠瘸了的领导留下烂摊子的工程师。你看着 Goodhart 定律一口一口吞掉速度指标、故事点、测试覆盖率;几乎所有非工程背景的人手里拿过、并当成“工作一切顺利”证据的数字,最后都被它吃掉。你知道,当你引入工具的速度快于培养判断力的速度时,DORA 指标早就在告诉你部署稳定性会发生什么。你知道,当那些本来会发现错误的人被挤出去,或者学会了不再发现错误时,代码库会变成什么样。

你知道。可你还是签字了。因为不这么做的代价,可能就是丢掉工作;而工作背后是房贷、学费、签证,以及那个“等局势稳定下来、以后再把事情修好”的你自己。

可“以后”从来不会来。我们都知道。我也签过一张名单。直到现在,我们还在互相指责到底是谁那张名单更糟。

已经没有新人了。2024 年,我们为他们的消失办过一场葬礼。没人来。机器现在在做他们做的事,而且更便宜。当然,新人的价值从来都不在于他们眼下能产出什么,而在于他们将来会成为什么:那个知道“尸体埋在哪儿”的资深工程师。我们为产出做了优化,于是废除了学徒制。再过几年,我们会一脸困惑地问:那些资深工程师都去哪儿了?是我们亲手把他们打死了。没人会记得。

可即便如此……

在你的某处基础设施里,一定有一个 cron job。它在凌晨 3 点运行。从 2016 年起,它就一直在跑。它做着某件关键的事情。你没法准确告诉我它到底做什么,但你知道有一个人肯定知道,而那个人在 2019 年就离职了。文件开头的注释写着:# DO NOT CHANGE!!! Ask Ben。Ben 联系不上了。过去四年里的每一次路线图规划会议上,“modernize legacy cron(现代化改造遗留 cron)”都会作为候选项目出现。它从来没排上优先级。你甚至亲手把它从列表里删掉过两次。

但总有人让它继续跑着。她叫 Sara。你并不知道这件事。

她五十多岁。她没去过 Captainpalooza。她以前在离总部三条街外的一间小办公室上班。去年,为了省钱,有人把那间办公室关掉了。离她最近、同时有桌子和网络连接的地方就是这艘船,所以她现在会自己带午餐,然后顺着舷梯走到甲板下的一间小舱室。船上没人知道她在那里。还记得 Ben 吗?Ben 从 1998 年开始带她,她后来从 Ben 那里接过了这个 cron job。

她知道 Ben 几年前已经去世了。她还参加了他的葬礼。你不知道这件事。

那个任务卡住的时候——而且它经常会卡住——她就轻轻推它一下,它就会再试一次。电话响了。她确认告警。她推一下。这个任务依赖一个早已失落于时间洪流中的模块。嗯,也不算完全失落,因为 Ben 去世后,她在他的办公桌里找到了一根 U 盘,里面正好有一份备份。没有 agent 碰过它。以后也不会有。

她不是这个行业里“最安全”的人。她代表着一种你无法触碰的东西。她就是你那场转型刚刚删除掉的所有组织记忆,装在一个五十五岁的身体里四处走动。她正是从你亲手废掉的那条学徒制管道里成长出来的:Ben、1998 年、那根 U 盘。她就是那条管道。当她死去时,那个能生产出像她这样的人才的系统,也早就已经不存在了。三年前,是你亲手杀掉它的。你根本不可能再招到一个能替代她的人,因为制造她的机器已经被你砸烂了。

她就像那个拿着勺子在魔多地下挖隧道的人。勺子是她的,隧道也是她的。没有别人想要这把勺子,也没有别人想要这条隧道。而等她死了,这个 cron job 也会死,工资发放会停摆,一家有三万名员工的公司将不得不想办法重新给所有人发薪水,而到那时,只会剩下一个答案:去找一个手里有勺子的人。你找不到的。是你自己确保了这一点。

那个 cron job 负责发工资。你并不知道这件事。

生日派对上的那个家伙还在等我回答。现在我已经喝得太多,没法再撒谎了。我告诉他:不是 AI 抢走了我们的工作。是贪婪。是同一种贪婪——那种把工厂迁去孟加拉、让刚果钴矿里继续存在奴工的贪婪——只不过现在换上了一张新面具。告诉你那个外甥,去做点别的吧。做什么都行。那也未必救得了他,但至少他不用假装,摧毁他生活的东西是一台机器人。

除了 Sara。她在甲板下,带着她的 U 盘。他们动不了她,因为他们甚至不知道她在那里。

而我们其余的人,都在甲板上,盯着那些上下颠倒的桅杆,琢磨那边那个娃娃到底是干什么的。

那娃娃着火了。

Refs

【翻译】如果你以为写代码速度是你的问题,那你还有更大的问题

前言

文章来源:Andrew Murphy

原文标题:If You Thought the Speed of Writing Code Was Your Problem You Have Bigger Problems

原文链接:https://andrewmurphy.io/blog/if-you-thought-the-speed-of-writing-code-was-your-problem-you-have-bigger-problems

本文为博主翻译,若有理解偏差欢迎指正。


周二早上,你们 VP of Engineering 站在投影前,兴奋得像刚在 2017 年买到第一枚加密货币。TA 刚从某个大会(或者厂商晚宴)回来,三杯黑皮诺下肚,看完一场 Demo,然后带回了“好消息”:

“我们要给所有团队上线 AI 编码助手。早期数据显示,代码产出提升 40%。这会彻底改变我们的研发速度。”

会议室里就会出现那种经典场面:一半人在点头,另一半人突然对自己的笔记本屏幕产生了浓厚兴趣。资深工程师脸上写着“要不要现在说真话,还是回去更新 LinkedIn”。

但没有人问最关键的问题:

你说的速度,是朝着什么目标在加速?

因为你们刚刚做了一件事:在整个交付系统里,挑中了本来就不慢的一环,然后把它继续加速。你们给“非瓶颈”砸了钱。

而系统论告诉我们,这不仅不会帮到你,甚至会让情况更糟。

Goldratt 会想跟你聊聊

1984 年,Eli Goldratt 写了《The Goal》。这是一本讲制造业的小说,却对软件交付异常适用。

核心思想是约束理论(Theory of Constraints)

  • 每个系统只有一个真正约束(瓶颈);
  • 整体吞吐量由这个瓶颈决定;
  • 在瓶颈解决前,优化别的环节意义不大。

很多人理解到这里就停了。真正可怕的是下一句:

当你优化的不是瓶颈时,你得到的不是“更快系统”,而是“更坏系统”。

很直观:A 工位更快了,但瓶颈 B 速度不变,于是 A 和 B 之间堆起半成品;库存上升,交付周期变长,B 工位被淹没,优先级更混乱,质量也会下降。

你并没有提速,你只是制造了一场交通堵塞,并把它叫做“生产力”。

恐怖现场:当你“3 倍代码产出”后会发生什么

开发者 PR 提交更快了,听起来很好。但评审人数没变,没人去扩容 reviewer。

于是 PR 堆在队列里:一天、两天、一周。作者已经上下文切换去写下一个 AI 加持功能,回头再看第一个 PR 时,连自己都快不认识了。为了赶队列,评审开始“橡皮图章式”通过;CI 跑 45 分钟,偶发失败,重跑通过;发布还需要人工审批,而审批人正在开“关于会议的会议”;功能在 staging 再躺三天,因为没人真正对“尽快上线”负责。

同时,开发者已经又提了两个 PR。队列越来越长,在制品(WIP)爆炸,人人手里都有 6 件“进行中”,但“真正完成”的反而更少。真正衡量价值交付速度的 cycle time 不降反升。

你会得到一个很荒诞的局面:

  • 代码更多;
  • 软件交付更少;
  • 仪表盘显示“生产力 +40%”。

你们建成了一个世界级工厂:特别擅长生产会堆在地上腐烂的库存。

更糟的是,很多 AI 生成代码没有被任何人真正理解。提示词的人不一定真正“写过”它;值班排障的人也不一定懂它。于是系统可出故障面积变大,而能推理系统的人变少。

更多代码,更少理解。这不是生产力提升,这是定时炸弹。

那真正的瓶颈在哪?

沿着价值流走一遍:从“有人提出想法”到“用户真正获得价值”。瓶颈会自己跳出来。

1. 你根本不清楚该做什么

PM 两个月没访谈真实用户;需求是三句 Jira + 一个 Figma;工程师每天要替产品做几十个没人定义的细节决策。大家在猜。

结果是:你可能用 6 周做了一个功能,最后只有 11 个人用,其中 9 个还是内部 QA。

这不是“交付慢”,而是“我们到底在干嘛”。

在这种环境里加速写代码,只会更快把错误功能做完。

瓶颈是“理解问题”,不是“敲键盘速度”。

2. 代码“写完”之后的所有环节

在多数组织里,写代码可能只占 20%,其余 80% 都在排队。

评审、CI、staging、QA、安全审查、产品验收、发布窗口、灰度……代码在各个环节之间静止等待。很多功能代码半天写完,却两个月后才到生产。

你看见过“紧急修复”9 天才上线,就知道瓶颈根本不在编码。

想提速,先看“等待时间”,而不是“编码时间”。

3. 发布信任的恶性循环

测试不稳定、可观测性混乱、灰度流程没人信,团队越来越怕发布。越怕越攒大包,包越大风险越高,风险越高就更怕。

这时再提高代码产出,只会把“恐惧文化”喂得更肥:更多代码、同样恐惧、更大批次、更低发布频率。

4. 上线了,但到底有没有效果?没人知道

功能发布后,没有像样分析、没有用户回访、没人复盘“问题是否被解决”。于是下一个需求继续猜。

你只是更快地重复“做了—发了—耸肩”的循环。

5. 你的日历才是承重墙

有时瓶颈不是技术,而是协作:

  • 等一个决策会议;
  • 三个团队一个月没对齐 API;
  • 某架构师成了所有设计的单点审批;
  • 季度规划流程太重,紧急事项也要排队。

这都是组织问题、人问题、协调问题。

写代码更快,对这些问题的作用是 0

应该做什么(不性感但有效)

  • 画出价值流:把一个功能从想法到上线的每一步写下来,也写下步骤之间“等了多久”。
  • 衡量 cycle time,不是产出量:别再盯代码行数、PR 数、故事点;看从提交到用户拿到价值要多久。
  • 消灭等待态:评审慢就改评审机制;发布卡人工审批就自动化或降低摩擦;决策依赖会议就拆小决策。
  • 少开工,多完工:限制 WIP,3 个真正完成比 10 个进行中更有价值。
  • 听一线团队的:开发者早就知道瓶颈在哪,只是通常没人认真听。

结语

如果真的想加速交付,正确的管理台词不该是“代码产出提升 40%”,而应是:

“我们做了价值流分析,发现功能平均在流程间等待 9 天。接下来我们要把这个时间砍半。”

写代码速度从来不是大多数团队的核心问题。真正的优势不属于“写得最快”的团队,而属于能持续做到这三件事的团队:

  1. 搞清楚该做什么;
  2. 把它做出来;
  3. 快速、稳定地送到用户手里。

修瓶颈。瓶颈不在键盘。

[翻译] 不要让 AI 替你写作

原文:Don’t Let AI Write For You 发布于 2026年3月8日

当你写一篇文档或文章时,你其实是在提出一个问题,然后回答它。例如,一份 PRD 回答的是”我们应该构建什么?”,一份技术规范回答的是”我们应该如何构建?”有时问题更难回答——”我们到底想要实现什么?”而每一次尝试作答,你都在反思自己是否问对了问题。

但现在,当然,我们有了 LLM。我看到越来越多由 LLM 生成的文档、文章和随笔。对此我想发出警示:每一篇由 LLM 生成的文档,都是一次错失的思考机会,也是一次错失的建立信任的机会。

思考

写作的目的不在于”写完”,而在于加深自己的理解,进而加深周围人的理解。当你被要求写点什么时,你的任务是深入那片混沌,带着结构和理解走出来——征服未知

写作的第二层目标是让自己变得更有能力。这就像锻炼身体。每一次在能力边界上多做一个动作,你就会变得更强。这既不舒适,也需要付出努力。

让 LLM 替你写作,就像花钱雇人替你健身。

建立信任

LLM 生成的写作还带来社会层面的影响。当我发给别人一份带着 LLM 气息的文档时,我只是在证明 LLM 能生成一些近似于”别人想听的话”的内容,而并没有展示出我曾真正与这些想法搏斗过。

这会损害我的公信力——作为一个能够主导这份文档所提出的任何项目的人,这种信任至关重要。这实在可惜,因为我本可以借此机会建立自己的公信力。

LLM 生成的写作不仅损害了写作本身的真实性,也损害了其背后思想的真实性。如果文字是自动生成的,那么其中的想法是否也是如此?

在 Oxide 使用 LLM:LLM 作为写作者

LLM 如何融入写作过程

LLM 在研究和校对工作方面很有价值。它们也适合快速记录信息或转录文字(这两者都不是我所说的”写作”,即”写一篇文章”的那种意义)。

它们尤其擅长生成创意。这一用途之所以有效,是因为即使它们生成了 10 个想法而只有一个有用,也毫无损失。你可以取其精华,弃其糟粕。

这些 LLM 将提高软件交付的效率。但要充分发挥其价值,我们需要在运用它们的同时,同步提升自身的思考深度。