首页
关于本站
私人 DNS 加密设置教程
全部文章
推荐
加密 DNS 服务
DNS 实时战报
过滤规则订阅
搜索
网络与 DNS
加密 DNS 实操
路由器与家庭网络
自建服务
AdGuard Home
服务器运维
开发与工具
安全与隐私
登录
搜索
23-4.CN
累计撰写
59
篇文章
累计收到
3
条评论
首页
分类
网络与 DNS
加密 DNS 实操
路由器与家庭网络
自建服务
AdGuard Home
服务器运维
开发与工具
安全与隐私
页面
关于本站
私人 DNS 加密设置教程
全部文章
推荐
加密 DNS 服务
DNS 实时战报
过滤规则订阅
注册丨登录
搜索到
5
篇
标签为
性能
的文章
2026-10-24
进程被 OOM Killer 杀掉之后:dmesg 怎么读、怎么防止再发生
这篇是服务器运维笔记,和 DNS 无关。站内的加密 DNS 服务在dns.23-4.cn。进程突然消失,日志里没有任何异常,systemctl status 显示 killed 或者干脆 inactive (dead)——这时候最容易被误导的方向是「程序有 bug 崩了」。实际上很可能是内核把它杀了。我在一台 3.8 GB 内存的小机器上排查过好几次。最近一次的真实记录:[529021.651374] Out of memory: Killed process 1461869 (python3) total-vm:2542300kB, anon-rss:2529504kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:5000kB oom_score_adj:0一个 python3 进程吃到了 2.5 GB anon-rss,然后被内核杀掉。这台机器总共 3835 MB。这篇讲三件事:怎么确认是 OOM、那行日志每个字段什么意思、以及怎么让它别再发生。一、先确认到底是不是 OOM第一件事永远是查内核日志,不是查应用日志。dmesg | grep -i "killed process"实测这台机器的输出(最近 3 条):[529021.651374] Out of memory: Killed process 1461869 (python3) total-vm:2542300kB, anon-rss:2529504kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:5000kB oom_score_adj:0 [529099.143488] Out of memory: Killed process 1462964 (python3) total-vm:2285420kB, anon-rss:2272596kB, file-rss:0kB, UID:0 pgtables:4504kB oom_score_adj:0 [960123.301888] Out of memory: Killed process 281498 (python3) total-vm:2296232kB, anon-rss:2132456kB, file-rss:4kB, shmem-rss:0kB, UID:0 pgtables:4232kB oom_score_adj:0数一下次数:dmesg | grep -ci "out of memory"4四次。 全是 python3,全是 2.1~2.5 GB。踩坑点:dmesg 在某些发行版上普通用户读不到,会输出空。这时要么 sudo dmesg,要么读持久化日志:# systemd 系统 journalctl -k | grep -i "killed process" # 或按时间窗口 journalctl -k --since "1 day ago" | grep -i "out of memory"dmesg 是环形缓冲区,重启就没了。如果机器刚重启过,dmesg 里查不到,但 journalctl -k 可能还在(取决于 journald 的持久化配置)。二、那行日志每个字段什么意思以这条为例:Out of memory: Killed process 1461869 (python3) total-vm:2542300kB, anon-rss:2529504kB, file-rss:0kB, shmem-rss:0kB, UID:0 pgtables:5000kB oom_score_adj:0字段含义怎么用1461869PID已经不存在了,只能用来对日志时间(python3)进程名先确认是不是你的业务进程total-vm虚拟地址空间总量通常没用,虚拟内存可以远超物理内存anon-rss匿名页常驻内存这才是真实占用,看这个file-rss文件映射的常驻页一般很小,代码段和 mmap 的文件shmem-rss共享内存常驻页用了 shm/大页的会很大pgtables页表本身占的内存内存碎片化严重时会异常大oom_score_adj优先级调整值-1000 到 1000,越大越容易被杀关键判断:anon-rss 接近 total-vm,说明是真实的内存分配(堆上的数据),不是 mmap 映射出来的虚拟空间。上面三条都是这样(2.5 GB vs 2.54 GB),说明程序真的把 2.5 GB 数据读进内存了。如果反过来,total-vm 很大但 anon-rss 很小,那是正常的——很多运行时(JVM、Go、Python)会预留大量虚拟地址空间。pgtables:5000kB 也值得注意。5 MB 页表意味着进程有大量分散的内存页,通常是内存碎片。这种进程往往不是「用得多」,而是「用得散」。三、内核是依据什么挑中它的oom_score 决定了谁被杀。每个进程都有:for f in /proc/self/oom_score /proc/self/oom_score_adj /proc/self/oom_adj; do printf "%-28s " $f; cat $f done实测:/proc/self/oom_score 666 /proc/self/oom_score_adj 0 /proc/self/oom_adj 0oom_score(0~1000)是内核算出来的,主要由内存占用比例决定;oom_score_adj(-1000~1000)是你可以调的偏移量;oom_adj 是老的、已废弃的接口,别用。找当前最容易被杀的进程:for p in /proc/[0-9]*; do [ -r "$p/oom_score" ] || continue printf "%5s %8s %s\n" "$(cat $p/oom_score 2>/dev/null)" "$(cat $p/oom_score_adj 2>/dev/null)" "$(cat $p/comm 2>/dev/null)" done | sort -rn | head -10这个排序就是内核的候选名单顺序。排第一的基本就是下次会被杀的。四、让它别再发生:四种手段,按性价比排手段 1:给关键进程设 oom_score_adj = -1000(最省事)# 临时生效 echo -1000 > /proc/$(pidof sshd)/oom_score_adj # systemd 服务里永久生效 systemctl edit myservice在打开的编辑器里写:[Service] OOMScoreAdjust=-1000注意:-1000 表示「永不杀」。⛔ 别给业务进程设 -1000——那只是把被杀的对象换成了别人(可能是 sshd 或数据库)。这个值应该留给 sshd、systemd 本身这类「死了就登不上机器」的进程。手段 2:用 cgroup 给服务设内存上限(最推荐)systemd 245 支持 MemoryMax,超了只杀这个 cgroup 里的进程,不影响整机。先看当前值:systemctl show -p MemoryMax -p MemoryHigh -p OOMPolicy nginx实测(默认全是无限):OOMPolicy=stop MemoryHigh=infinity MemoryMax=infinity设置上限:systemctl edit myservice[Service] MemoryMax=512M MemoryHigh=400M OOMPolicy=stopMemoryHigh 是软限制:超过后内核会强力回收(让进程变慢),但不杀;MemoryMax 是硬限制:超过直接杀,且只杀这个 cgroup 内的;OOMPolicy=stop 表示 cgroup 内 OOM 时把整个服务停掉(而不是只杀一个进程,留下半死不活的状态)。这是最该用的手段,因为它把「整机 OOM」变成了「单个服务重启」,故障范围小了一个数量级。手段 3:加 swap(有争议,但对小机器有用)free -m实测这台机器: total used free shared buff/cache available Mem: 3835 1283 260 9 2291 2277 Swap: 0 0 0Swap 是 0。 没有 swap 意味着内存一旦用满,内核只有一条路:杀进程。加一个 2 GB 的 swap 文件:fallocate -l 2G /swapfile chmod 600 /swapfile mkswap /swapfile swapon /swapfile echo '/swapfile none swap sw 0 0' >> /etc/fstab验证:swapon --show free -m踩坑点:swap 不是万能药。如果进程是持续增长的(内存泄漏),加 swap 只是把「立刻被杀」变成「慢慢变卡,最后连 swap 也用满还是被杀」。swap 的正确用途是吸收突发峰值,不是掩盖泄漏。另外 vm.swappiness 默认 60,对小内存机器建议调到 10~20,让内核尽量不把热数据换出去:sysctl -w vm.swappiness=10手段 4:找根因(唯一真正的解法)上面三条都是「控制损失」,不解决「为什么它要吃 2.5 GB」。Python 进程吃到 2.5 GB,最常见的四个原因:原因特征怎么查一次性读入大文件anon-rss 突然涨到文件大小量级查代码里有没有 read() / readlines()无界缓存/字典缓慢单调增长,永不回落加 len(cache) 日志生成器误用成 listlist(gen) 把整个结果集物化搜 list( 包生成器的地方子进程/线程泄漏进程数或线程数持续涨`ls /proc/PID/task \wc -l`看进程内存的时间序列(不用装任何工具):PID=$(pidof myservice) while true; do echo "$(date +%H:%M:%S) $(awk '/VmRSS/{print $2, $3}' /proc/$PID/status)" sleep 5 doneVmRSS 单调上涨且不回落 = 泄漏。锯齿形(涨到某个值又掉下来)= 正常的批量处理峰值。五、怎么验证做对了改完配置,做一次受控压测,别等生产环境再出问题。# 1. 确认上限已生效 systemctl show -p MemoryMax -p MemoryHigh -p OOMPolicy myservice # 2. 确认 swap 已启用 swapon --show free -m # 3. 观察 PSI(内存压力),这是比 free 更准的指标 cat /proc/pressure/memory实测这台机器的 PSI:some avg10=0.00 avg60=0.00 avg300=0.00 total=138645986 full avg10=0.00 avg60=0.00 avg300=0.00 total=59592289some = 至少有一个任务因为等内存而卡住的时间占比;full = 所有任务都卡住的时间占比。full 长期大于 0 就说明内存真的不够了。这是比 free 好用得多的指标:free 里的 available 再大,只要 PSI 的 full 在涨,就说明实际有压力;反过来 free 看着很少但 buff/cache 可回收、PSI 为 0,那就是健康的。第 4 步,故意触发一次,确认隔离有效:# 在一个 cgroup 里跑一个吃内存的程序,确认只有它被杀 systemd-run --scope -p MemoryMax=100M python3 -c "a=bytearray(200*1024*1024)"实测输出:Running scope as unit: run-r258b23d3d64041bc9f1ce1255cf99392.scope Killed一个 200 MB 的分配,被 100 MB 的硬上限立刻截断,只杀这一个 scope——机器上其它服务完全不受影响。这就算验证通过了。对比一下不设上限时会发生什么:同样的分配会真的吃满 200 MB,如果整机剩余内存不够,内核就要在全局挑一个进程杀——可能就是你的数据库或 sshd。另外看一眼这台机器当前的候选名单(oom_score 降序前 5):for p in /proc/[0-9]*; do [ -r "$p/oom_score" ] || continue printf "%5s %8s %s\n" "$(cat $p/oom_score 2>/dev/null)" "$(cat $p/oom_score_adj 2>/dev/null)" "$(cat $p/comm 2>/dev/null)" done | sort -rn | head -5实测: 734 0 python3 708 0 AdGuardHome 706 0 python3 671 0 php-fpm 671 0 php-fpm排第一的 python3 就是下一次最可能被杀的。这份名单比任何监控面板都直接——它告诉你「如果再 OOM 一次,谁先死」。六、完整流程回顾# 1. 确认是不是 OOM(不是查应用日志) dmesg | grep -i "killed process" journalctl -k --since "1 day ago" | grep -i "out of memory" # 2. 读那行日志:重点看 anon-rss,不是 total-vm # anon-rss ≈ total-vm ⇒ 真实内存分配,不是虚拟空间 # 3. 看谁最容易被杀 for p in /proc/[0-9]*; do printf "%5s %s\n" "$(cat $p/oom_score 2>/dev/null)" "$(cat $p/comm 2>/dev/null)"; done | sort -rn | head # 4. 保护关键进程(只给 sshd 这种) echo -1000 > /proc/$(pidof sshd)/oom_score_adj # 5. 给业务服务设硬上限(最推荐) systemctl edit myservice # MemoryMax=512M / OOMPolicy=stop # 6. 有 swap 吗?没有就加,但别拿它掩盖泄漏 free -m; swapon --show # 7. 用 PSI 判断真实压力 cat /proc/pressure/memory # 8. 找根因:VmRSS 是否单调上涨 awk '/VmRSS/{print $2, $3}' /proc/$(pidof myservice)/statusOOM 不是一个「需要修 bug」的故障,它是一个容量与隔离问题。绝大多数「进程莫名消失」都能靠第 5 步(cgroup 硬上限)从「整机雪崩」降级成「单服务重启」——这是投入产出比最高的一步。相关阅读服务器磁盘满了怎么查:df / du / inode / 日志轮转的可执行排查顺序谁在占着这个端口:ss / lsof / /proc 三条路各自的盲区nginx error.log 里那 5 类 warn 到底要不要管SQLite 博客的备份与恢复:为什么不能 cp,该用 .backup 还是 VACUUM INTO
2026年10月24日
1 阅读
0 评论
0 点赞
2026-10-04
不装 Puppeteer:用 Node 内置 WebSocket 直接驱动 Chrome DevTools 协议
这篇是开发工具笔记,和 DNS 无关。站内的加密 DNS 服务在dns.23-4.cn。要做浏览器自动化,第一反应通常是装 Puppeteer 或 Playwright。但如果你的需求只是「打开页面、等它渲染完、取几个值、点几下」,那 Node 24 已经内置了需要的一切——全局 WebSocket 和全局 fetch,加起来不到 120 行就能驱动 Chrome DevTools 协议(CDP)。这篇是我自己写回归脚本时踩出来的完整做法,含几个会让人白白调试半小时的坑。一、先启动一个带调试端口的 Chromechrome --headless=new --remote-debugging-port=9357 \ --user-data-dir=/tmp/cdp-profile \ --no-first-run --disable-gpu \ --window-size=1440,900 about:blank关键点:--remote-debugging-port 是唯一入口,之后所有操作都通过 CDP 走 WebSocket。--user-data-dir 单独给一个目录,避免污染你日常的浏览器配置。二、开一个新标签页,拿到它的 WebSocket 地址const CDP_PORT = 9357; async function newTab() { // 注意:必须是 PUT,GET 在较新的 Chrome 上已不再创建标签页 const r = await fetch(`http://127.0.0.1:${CDP_PORT}/json/new?about:blank`, { method: 'PUT', }); return r.json(); // { id, webSocketDebuggerUrl, ... } }三、一个最小可用的 CDP 会话封装CDP 是「发一条带 id 的消息,等一条同 id 的回复」,另外还会不停推事件。把它包成 Promise 就够了:async function attach(wsUrl) { const ws = new WebSocket(wsUrl); await new Promise((res, rej) => { ws.onopen = res; ws.onerror = rej; }); let id = 0; const pending = new Map(); const events = []; ws.onmessage = (ev) => { const m = JSON.parse(ev.data); if (m.id && pending.has(m.id)) { // 这是某条命令的回复 const { res, rej } = pending.get(m.id); pending.delete(m.id); m.error ? rej(new Error(JSON.stringify(m.error))) : res(m.result); } else if (m.method) { // 这是事件推送 events.push(m); } }; const send = (method, params = {}) => new Promise((res, rej) => { const i = ++id; pending.set(i, { res, rej }); ws.send(JSON.stringify({ id: i, method, params })); setTimeout(() => { // 一定要有超时,否则一条丢失的回复会永久挂住 if (pending.has(i)) { pending.delete(i); rej(new Error('timeout ' + method)); } }, 40000); }); const evaluate = async (expression) => { const r = await send('Runtime.evaluate', { expression, returnByValue: true, awaitPromise: true, }); if (r.exceptionDetails) { throw new Error('eval: ' + (r.exceptionDetails.exception?.description || r.exceptionDetails.text)); } return r.result.value; // returnByValue 时结果在这里 }; return { send, evaluate, events, close: () => ws.close() }; }四、打开页面并「确认它真的渲染了」const s = await attach((await newTab()).webSocketDebuggerUrl); await s.send('Page.enable'); await s.send('Runtime.enable'); await s.send('Log.enable'); // 收集 console 错误 await s.send('Emulation.setDeviceMetricsOverride', { width: 390, height: 844, deviceScaleFactor: 1, mobile: true, }); await s.send('Page.navigate', { url: 'https://example.com' }); // 等 load for (let i = 0; i < 120; i++) { await new Promise(r => setTimeout(r, 250)); if (await s.evaluate('document.readyState').catch(() => null) === 'complete') break; }到这里就有一个必须做的动作:// 先断言页面确实渲染了,再断言别的东西 const rendered = await s.evaluate(`({ title: document.title, bodyLen: document.body ? document.body.innerHTML.length : 0, items: document.querySelectorAll('.some-selector').length, })`); if (!rendered.bodyLen) throw new Error('页面根本没渲染,后面的断言全是假的');为什么必须先做这一步:如果导航其实没成功(比如 URL 写错了),document 还是 about:blank。此时 document.querySelectorAll(...).length 会老老实实返回 0,而你如果断言的是「不应该有某个元素」,它会通过——测试全绿,功能全坏。这种「空断言」是最难发现的假阳性。五、五个会浪费你半小时的坑1. requestAnimationFrame 在无头模式下不会按你预期触发。没有合成器时,rAF 可能永远不触发,或者只触发一两次。用 setTimeout 代替所有等待:const sleep = (ms) => new Promise(r => setTimeout(r, ms)); await sleep(2500); // 让 JS 跑完2. 不要把你的变量命名成 URL。URL 是 Node 的全局 WHATWG URL 类。写 const URL = 'https://...' 之后,某些库内部 new URL(...) 会抛错或被静默吞掉,导航「看起来成功了」但实际去了 about:blank——于是所有断言真空通过。这是个非常隐蔽的坑,换个名字就好。3. 每个视口开一个新标签页。Emulation.setDeviceMetricsOverride 改尺寸之后,页面里的媒体查询、懒加载、以及已经算好的布局不会自动全部重算。做「桌面 / 移动」两套验证时,开新标签页重新加载,别在同一页上反复改尺寸。4. 用 process.exitCode,不要 process.exit()。process.exit() 会立刻切断进程,WebSocket 里的响应还没读完,输出可能被截断。设置 process.exitCode = 1 然后自然退出。5. 收集 console 错误。Runtime.exceptionThrown 给的是未捕获异常,Log.entryAdded 给的是 console 输出。两个都开,才能看到「页面加载时的报错」:const errors = s.events .filter(e => e.method === 'Runtime.exceptionThrown' || (e.method === 'Log.entryAdded' && e.params.entry.level === 'error')) .map(e => e.method === 'Runtime.exceptionThrown' ? 'EXC: ' + (e.params.exceptionDetails?.exception?.description || '') : 'LOG: ' + e.params.entry.text);六、和 Puppeteer 比,值得吗 原生 CDPPuppeteer / Playwright依赖0,Node 内置需要下载浏览器 + 一堆依赖体积一个脚本文件几百 MB上手成本需要理解协议API 友好能力上限协议本身,最全封装了常用部分,偶尔要 evaluate 绕回 CDP适合场景固定环境的回归脚本、CI 里的定点验证复杂交互、爬虫、端到端测试如果你只是要跑几个固定页面的定点验证(比如「这个链接是不是 200」「这个元素在移动端有没有被遮挡」),原生 CDP 更省事,而且不会因为浏览器版本升级而整体失效——你依赖的是协议,不是封装层。相关阅读在 Alpine Linux 上从零搭一个 Typecho:nginx + php-fpm + SQLite 的完整清单服务器磁盘满了怎么查:df / du / inode / 日志轮转的可执行排查顺序
2026年10月04日
26 阅读
0 评论
0 点赞
2026-10-04
GitHub 和 Cloudflare 有没有被加速?说清「DNS 加速」能做什么、不能做什么
加密 DNS 主机名:dns.23-4.cn | DoH 地址:https://dns.23-4.cn/dns-query | 配置教程 | 实时战报「换了你们的 DNS,GitHub 是不是就变快了?」「Cloudflare 有没有被加速?」这两个问题背后是同一个误解:以为 DNS 能加速网络。DNS 只负责把域名翻译成 IP,它决定你连到哪台机器,不决定你连过去有多快。但这不代表换 DNS 对 GitHub 没用。下面把「能做什么」和「不能做什么」分开说。一、DNS 唯一能改变的两件事换一个解析器,物理上只可能改变两件事:返回的 IP 不一样了。 同一个域名,不同解析器可能返回不同的 IP(多机房、CDN 调度、或其中一方被污染)。返回的 IP 是对的还是错的。 明文 DNS 在链路上可以被篡改,加密 DNS 不能。速度只可能通过第 1 条间接变化:如果新返回的 IP 恰好是离你更近的节点,那确实会更快。但这是「换了一个更好的节点」,不是「DNS 加了速度」。而且如果原来的解析器已经返回了最优节点,换谁都不会变快。二、GitHub 的实际情况GitHub 相关的域名不是一个,而是一串,各自的情况完全不同:域名用途换 DNS 有没有用github.com主站页面、Git 操作一般没明显变化,瓶颈在跨境线路api.github.comAPI、CI、部分客户端同上codeload.github.com下载仓库压缩包同上raw.githubusercontent.com原始文件、脚本、图片可能有明显变化——这个域名经常被返回错误/不可达的 IP,换一个能返回正确 IP 的解析器,从「完全打不开」变成「能打开」objects.githubusercontent.comRelease 附件下载有可能变化,同样是「修好 vs 打不开」*.githubassets.com页面静态资源有可能变化所以准确的说法是:换 DNS 不会让 GitHub 变快。 跨境带宽和路由质量不是 DNS 能改的。换 DNS 可能让 GitHub 从「打不开」变成「能打开」。 这是修错,不是加速。典型症状是页面样式全丢、Release 下载 404、raw 链接超时——这些多半是解析结果错了。想要真的「快」,需要的是代理 / CDN / 中继,不是 DNS。顺便说一句:如果你看到某个「DNS 加速」服务声称能把 GitHub 下载速度提到几 MB/s,它多半在做 HTTP 代理或反向代理,而不是在做 DNS。这两件事经常被打包成一个产品卖,但技术上是两回事。三、Cloudflare 的实际情况Cloudflare 的情况和 GitHub 相反:Cloudflare 大量使用任播(anycast)。同一个 IP 在全球很多机房都有,用户连上去之后由 BGP 路由决定落到哪个机房;这意味着 DNS 返回哪个 Cloudflare IP 其实不太重要——反正是同一批任播地址,路由会把你带到最近的可用机房;所以「换 DNS 让 Cloudflare 变快」基本是无效的。真正影响速度的是你的运营商到 Cloudflare 机房的互联质量,这跟 DNS 无关;1.1.1.1 这个地址本身是个特例:它在部分网络下确实比运营商 DNS 响应更快,但那是「解析器本身快」,不是「被加速」。一个实测办法,把「解析」和「连接」分开量:# 1. 解析耗时:只测 DNS,不测连接 dig github.com | grep -i "Query time" # 2. 连接耗时:解析到一个固定 IP 之后,测 TCP/TLS 握手 curl -o /dev/null -s -w "dns=%{time_namelookup} connect=%{time_connect} tls=%{time_appconnect} total=%{time_total}\n" https://github.com # 3. 下载速度:和 DNS 完全无关 curl -o /dev/null -s -w "speed=%{speed_download} bytes/s\n" https://github.com/看 time_namelookup(DNS)和 time_connect(连接)的比例。如果 DNS 只占几十毫秒、而 connect 要几百毫秒,那就说明瓶颈在线路,换 DNS 没有任何意义。四、那这台站的服务对 GitHub 到底有没有帮助诚实回答:✅ 可能修好「打不开」:如果运营商 DNS 对某些 GitHub 子域返回了错误结果,加密 DNS 能给出正确结果;✅ 顺带拦截部分广告和跟踪器,让页面加载少几个请求;❌ 不会提高下载速度:本站不做任何 HTTP 代理、中继或 CDN 回源,它只解析域名;❌ 不改变你的出口 IP:所有流量还是从你自己的线路出去。如果你的诉求是「GitHub 下载要快」,DNS 不是答案,你需要的是代理或镜像。如果你的诉求是「GitHub 有些页面打不开」,那换一个能给出正确解析的 DNS 值得一试。五、一张表说清边界你想要的效果DNS 能做到吗该用什么修好被污染 / 被劫持的解析✅ 能加密 DNS在多个 CDN 节点里选一个更近的⚠️ 间接,且不保证就近的解析器提高跨境下载速度❌ 不能代理 / 中继 / CDN隐藏你访问了哪些站点❌ 不能VPN / Tor拦掉 DNS 层的广告域名✅ 能带过滤的 DNS改变游戏对局延迟❌ 不能优化线路相关阅读一个免费的加密 DNS(dns.23-4.cn):全平台去广告与隐私保护私人 DNS 是什么、适合谁:一份不带玄学的判断清单公共DNS服务商对比:Cloudflare/Google/阿里/腾讯/114 哪个更适合你?私人DNS速度怎么样?DNS延迟测速方法与工具推荐
2026年10月04日
23 阅读
0 评论
0 点赞
2026-10-04
开加密 DNS 会拖慢游戏吗?把「开局解析一次」和「全程连接」分清楚
加密 DNS 主机名:dns.23-4.cn | DoH 地址:https://dns.23-4.cn/dns-query | 配置教程 | 实时战报「换了 DNS 会不会打游戏卡?」这是被问得最多的一个问题。答案是不会,但原因值得说清楚——因为大部分人对 DNS 在游戏里扮演什么角色,理解是反的。一、游戏连的是 IP,不是域名一局游戏里,客户端真正在做的事是:和某个服务器 IP(通常是 UDP)保持一条长连接,来回发小包。这条连接建立之后,游戏过程中根本不再查 DNS。DNS 只在下面这几个时刻被用到:时刻查几次影响启动游戏、登录几次到几十次影响「点开游戏到进大厅」的等待时间检查 / 下载更新几次(后面走 CDN 的 IP)影响下载速度,间接和解析到的节点有关对局匹配通常 0 次匹配用的是已经建立的连接所以对局内的延迟(ping)和 DNS 没有关系。对局延迟取决于你到那个服务器 IP 之间的路由、运营商互联质量、以及你本地网络。DNS 在开局之前就已经把 IP 交出去了,之后它不在链路上。二、加密 DNS 多出来的开销有多大DoT / DoH 相比明文 DNS,多出来的是建连时的一次 TLS 握手(1~2 个 RTT)。关键在于:这条加密连接是长连接,会被复用。第二次查同一个域名,通常直接命中本地缓存,连连接都不用碰;真正需要重新握手的场景是「首次查询」和「连接空闲超时之后」;一次 DNS 查询的耗时在几毫秒到几十毫秒之间,而游戏启动本身要加载几百 MB 资源。把这两个数量级放一起看:握手多花的那几十毫秒,在「启动游戏」这个几十秒的过程里可以忽略。反过来说,如果原来的运营商 DNS 又慢又经常超时,换成就近的加密 DNS,启动反而更快。这不是加密带来的,是换了一个响应更快的解析器带来的。三、真正可能被 DNS 影响的情况有两个,都不是「DNS 加延迟」,而是「DNS 给错了答案」:1. 解析到了远处的 CDN 节点。下载游戏更新时,如果解析器把 cdn.xxx.com 解到了很远的节点,下载速度会明显变差。这是 CDN 调度问题,不是加密问题——用明文 DNS 同样会发生。对局本身不受影响,因为对局连的是固定服务器 IP。2. 解析被劫持到广告页或错误 IP。这才是加密 DNS 要解决的问题。明文 DNS 在链路上可以被篡改,返回一个假的 IP;加密之后,中间人改不了。表现上就是「某些游戏启动器里的公告页、活动页打不开或者跳到奇怪页面」这类问题会消失。四、怎么自己验证想确认「开加密 DNS 前后 ping 没变化」,做一个简单对照:# 1. 找出游戏实际连的服务器 IP(对局进行中执行) # Windows netstat -ano | findstr UDP | findstr ESTABLISHED # Linux / macOS ss -unp | head -30 # 2. 记录这个 IP,玩一局,看延迟 # 3. 切换 DNS(关掉私人 DNS / 换回自动),再玩一局 # 4. 对比同一个服务器 IP 的延迟你会发现连的还是同一个 IP,延迟也基本一致。如果延迟确实变了,那说明解析到的服务器 IP 变了(游戏按 DNS 结果选服),这时候换回原来的解析结果就行——但这是选服问题,不是 DNS 开销问题。五、结论担心的事实际情况对局延迟变高不会。对局连的是已解析的 IP,DNS 不在链路上启动变慢首次握手多几十毫秒,相对启动耗时可以忽略;解析器更快时反而更快下载更新变慢有可能,但原因是解析到的 CDN 节点变了,和加密无关游戏连不上极少见。如果发生,多半是某些 ROM 的私人 DNS 配置无效导致整体解析失败,切回「自动」即可一句话:DNS 是开局前问一次路,不是全程导航。 它决定你连到哪台服务器,不决定你连过去有多快。相关阅读一个免费的加密 DNS(dns.23-4.cn):全平台去广告与隐私保护加密 DNS 到底费不费电?按每天的查询量算一笔账私人DNS速度怎么样?DNS延迟测速方法与工具推荐私人 DNS 是什么、适合谁:一份不带玄学的判断清单
2026年10月04日
18 阅读
0 评论
0 点赞
2026-10-03
加密 DNS 到底费不费电?按每天的查询量算一笔账
加密 DNS 主机名:dns.23-4.cn | DoH 地址:https://dns.23-4.cn/dns-query | 配置教程 | 实时战报「开了加密 DNS 会不会更费电?」这个问题通常来自一个合理的直觉:多了一层加密,多了一条常驻连接,听起来就像多了一个后台服务。实际不是。把账按每天的真实查询量算一遍就清楚了。一、先看一次查询有多大项目明文 DNSDoT / DoH单次查询报文约 40~100 字节约 100~300 字节(含 TLS 记录头)建立连接无一次 TLS 握手,约 2~4 KB连接复用不适用是,后续查询共用同一条连接即使按最坏情况算——假设你一天查 2000 次域名、每次都不命中缓存、每次都重新握手(现实中不可能)——总量也就是:2000 × 4 KB ≈ 8 MB / 天而刷 10 分钟短视频的流量是几十到几百 MB。DNS 的流量在整机的流量账本里小到看不见。二、耗电的真正大头不是字节数,是「唤醒射频」手机耗电的关键不是传输了多少字节,而是射频被唤醒了多少次、醒了多久。这一点上,加密 DNS 反而有个优势:它是系统组件,不是一个 App。安卓的私人 DNS 实现在系统的解析器里(netd 一侧),没有额外的常驻进程、没有额外的唤醒锁、没有额外的通知;它复用系统已有的网络栈和长连接机制,不会因为「多了一个 DNS 应用」而多一份后台驻留;对比一下:装一个第三方「DNS 加速」App,那个 App 才是真正费电的东西——它有进程、有前台服务、有定时任务。换句话说:系统级 DoT ≈ 0 额外后台成本;第三方 DNS App = 一个真实的后台进程。 这两者的耗电差异,比「明文 vs 加密」大一个数量级。三、什么情况下加密 DNS 会明显更费电有三种,都和加密本身无关,而是配置问题:1. 服务器离你太远,或者连接老断。每次重连都要重新握手、重新唤醒射频。如果解析器在地球另一边,每次查询都要跑一趟国际线路,射频醒着的时间会明显变长。选一个就近的解析器是唯一有效的省电建议。2. 开了「自动」模式,在多个服务器之间反复切换。安卓的「自动」会尝试用运营商提供的 DoT 服务器,失败再回退。这个反复尝试的过程会产生额外的连接建立。如果已经明确要用某个解析器,就直接填主机名,别用「自动」。3. 私人 DNS 配置无效,导致解析一直失败重试。这是最费电的一种:每一次域名解析都要等超时,射频被反复唤醒。表现通常是「手机发热、掉电快、部分 App 打不开」。判断方法很简单——看私人 DNS 那一栏是不是「无法连接」,是的话就是这个问题。四、怎么自己量想验证「开/关加密 DNS 对续航有没有影响」,别凭感觉,用系统自带的电量统计:安卓:设置 → 电池 → 电池用量 → 按「系统」/「网络」分类查看 iOS:设置 → 电池 → 查看「网络」一栏的占比更严谨一点的做法是:在同一段使用习惯下(比如都待机 8 小时),分别记录开/关私人 DNS 的电量消耗。差异如果落在统计噪声里(几个百分点以内),就说明没有可感知影响。需要注意的是:任何一次「刷视频」都会把 DNS 的影响彻底淹没。 想量出来,必须控制变量。五、结论问题结论加密 DNS 的流量大吗一天几 MB 级别,可以忽略会多一个后台服务吗不会,它是系统解析器的一部分会比明文 DNS 明显费电吗不会,差异在噪声范围内什么情况下会费电解析器太远、开了「自动」反复切换、或配置无效导致解析一直失败一句话:加密 DNS 不是后台服务,是解析器的一个开关。 真正影响续航的是「连不连得上」,不是「加不加密」。相关阅读开加密 DNS 会拖慢游戏吗?把「开局解析一次」和「全程连接」分清楚安卓「私人 DNS」为什么只能填主机名、不能填 IP私人DNS不生效怎么办?多设备通用排查方法与常见原因私人DNS速度怎么样?DNS延迟测速方法与工具推荐
2026年10月03日
10 阅读
0 评论
0 点赞