Pickles 4: 芯片升级、流浪猫、展览岛、真假消息与云端性能
Vision Pro M5、街猫喂养、嵊山展览、信息求真、Cloudflare 性能排查。
Apple Vision Pro upgraded with the M5 chip and Dual Knit Band - Apple
苹果发布了新的 Vision Pro 和 Macbook Pro,升级到了最新的 M5 芯片。
Vision Pro 一个很好的体验是链接 Mac 以虚拟窗口的形式办公,但是既然 Vision Pro 已经是 M5 芯片了,为什么不能直接在 Vision Pro 上执行 Mac 系统?
-
在 Hackernews 上收到很多关注的一个项目,在各个地方投放喂猫的箱子,网上的人使用 App 可以买猫粮喂猫。因为是救助流浪猫的,所以有人会有意愿定时打扫和添加猫粮。看了几个视频才发现这个事情是国内的一个公司做的,有点意思。
Hackernews 上第一个评论是说中国缺少帮助流浪动物的法律,但是下面的评论说没美国也缺少😂。
【Hirono小野|时间之后的风景 - 泡泡玛特 POP MART | 小红书 - 你的生活兴趣社区】 😆 3iiQSCdLVut40MX 😆
泡泡玛特在舟山嵊山岛办的展览,玩设计确实有一套,这场景太出片了。年初刚去大洋山玩过,时间原因没有去嵊泗岛,现在看太可惜了。
You did no fact checking, and I must scream – Terence Eden’s Blog
互联网上充斥着不实消息,哪怕是严肃的新闻媒体也可能犯错。其实很多时候检查一下事实不难,但相信自己愿意相信的是人的天性,哪怕就是社交媒体上某个人的一句话,没有任何论据和事实只有一个结论,也有人愿意相信。
Unpacking Cloudflare Workers CPU Performance Benchmarks
独立开发者 Theo Browne 近期发布了一个 benchmark 用来测试 Cloudflare Worker 和 Vercel 的性能,结果发现 Vercel 的 CPU 计算性能要好 3.5x。这个结论很奇怪,因为虽然 Cloudflare Worker 用的是自己开发的运行时而 Vercel 用的 Node.js,但他们的 JS 引擎都是 V8,有这么大的性能差距很不合理。
Cloudflare 的开发者进行了详细的排查,写了这篇文章。这是一篇很好的进行 JS 服务端运行时性能排查的案例。通过排查,Cloudflare 的开发者发现了多个问题,比较典型的有:
- Cloudflare Worker 的调度机制不理想。这个 benchmark 测试是在客户端发请求来测试的,Cloudflare Worker 把突然发来的多个请求发送到同一个 Worker 实例上去,这导致很多任务都在等待。以下是我自己的一个猜测:V8 虽然快,但启动其实很慢,代码执行多次后才能有好的执行效率,Cloudflare Worker 的架构会把所有用户的脚本在所有实例上执行,如果每次都发送到一个全新的实例上每次都需要冷启动,效率自然会比较低。因此他们之前的算法应该是会尽量把请求发送到之前执行过相同脚本的实例上去,但这在大流量的时候就会导致阻塞了。
- V8 的垃圾回收,新生代空间大小设置不合理。早期 Node.js 是推荐自己设置这个空间的,但后续 Node.js 会自己尝试设置合理的值,现在的机器内存都打了很多,脚本代码量也大了很多,如果新生代空间设置的过小,就会导致频繁触发垃圾回收机制。我之前也尝试过调整这个值的大小,发现 Node.js 自己设置的值就已经很合理了,不太需要我们自己设置了。
- OpenNext 项目自己的性能问题。Next.js 是 Vercel 开发的,为了能让他跑在 Node.js 之外的平台上, 就有了 OpenNext 项目,但 Cloudflare 的开发者发现这个项目的很多实现会影响性能:
多余的内存申请和拷贝。这个应该是 Node.js 开发最容易出现的性能问题了,现在 SSR 渲染出的 HTML 体积都不小,一旦拷贝几次性能就会差很多,哪怕不是垃圾回收,光是拷贝时间都要很久。
Stream 的使用姿势问题。这也是老大难问题了,Node.js 之前有自己的 Stream 实现,后来 Web 标准又有了 Streams API,这导致经常需要在两种 Stream 实现之间进行转换。我自己也提过几个 Node.js Stream 的 PR,其实大部分情况下 Node.js 内部帮你处理了很多转换操作,自己尝试手动转换就会影响性能。另外就是 ReadableStream 使用上的一个坑了,默认他是按照 JS 值的粒度来传输的,如果我们传的值是 Buffer 或者 Uint8Array,那么实际上 highWaterMark 就是 1 了,pull 会被拆成很多次调用。这在传输 HTML 渲染结果时很容易发生。
1
2
3
4
5
6
7const readable = new ReadableStream({
pull(controller) {
controller.enqueue(chunks.shift());
if (chunks.length === 0) {
controller.close();
}
}); // Default highWaterMark is 1!正确的使用姿势是给 type 设置为 bytes,这样就不会考虑 JS 值了,完全按照 highWaterMark 的大小来传输了。
1
2
3
4
5
6
7
8const readable = new ReadableStream({
type: 'bytes',
pull(controller) {
controller.enqueue(chunks.shift());
if (chunks.length === 0) {
controller.close();
}
}, { highWaterMark: 4096 });
另外文章中还讨论了 Benchmark 的设计,这种云环境,使得用户只能在客户端发请求测试云端的性能,这不可避免的引入了网络通信耗时。Cloudflare Worker 是不允许你测量 CPU 精确耗时的,原因是防止时间侧信道攻击:这是一种安全攻击方式,攻击者通过分析程序运行所需时间的细微差异,推测出系统内部的敏感信息(例如密钥、加密算法的内部状态等),举个例子就很容易理解了。
1
2
3
4
5def check_password(input_password, real_password):
for i in range(len(real_password)):
if input_password[i] != real_password[i]:
return False
return True上面这个函数逐字符比对输入密码和正确密码,一旦发现某一位不一致就立刻返回 **
False
**。攻击者怎么利用这个?- 攻击者不知道真实密码,但可以多次尝试。
- 他测量每次服务器响应的时间:
- 当首位猜错时,服务器几乎立刻返回。
- 当首位猜对时,会比对第二位,响应稍微慢一点。
- 这样攻击者就能根据每次响应时间的微小差异推测:
- 第 1 位是什么字符;
- 接着第 2 位;
- 一直到整串密码都被推出来。
即使差异只有几微秒,只要能重复请求足够多次并统计平均差异,这个时间信息就能用来“侧面观察”内部逻辑,从而泄露敏感数据。