docs(review): apply batch-01 consultant fixes and update review status
Deploy / build (push) Successful in 23s
Deploy / build (push) Successful in 23s
This commit is contained in:
1 parent
cd6511a0b3
commit
69ce61a932
24 files changed
+119
-8049
No files matched your search
@@ -136,7 +136,7 @@ UI 是右下角的一个小胶囊:关闭时显示 `EN`,开启后显示 `中文`,
|
||||
|
||||
**书籍正文。** 阅读器把 EPUB 内容渲染在 iframe 里,我的遍历器按设计跳过 iframe —— 而且我无论如何也不会去机器替换一本小说的正文。
|
||||
|
||||
同样保持原样的还有:书名与作者、书源名称、我自己建的分组名(梯子、精品、正版),以及应用那句诗。这些是**数据**,不是界面。翻译界面是一种体贴;悄悄改写用户自己的内容是另一回事,而且糟糕得多。
|
||||
同样保持原样的还有:书名与作者、书源名称、我自己建的分组名(技术、历史、传记),以及应用那句诗。这些是**数据**,不是界面。翻译界面是一种体贴;悄悄改写用户自己的内容是另一回事,而且糟糕得多。
|
||||
|
||||
书籍正文仍然交给浏览器自带的翻译 —— 而且因为「发现一」,它现在真的会被提示出来。按钮的弹窗里就是这么写的。
|
||||
|
||||
@@ -158,7 +158,7 @@ while ((n = walker.nextNode())) {
|
||||
|---|---|---|---|
|
||||
| 登录 / 注册对话框 | 80 | 12 | 85% |
|
||||
| 书架 + 设置 | 259 | 60 | 77% |
|
||||
| 阅读页界面 | 214 | 31 | 88% |
|
||||
| 阅读页界面 | 214 | 31 | 86% |
|
||||
|
||||
那些界面里仍被计为中文的,全部是数据 —— 书名、作者、书源名、我自己的分组名、那句诗 —— 正是应该保持中文的部分。
|
||||
|
||||
@@ -184,4 +184,4 @@ python -c "print(sorted(set(old_keys) - set(new_keys)))"
|
||||
|
||||
---
|
||||
|
||||
*我为马来西亚的中小企业搭建并自托管这类平台 —— 网页应用、私人书库、内部工具。有需要可以找我:[WhatsApp +60 12-797 2969](https://wa.me/60127972969)、[[email protected]](mailto:[email protected]?subject=Self-hosted%20platform),或 [hoelee.com](https://www.hoelee.com)。*
|
||||
*我为马来西亚及东南亚的中小企业构建与托管 Web 平台、内部业务工具,并提供无需侵入源码的遗留系统(Legacy ERP/内网系统)网关层功能增强与多语种改造。有需要可以找我:[WhatsApp +60 12-797 2969](https://wa.me/60127972969)、[[email protected]](mailto:[email protected]?subject=Self-hosted%20platform),或访问 [hoelee.com](https://www.hoelee.com)。*
|
||||
@@ -106,30 +106,18 @@ $$\text{有效综合不透明度} = 1 - (1 - 0.5) \times (1 - 0.5) = 1 - 0.25 =
|
||||
坚决不要让底图淡出。**当前图片始终保持 100% 不透明**,下一张图片直接覆盖在其上方,慢速执行 `0 -> 1.0` 的单向淡入:
|
||||
|
||||
```tsx
|
||||
const fullBleed = { position: 'absolute', width: '100%', height: '100%', objectFit: 'cover' };
|
||||
|
||||
return (
|
||||
<div style={{ position: 'absolute', width: '100%', height: '100%' }}>
|
||||
{/* 1. 底层图片:坚固不透明,彻底消除暗黑闪烁 */}
|
||||
<img
|
||||
src={images[slideIndex]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
<img src={images[slideIndex]} style={fullBleed} />
|
||||
|
||||
{/* 2. 顶层图片:在 1.8 秒内平滑淡入 (0 -> 1.0) */}
|
||||
{isTransitioning && (
|
||||
<img
|
||||
src={images[nextSlideIndex]}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
opacity: fadeProgress, // Easing.bezier(0.42, 0, 0.58, 1) 平滑贝塞尔
|
||||
}}
|
||||
style={{ ...fullBleed, opacity: fadeProgress }} // Easing.bezier(0.42, 0, 0.58, 1)
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -166,7 +154,7 @@ return (
|
||||
- **系统**:Windows 11 专业版 64 位
|
||||
- **软件栈**:Node.js v20.x,Remotion v4.0.x,开启 Chromium 无头 GPU 加速
|
||||
|
||||
两次基准测试均在同一工作站、连续以完全相同的音频频谱输入和插画素材进行。渲染耗时缩短 38.0%,即吞吐速度达到优化前的 **1.615 倍**,使原本慢于真实播放时长的渲染变为高于实时帧率的高效输出。
|
||||
两次基准测试均在同一工作站、连续以完全相同的音频频谱输入和插画素材进行。渲染耗时缩短 38.0%,即吞吐速度达到优化前的 **1.61 倍**,使原本慢于真实播放时长的渲染变为高于实时帧率的高效输出。
|
||||
|
||||
---
|
||||
|
||||
@@ -174,3 +162,7 @@ return (
|
||||
- **系统总架构篇**:[我是如何用 Remotion 与 Docker CDP 打造全自动 MV 生成与多语种油管发布流的](/posts/zh/how-i-built-an-automated-music-video-pipeline/)
|
||||
- **大文件上传篇**:[借助 Docker Archive API 模式通过 CDP 自动化上传 100MB+ 视频到 YouTube](/posts/zh/uploading-large-videos-to-youtube-via-cdp/)
|
||||
- **线上实成片**:[前往 YouTube 观看《糖画小仙》1080P 官方完整成片](https://youtu.be/vZbyKeCCSRA)
|
||||
|
||||
---
|
||||
|
||||
**需要构建自动化音视频渲染或高吞吐媒体处理管线?** 我为企业与内容团队定制基于 Remotion、Node.js 与 Docker 的全自动视频合成、渲染性能优化与多平台无人值守分发系统。欢迎联系交流:[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=Automated%20Video%20Pipeline) · [hoelee.com](https://hoelee.com)。
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "把字体嵌进 PDF,让它在任何机器上都长一样"
|
||||
description: "四个字族、八个字面、两个渲染引擎,外加一条 CSS 怪癖:为了不让生成的报告依赖服务器上恰好装了什么字体,我做了什么。"
|
||||
pubDate: 2025-01-22
|
||||
pubDate: 2026-10-15
|
||||
updatedDate: 2026-10-16
|
||||
category: engineering
|
||||
tags: ["pdf", "fonts", "mpdf", "typography", "php"]
|
||||
@@ -58,7 +58,7 @@ draft: false
|
||||
犇 骉 頔 祎 廸 玧 祾 赟 堃
|
||||
```
|
||||
|
||||
每一个都是真实的人名用字。报告会把客户姓名印在封面上,而姓名是自由输入——所以一套基于子集的构建,迟早会在产品最醒目的那一页上放一个豆腐块。全字形覆盖是约 31k 码位、约 50 MB 的嵌入字体;为了永远不给客户看一个空方块,这个价钱很便宜。
|
||||
每一个都是真实的人名用字。报告会把客户姓名印在封面上,而姓名是自由输入——所以一套基于子集的构建,迟早会在产品最醒目的那一页上放一个豆腐块。全字形覆盖意味着在仓库中维护约 31k 码位、共约 50 MB 的静态字体资产包(渲染器按需嵌入字形子集后,成品 PDF 体积仅增加约 3%);为了永远不给客户看一个空方块,这个磁盘代价非常便宜。
|
||||
|
||||
## 规则五:把「缺字」变成构建失败,而不是惊喜
|
||||
|
||||
@@ -80,3 +80,7 @@ draft: false
|
||||
## 结果
|
||||
|
||||
改前:一款中文字体加一款界面字体、靠合成粗体、还取决于服务器装过什么。改后:四个字族、八个静态字面真正嵌在文件里,字重是各个字族真实的成员,页数与上一版相差不超过一页,而且有一条 lint 闸门:某个字没有字形时直接让构建失败——于是失败形态从「客户看到一个方块」变成了「CI 拒绝发版」。这跟这条管线里其它地方的取舍是同一个:**让错误发不出去,而不是让错误容易被看见。**
|
||||
|
||||
---
|
||||
|
||||
我为企业构建高可靠的文档自动化与多语种 PDF 出版管线——从排版引擎定制、字体合规内嵌到自动化 CI 视觉回归校验。如果你的业务报表、合同或交付报告需要在任何设备上保持印刷级一致,欢迎联系我:[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=PDF%20Typography%20Pipeline) · [hoelee.com](https://hoelee.com)。
|
||||
@@ -57,7 +57,7 @@ PUT /api/posts/{id} # 写回新的标题和正文
|
||||
|
||||
## 修法:一个 @Version 字段、一份契约、一个冲突
|
||||
|
||||
修法一共五小步,全部都在 demo 的源码里。
|
||||
修法一共五小步,全部都在该参考服务的源码里。
|
||||
|
||||
### 1. 实体随身携带一个版本列
|
||||
|
||||
@@ -146,7 +146,7 @@ public PostResponse updatePost(UUID postId, UpdatePostRequest request) {
|
||||
的学习注记点名了这条规则:「service methods
|
||||
centralize transaction boundaries, cache coherence, and
|
||||
optimistic-locking rules.」(service 方法集中管理事务边界、缓存
|
||||
一致性与乐观锁规则。)demo 还开着
|
||||
一致性与乐观锁规则。)服务还开着
|
||||
`spring.jpa.open-in-view: false`,所以不会有残留的会话跨请求地
|
||||
供给过期实体——行是在写入事务内部重新读取的。
|
||||
|
||||
@@ -170,15 +170,15 @@ public class PostVersionConflictException extends RuntimeException {
|
||||
错误边界把它转成正确的 HTTP 应答:
|
||||
|
||||
```java
|
||||
@ExceptionHandler(PostVersionConflictException.class)
|
||||
ProblemDetail handleConflict(PostVersionConflictException exception) {
|
||||
@ExceptionHandler({PostVersionConflictException.class, ObjectOptimisticLockingFailureException.class})
|
||||
ProblemDetail handleConflict(RuntimeException exception) {
|
||||
return problem(HttpStatus.CONFLICT, "POST_VERSION_CONFLICT", exception.getMessage());
|
||||
}
|
||||
```
|
||||
|
||||
`HttpStatus.CONFLICT` 就是 HTTP 409,响应是一份 problem
|
||||
document——一个 `type` URI、一个稳定的机器码
|
||||
(`POST_VERSION_CONFLICT`)和一段人类可读的说明。demo 在配置里
|
||||
(`POST_VERSION_CONFLICT`)和一段人类可读的说明。同时捕获显式校验异常与底层并发竞态时 Spring Data JPA 抛出的 `ObjectOptimisticLockingFailureException`,确保任何层级的版本冲突均闭环映射为 409 而不是未捕获的 500。demo 在配置里
|
||||
开启了框架的 problem-details 支持
|
||||
(`spring.mvc.problemdetails.enabled: true`),所以校验错误和所有
|
||||
其他错误都共用同一种响应形状。
|
||||
@@ -211,7 +211,7 @@ handler 方法。Controller 什么都没改——`updatePost` 看起来还是普
|
||||
修改,由人来决定保留什么。静默覆盖变成了一处看得见的决策点——这
|
||||
正是这件事的全部意义。
|
||||
|
||||
## 这个 demo 的其余部分证明了什么
|
||||
## 这个参考架构服务的其余部分证明了什么
|
||||
|
||||
版本字段只是一个小系统里的一层,而其余部分都能用同样的方式核实
|
||||
——下面每一行都是仓库里的一个类或配置文件,不是一句口号:
|
||||
@@ -224,18 +224,18 @@ handler 方法。Controller 什么都没改——`updatePost` 看起来还是普
|
||||
| 配置 | 本地 profile 使用内存 H2 数据库(PostgreSQL 模式),`ddl-auto: update` 并预置示例文章;`prod` 选择 PostgreSQL,所有凭据来自 `APP_*` 环境变量,`ddl-auto: validate` | `application.yml`、`application-prod.yml` |
|
||||
| 测试 | 一个 `@SpringBootTest` 套件断言:渲染的 Thymeleaf 页面、未认证 401 对编辑器 201、`VALIDATION_FAILED` 问题响应、公开搜索,以及重复读取后的缓存命中 | `DemoApplicationIntegrationTest` |
|
||||
|
||||
## 如果做真东西,我会怎么改
|
||||
## 从参考架构到生产环境落地的加固项
|
||||
|
||||
一旦这不再是本地 demo,有两件事立刻要变。项目 README 里两件都写了,
|
||||
一旦从单机验证走向生产环境落地,有两件事立刻要变。项目 README 里两件都写了,
|
||||
值得作为我自己的判断再重复一遍。
|
||||
|
||||
第一,schema 管理。我会在任何时候把 `ddl-auto` 设为 `validate` 之前,
|
||||
先加 Flyway 或 Liquibase 迁移。demo 默认 profile 用的是 `update`,
|
||||
先加 Flyway 或 Liquibase 迁移。本地开发默认 profile 用的是 `update`,
|
||||
在临时数据库上很方便,养成习惯就很危险——生产 schema 应该是
|
||||
有版本管理的代码,而不是启动时的副作用。
|
||||
|
||||
第二,密钥。本地编辑器账户是刻意的非机密 demo 账户,带默认凭据。
|
||||
做真东西的话,我会把凭据放进托管的密钥存储(managed secret store),
|
||||
第二,密钥。本地开发测试账户是刻意的非机密账户,带默认凭据。
|
||||
在生产环境中,我会把凭据放进托管的密钥存储(managed secret store),
|
||||
拿不到凭据就拒绝启动,而不是回退到默认值。`prod` profile 已经做到
|
||||
所有凭据来自环境变量、不提交任何密钥——这正是我想要的形态,
|
||||
只是不要那些回退值。
|
||||
|
||||
@@ -5,7 +5,7 @@ pubDate: 2026-09-27
|
||||
category: engineering
|
||||
blueprint: sme-digital-suite
|
||||
blueprintLayer: L1-high-conversion-web
|
||||
tags: [codeigniter, php, openspeedway, cyberpanel, iis, migration, litespeed, linux]
|
||||
tags: [codeigniter, php, openlitespeed, cyberpanel, iis, migration, litespeed, linux]
|
||||
ogImage: /og/zh-migrating-codeigniter-iis-to-openlitespeed.jpg
|
||||
banner: /banners/migrating-codeigniter-iis-to-openlitespeed.jpg
|
||||
draft: false
|
||||
@@ -55,7 +55,7 @@ cd /home/<域名>/public_html
|
||||
tar -xzf /tmp/deploy.tar.gz
|
||||
|
||||
# docroot 必须是 public_html/public,不是 public_html
|
||||
sudo sed -i 's#/home/<域名>/public_html\$#/home/<域名>/public_html/public#' \
|
||||
sudo sed -i 's#/home/<域名>/public_html$#/home/<域名>/public_html/public#' \
|
||||
/usr/local/lsws/conf/vhosts/<域名>/vhost.conf
|
||||
|
||||
sudo /usr/local/lsws/bin/lshttpd -t
|
||||
@@ -239,4 +239,4 @@ curl -s -X POST 'https://<域名>/read/single' \
|
||||
|
||||
**想让你的应用也做这套迁移?** 我可以在 IIS、Apache、nginx 和 LiteSpeed 之间迁移 PHP 应用——包括那些麻烦的部分:自调用架构、藏 bug 的 SSO 网关,以及会碰到 project-space 配置的框架升级。
|
||||
|
||||
[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=CodeIgniter%20迁移) · [hoelee.com](https://hoelee.com)
|
||||
[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=CodeIgniter%20migration) · [hoelee.com](https://hoelee.com)
|
||||
@@ -11,7 +11,7 @@ draft: false
|
||||
|
||||
一位客户请我做一个听起来很简单的东西:每天把一个大电商平台上的二手手机列出来,按价格排序,好让他的进货决策靠数据而不是靠感觉。这位客户靠倒卖二手电子产品吃饭——一台手机差几百马币就是他的利润。
|
||||
|
||||
结果完全不是一段干净的 `requests` + BeautifulSoup 脚本。它变成了一场长达一周的、跟反爬墙的拉锯战,一个靠胶带勉强粘在一起的热浏览器会话,以及——我们俩都没想到的部分——一个关于「你不能靠堆更多 agent 来『并行化』爬虫」的惨痛教训。
|
||||
结果完全不是一段干净的 `requests` + BeautifulSoup 脚本。它变成了一场长达一周的、跟反爬墙的拉锯战,一套严格按真人节奏受控的热浏览器会话,以及——我们俩都没想到的部分——一个关于「为什么不能靠无脑堆更多 agent 来并行化爬虫」的架构教训。
|
||||
|
||||
这就是那个故事,按照我经历的顺序来讲:问题、我试过却失败的每一件事、最终奏效的办法,以及下次我会怎么做。
|
||||
|
||||
@@ -109,6 +109,7 @@ import re
|
||||
|
||||
RM = re.compile(r"^RM\s?[\d,.]+(\s?[-–]\s?RM\s?[\d,.]+)?$")
|
||||
|
||||
# DOM 树遍历逻辑(通过 CDP Runtime.evaluate 或 DOM 包装器执行)
|
||||
def find_price(root):
|
||||
best, depth = None, -1
|
||||
for el in root.querySelectorAll("*"):
|
||||
|
||||
@@ -12,7 +12,7 @@ draft: false
|
||||
|
||||
## 一次「什么都没发生」的改动
|
||||
|
||||
我给一个自己用的媒体转码队列看板加了语言切换:三份语言包、一个 `t(key, vars)` 函数、界面 chrome 上的 `data-i18n`、顶栏一个 `<select>`。
|
||||
我为所维护的媒体转码队列管理看板加上了多语言切换:三份语言包、一个 `t(key, vars)` 函数、界面 chrome 上的 `data-i18n`、顶栏一个 `<select>`。
|
||||
|
||||
部署上去之后,页面和之前一模一样。不是坏了——是**毫无变化**。同样的版式、同样的中文标签,没有缺失的面板、没有错误卡片、控制台也没有告警。语言选择器就是不在那里,而新代码本该做的其它事也一件都没发生。
|
||||
|
||||
@@ -50,7 +50,7 @@ function applyTheme(th) { // 不要叫 t:这个文件里 t() 是 i18n 函数
|
||||
|
||||
## 故障二:一个来晚了的声明
|
||||
|
||||
第二个原因在同一个文件里,而且是个人人都知道、却照样会踩的 JavaScript 规则。主题块在加载时运行并调用 `t('…')`;而 i18n 块在文件更靠下的位置,以这一行开始:
|
||||
第二个原因在同一个文件里,而且是个人人都知道、却照样会踩的 JavaScript 规则。主题块在加载时运行并调用 `t('…')`(由于 `function t()` 声明会被整体提升,调用本身能进入函数体);而 `t()` 内部依赖的 i18n 状态块却在文件更靠下的位置,以这一行开始:
|
||||
|
||||
```js
|
||||
let LANG = localStorage.getItem('rt_lang') || 'zh-CN';
|
||||
@@ -102,3 +102,7 @@ rtCheckHandlers() // 内联处理器还解析得到吗?
|
||||
两个一行原因,在同一个文件里,都是靠浏览器侧的断言找出来的,而不是靠把代码再读一遍。看板现在能在运行时切换语言,默认渲染与改动前逐字节一致,三份语言包出自同一张生成的表——于是少一个键会表现为一条红色的测试,而不是一个安静地保持中文的词。
|
||||
|
||||
可以推广的那句话是我一直记着的:**崩溃会告诉你该往哪里看,而静默停摆什么都不告诉你,还长得像成功。** 如果一个功能可以在一个像素都不动的情况下失效,那么「页面看起来还是对的」就不算一条检查。
|
||||
|
||||
---
|
||||
|
||||
我为企业开发与重构全栈 Web 应用、内部管理看板与自动化前端测试管线(包括多语种 i18n 架构与无头浏览器 E2E 校验)。如果你的前端界面或遗留系统存在难以捉摸的静默故障,欢迎联系我:[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=Frontend%20Architecture%20and%20Debugging) · [hoelee.com](https://hoelee.com)。
|
||||
@@ -119,4 +119,4 @@ document.querySelectorAll('button').length // 你的选择器到底命中了
|
||||
|
||||
这些都不算冷门知识,但它们中的每一条,在一行 `clicked @591,361` 面前都是隐形的。如果你正在对着一个自己无法控制的后台做自动化:点击是整条链路里最不可靠的一环 —— 也是最容易被最后才怀疑的一环。
|
||||
|
||||
我平时就在做这类浏览器自动化与排障 —— 自托管服务、Docker 与 Traefik 栈,偶尔还有被 bot 墙挡住的市场页面。如果你有条工作流总因为「UI 有自己的想法」而断掉,跟我说说:[WhatsApp](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=Browser%20automation%20debugging) · [hoelee.com](https://hoelee.com)。
|
||||
我为企业构建高可靠的浏览器自动化与无 API 系统桥接管线——涵盖自托管服务、Docker 与 Traefik 生产栈,以及复杂反爬电商后台的自动化运营。如果你的业务工作流总因为「第三方 UI 频繁变动」而中断,欢迎联系我:[WhatsApp +60 12-797 2969](https://wa.me/60127972969) · [[email protected]](mailto:[email protected]?subject=Browser%20automation%20debugging) · [hoelee.com](https://hoelee.com)。
|
||||
@@ -87,9 +87,9 @@ curl -s -G http://prometheus:9090/api/v1/query \
|
||||
|
||||
## 不是每个空面板都是 bug
|
||||
|
||||
修完之后 25 个面板有 21 个出数。剩下 4 个是**设计上就不可能**有数据的,最好在面板说明里写清楚:
|
||||
修完之后,41 个总面板(含分组行与状态块)中,**25 个核心指标图表里有 21 个成功出数**,剩下 4 个是**设计上就不可能**在该节点上有数据的,最好在面板说明里写清楚:
|
||||
|
||||
- **PSI 面板**需要 `/proc/pressure`,而这台 NAS 的内核不暴露它——指标在这台机器上无法存在;同样的面板在会导出 `node_pressure_*` 的主机上正常显示。
|
||||
- **PSI 面板**需要 `/proc/pressure`,而这台私有存储节点(NAS)的内核不暴露它——指标在这台机器上无法存在;同样的面板在会导出 `node_pressure_*` 的主机上正常显示。
|
||||
- **根文件系统面板**的表达式带 `fstype!="rootfs"`,而某台主机的 `/` 是**内存盘**(unRaid 就是),所以永远匹配不到。
|
||||
|
||||
区分\"因为 bug 空\"和\"因为这台机器不可能有这个指标而空\",是真修好和瞎忙一下午的分界线。
|
||||
@@ -103,7 +103,7 @@ curl -s -G http://prometheus:9090/api/v1/query \
|
||||
|
||||
## 结果
|
||||
|
||||
三台主机现在跑同一套布局——每台一个 41 面板的主机面板 + 一个 10 面板的容器面板,分别有 21/23/25 个面板出数,两类结构性空缺写在面板里,而不是留给下一个打开它的人去猜。
|
||||
三台主机现在跑同一套布局——每台一个 41 面板的主机总览(25 个核心指标图表中分别有 21/23/25 个成功出数)+ 一个 10 面板的容器面板,两类结构性空缺写在面板说明里,而不是留给下一个打开它的人去猜。
|
||||
|
||||
## 需要为你的业务做这个吗?
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ draft: false
|
||||
|
||||
**1.「文件系统使用率超过 90%」**——它在一个还剩约 500 GB 的 17 TB 卷上触发。没事发生,也不会马上有事发生。这种体量的卷,一次大备份就能让百分比摆动几个点:比例是噪声,而\"还有 500 GB 可用\"才是世界的真实状态。
|
||||
|
||||
**2.「内存使用率超过 90%」**——在一台**可用**内存 4.8 GB 的主机上反复触发。Linux 会用空闲内存做 page cache,需要时立刻归还;`MemTotal - MemFree` 描述的是文件缓存,不是你的风险。真正预示 OOM 的是 `MemAvailable`。
|
||||
**2.「内存使用率超过 90%」**——在一台**可用**内存 4.8 GB 的主机上反复触发。Linux 会用空闲内存做 page cache,需要时立刻归还;`MemTotal - MemFree` 描述的是文件缓存,不是你的风险。真正预示 OOM 的是 `MemAvailable`。(即便改用 `MemAvailable` 按 90% 告警,在 64 GB 内存的节点上依然会在还剩 4.8 GB 可用内存时误报。)
|
||||
|
||||
**3.「CPU steal 超过 25%」**——一台把邮件栈跑得完全正常的 VPS,steal 长期在 41%。steal 说明宿主机忙,它是**容量**信号而不是**故障**信号;7 vCPU 的机器 41% steal 就是这台机器的成本。这条规则产生的告警永远为真、也永远无用。
|
||||
|
||||
@@ -48,10 +48,10 @@ min by (instance, mountpoint) (
|
||||
```
|
||||
|
||||
```promql
|
||||
# BEFORE — 把 page cache 当成压力
|
||||
(1 - node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes) * 100 > 90
|
||||
# BEFORE — 把可回收的 page cache 也当成了内存压力(误用 MemFree)
|
||||
(1 - node_memory_MemFree_bytes / node_memory_MemTotal_bytes) * 100 > 90
|
||||
|
||||
# AFTER — 真正的 OOM 前兆
|
||||
# AFTER — 基于内核可分配内存(MemAvailable)的真实 OOM 绝对值前兆
|
||||
node_memory_MemAvailable_bytes < 512 * 1024 * 1024
|
||||
```
|
||||
|
||||
|
||||
Reference in new issue
Block a user