Compare commits

..
6 Commits
Author SHA1 Message Date
FlintyLemmingandCursor fe10a57be8 fix post
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 22:34:08 +08:00
FlintyLemmingandCursor f489be617e new post
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 22:29:49 +08:00
FlintyLemming a616c96ebd new post 2026-08-18 18:32:20 +08:00
FlintyLemmingandClaude Fable 5 929de3b2d8 删除 L2TP VPN 连接文章
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 10:36:27 +08:00
FlintyLemmingandClaude Fable 5 097c3214e6 删除四篇可能涉及翻墙内容的文章
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 10:35:42 +08:00
FlintyLemmingandClaude Fable 5 7da2fbb140 删除两篇 Clash 相关文章
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-18 10:16:04 +08:00
9 changed files with 426 additions and 466 deletions
@@ -0,0 +1,291 @@
+++
author = "FlintyLemming"
title = "vLLM 推理 DeepSeek + New API 前缀缓存两起故障的定位与处置"
slug = "3c0779ab6468801d9d92e4025c0b9cd0"
date = "2026-08-18"
description = "需要对 Claude Code 做一些单独的适配"
categories = ["AI"]
tags = ["DeepSeek"]
image = "https://assets.flinty.moe/blog/posts/2026/08/vLLM%20%E6%8E%A8%E7%90%86%20DeepSeek%20%2B%20New%20API%20%E5%89%8D%E7%BC%80%E7%BC%93%E5%AD%98%E4%B8%A4%E8%B5%B7%E6%95%85%E9%9A%9C%E7%9A%84%E5%AE%9A%E4%BD%8D%E4%B8%8E%E5%A4%84%E7%BD%AE/anthony-nEn0V7fnQLw-unsplash.jpg"
+++
## 摘要
例行巡检发现,某些用户 Claude Code 的前缀缓存命中率长期停在 14% 左右,而同一渠道、同一模型的其他客户端在 83%–91%。定位结论是:Claude Code 每个请求都会在 `system[0]` 放一段每次都变的计费探测头,网关做协议转换时把它拼到了上游 prompt 的最前端,而 vLLM 的前缀缓存是链式哈希,首块一变、后续全废。网关侧增加渠道级过滤后,该入口命中率恢复到 83%,会话内连续轮次可达 98%–99%。
同一轮排查中核实了二级缓存的实际行为,发现主机内存侧的 KV offload 自 08-07 启动以来只写不读:写入指标点累计 198422 次,回读 0 次,External 命中率恒为 0.0%。原因不是 compose 漏配,而是 vLLM `OffloadingConnector` 的 lookup 对混合 KV 采取全组 AND 判定,DeepSeek-V4-Flash 的滑动窗口组与投机解码组按设计不落盘尾块,导致查询整体返回 0,已经写到内存里的 MLA 前缀也不会被提回显存。该问题只能改 vLLM 或等上游,本次未在生产上处理。
此外补记一项 8/13 已合入的相关修正:网关回给 Claude Code 的 `input_tokens` 此前包含缓存命中部分,导致客户端高估上下文占用、过早触发自动压缩。修正后按 Anthropic 语义扣除缓存。这一项在第 1 节修好之前意义有限,修好之后才真正生效。
| 编号 | 问题 | 状态 |
| --- | --- | --- |
| 一 | 计费探测头打断上游前缀缓存 | 已解决,命中率 14.29% → 83.09% |
| 二 | 二级 KV 缓存(主机内存)只写不读 | 根因已定位,未修复,需改 vLLM |
| 三 | 回包 usage 口径导致上下文过早压缩 | 已解决(8/13 合入),依赖问题一才见效 |
---
## 一、系统构成与关键指标
Claude Code 通过 Anthropic 的 `/v1/messages` 接口接入。网关将请求转换为 OpenAI Chat 格式,转发到 8 卡 H200 上的 DeepSeek-V4-Flash-0731。Reasonix、opencode 等客户端则直接走 `/v1/chat/completions`,落到同一渠道、同一模型。
推理侧设计了两级前缀缓存:
| 层 | 位置 | 作用 | 观测方式 |
| --- | --- | --- | --- |
| L1 | GPU KV | 同一前缀的后续轮次免于重复预填充 | usage 的 `cached_tokens`,日志字段 `insight_cache_tokens` |
| L2 | pinned 主机内存约 960 GB | GPU 池放不下而被挤出的前缀,命中时按需 DMA 回显存 | vLLM 指标 `kv_offload_load_bytes``External prefix cache hit rate` |
命中率按 token 计算,即 `sum(insight_cache_tokens) / sum(prompt_tokens)`。这个指标直接决定 H200 的预填充开销:命中的部分不需要重算,未命中的部分要按完整长度重新做一次 attention 预填充。在十万量级的上下文里,两者的耗时差一个数量级。
需要先说明的是,Claude Code 的自动上下文压缩(compact)读取的是接口返回的 `input_tokens`,而不是网关后台记录的 `prompt_tokens`。这一点使得三个问题在用户观感上是耦合的:前缀不命中导致预填充变慢变贵;回包把缓存计入 `input_tokens` 导致客户端以为窗口更满;提前压缩后整段历史被摘要替换,前缀全部改变,又开出一条几乎没有缓存的新会话。
---
## 二、问题一:计费探测头打断上游前缀缓存
### 2.1 现象
2026-08-17,样本用户(`user_id=xx`)当日整体命中率约 48%,明显低于其 8/10–8/13 的 73%–91%。整体数字下降通常有两种形态:全局劣化,或若干维度被拉低后的加权平均。按请求入口拆开后属于后者。
| 8/17 入口 | 请求数 | prompt | 缓存 | 命中率 |
| --- | --- | --- | --- | --- |
| `/v1/chat/completions` 直打 `deepseek-v4-flash-0731`Reasonix | 444 | 5669 万 | 4744 万 | 83.67% |
| `/v1/messages``llm-prime`Claude Code | 451 | 4811 万 | 687 万 | **14.29%** |
| `/v1/chat/completions``llm-prime` | 7 | 132 万 | 19 万 | 14.31% |
两条链路使用同一 channel、同一 token、同一上游模型,`llm-prime` 只做模型名映射。回溯更早日期,该形态可复现,不是偶发抖动:8/14 首次大量使用 Claude Code 当天,`/v1/messages` 为 15.96%,而同日同模型的 `/v1/chat/completions` 仍有 93.11%。
### 2.2 定位过程
**第一步,看分布而不是均值。** 对 8/17 的 451 次 `/v1/messages` 请求按缓存长度分桶,结果高度反常:
| 缓存 token | 请求数 | 含义 |
| --- | --- | --- |
| 0 | 28 | 完全冷启动或被挤出 |
| 256 | 40 | 仅剩模板级极短前缀 |
| 恰好 17920 | **383** | 固定前缀命中,之后全部 miss |
当天 prompt 从 4 万涨到 18 万,缓存长度却纹丝不动地钉在 17920。这排除了"用户内容太碎"或"KV 争抢"这类概率性解释——概率性因素不会产生一个精确常数。
**第二步,从常数反推断点位置。** 17920 = 280 × 64DeepSeek 的 block 按 64 token 对齐,说明这是一个真实的、稳定的前缀命中。Claude Code 的 23 个工具定义 JSON 约 7.4 万字符,按约 4 字符/token 估算约 1.85 万 token,与 17920 吻合。据此可以判断:请求模板中排在工具定义之后的第一段内容,每一轮都在变。平均 prompt 约 10.7 万,17920 / 106667 ≈ 14.3%,与当日实测的 14.29% 完全一致,反推链条闭合。
**第三步,抓转换前的原始请求,对各段取哈希。** 从 Langfuse `events_full` 取出原始 Claude 请求,对 `system` 三块和 `tools` 分别做 sipHash64
| 块 | 内容 | 8/17 全天稳定性 |
| --- | --- | --- |
| 0 | `x-anthropic-billing-header: cc_version=2.1.116.d8c; cc_entrypoint=claude-vscode; cch=<5 位十六进制>;` | **每请求唯一** |
| 1 | `You are Claude Code, Anthropic's official CLI...` | 同一哈希 `88D95ABDDD0F5572` |
| 2 | 主系统提示(工作区、分支、git status 快照等) | 同一哈希 `363BFB9E19A6BCC3` |
| tools | 23 个工具定义 | 同一哈希 |
连续几拍的 `cch` 取值分别为 `8c7f0``7ee55``490dc`,无规律。块 2 虽然包含 git status,但 Claude Code 在会话内不刷新它,当天哈希未变,因此"git status 每轮在变"这一常见猜测也被排除。
**第四步,确认转换层行为。** `/v1/messages` 转 OpenAI Chat 时,非 OpenRouter 路径把所有 system 段顺序拼成一条 string
```
} else {
systemStr := ""
for _, system := range systems {
if system.Text != nil {
systemStr += *system.Text
}
}
openAIMessage.SetStringContent(systemStr)
```
实际发给 H200 的 system 因此以这段头开始:
```
x-anthropic-billing-header: cc_version=2.1.116.d8c; cc_entrypoint=claude-vscode; cch=XXXXX;You are Claude Code...
```
### 2.3 根因
这段头是 Claude Code 给 Anthropic 官方账单用的探测字段,new-api 与本地 DeepSeek 都不读取(全库检索无该字段名)。问题不在于它存在,而在于它每请求变化,且经拼接后落在了上游 prompt 的最前端。
vLLM 的前缀缓存是链式哈希:block N 的 key 由 block N-1 的 key 与本块内容共同决定。首块内容一变,其后所有 block 的哈希随之改变。`cch=` 位于 token 0 附近,因此同一会话的下一轮请求既无法复用上一轮的 system,也无法复用上一轮的对话历史。只有排在这段头之前的工具定义仍能命中,这就是那个精确的 17920。
作为对照,Reasonix 的 system 以固定的 `You are Reasonix, a coding agent.` 开头,327 次以上请求的前 500 字节几乎不变,缓存随对话长度增长到 12 万以上,命中率因此维持在 80% 以上。
补充两项次要因素,二者均不构成根因:当天 Claude 会话几乎全部是 compact 续聊,compact 会整段改写第一条 user 消息,这解释了压缩周期边界处的冷启动,但解释不了周期内每一轮都 miss09:0014:00 Claude 与 Reasonix 并发打同一块 H200 存在 KV 争抢,但 Reasonix 在同样争抢下仍能维持高命中,说明争抢不是决定性因素。
### 2.4 处置
new-api 原本没有"丢弃指定 system 前缀"的能力。`system_prompt_override` 只能向前插入,`RemoveDisabledFields` 只处理顶层 JSON 键,`param_override``regex_replace` 可以workaround但不是产品功能。
最终实现为渠道级开关 `strip_anthropic_billing_header`,默认关闭。设计上做了几处收敛:
- 只在 Claude Messages → 非 Claude 上游、且把 system 压成一条 string 的路径生效。OpenRouter 的 Claude 分块路径不过滤,因为那条链路上的头可能被真正的 Anthropic 端识别。
- 识别规则写死:text 块 `TrimSpace` 后以 `x-anthropic-billing-header:` 开头则整块丢弃;字符串形态的 system 则去掉第一行。不做成用户可填的通用前缀编辑器——目前只有这一个已知的、每轮都变且排在最前的头,通用能力会带来更大的误伤面。
- 不修改 `ClaudeRequest.System` 原文,因此预扣估算与 Langfuse 原始抓包仍能看到这段头。
- 不改计费公式,不影响走 `/v1/chat/completions` 的其他客户端。
- 回滚只需关闭渠道开关,不必回滚镜像。
上线后在连接 H200 vLLM 对应的渠道打开此功能。
### 2.5 效果验证
验证的前提是确认用法没变,否则前后对比不成立。8/18 当日 Langfuse 记录显示:Claude Code 仍走 `/v1/messages`,每个请求的 `cch` 仍在变化,块 1/2 哈希与 8/17 相同,会话仍然全部是 compact 续聊。也就是说,唯一变量是网关剥掉了这段头。
| | 请求数 | 命中率 | 缓存钉在 17920 的次数 |
| --- | --- | --- | --- |
| 8/17(修复前) | 451 | 14.29% | 383 |
| 8/18(修复后,截至回看时) | 54 | **83.09%** | **0** |
缓存长度分布也从"单点钉死"变回正常形态:5 次 0、4 次 256、3 次不足 64k、14 次 64k128k、28 次 128k 以上。高桶中 `avg_cache ≈ avg_prompt`,说明命中的是对话前缀而非仅工具定义。
同一小时内连续几轮的实际数据(上海时间):
```
09:28 prompt 109974 → cache 256 0.2% 冷启动
09:28 111232 → 109824 98.7%
09:28 111422 → 111104 99.7%
09:36 118702 → 0 0% 间隔较久,KV 被挤出后再冷
09:37 119455 → 118528 99.2%
```
需要说明的是 8/18 样本仅 54 次请求,为部分日数据。但结合分布形态、连续轮次曲线以及"钉死次数归零"三项证据,可以认定行为已经改变,而不是样本波动。另一位仅使用 Claude Code、同样携带该头的用户当日命中率约 91%,可作旁证。
剩余未达 95% 以上的部分,主要来自多客户端并发、超长上下文把 GPU KV 挤爆后的整段冷启动,占请求约 15%。这一部分本应由二级缓存兜底,见下一节。
---
## 三、问题二:二级 KV 缓存只写不读
### 3.1 现象
compose 中配置了 `OffloadingConnector`,约 960 GB pinned 主机内存,设计意图是 GPU 池放不下的前缀在命中时从内存 DMA 回显存。
对容器自 08-07 02:48 启动至记录日的完整日志做只读计数(`docker logs | rg -c`,未改动运行中进程):
| 模式 | 命中行数 |
| --- | --- |
| `kv_offload_store_bytes`(写入) | 198422 |
| `kv_offload_load_bytes`(回读) | **0** |
| `External prefix cache hit rate: 0.0%` | 413174 |
| `External prefix cache hit rate` 非 0.0% | **0** |
11 天,近 20 万次写入指标点,零次回读。
### 3.2 排除配置缺失
启动参数确认 L2 是开启的(`08-07 02:48:01`):
```
kv_transfer_config=KVTransferConfig(
kv_connector='OffloadingConnector',
kv_role='kv_both',
kv_connector_extra_config={'cpu_bytes_to_use': 960000000000},
)
disable_hybrid_kv_cache_manager: False
speculative_config: {'method': 'dspark', 'num_speculative_tokens': 7, ...}
block_size: 256
enable_cumem_allocator: True
```
`expandable_segments` 与 connector 的互斥已通过 `--enable-cumem-allocator` 解开,KV 页物理地址稳定才能 pin 给 DMA,这部分是正确的。8 个 worker 与 EngineCore 均创建了 CPU offload`02:50:05` / `02:54:45`),写入每秒数 GB,说明 `cpu_bytes_to_use` 确实生效。改用 `--kv-offloading-size` 也无济于事,那只是同一套 connector 的另一个入口,lookup 语义相同。
### 3.3 指标口径澄清
这个问题此前之所以没被发现,一部分原因是指标容易被误读。`cpu_cache_usage_perc` 经常掉回 0,看上去像"内存池是空的",但这个 gauge 的定义是**正在 pin 的传输**占池子的比例,写完 `complete_store` 的驻留块会转为 evictable,按设计不计入。因此写完立刻归零是正常现象。
判断"有没有回读"的正确判据只有两个:`kv_offload_load_bytes` 是否大于 0,以及 `External prefix cache hit rate` 是否非 0。
### 3.4 排除"时机不对"
一种自然的怀疑是:GPU 一直有空间,所以还没到该走 L2 的时候。这个假设在 L1 已命中的时刻成立,但在下面这种时刻不成立。取记录日 `08-18 09:25` 附近的日志:
```
09:25:22 GPU KV 0.5%, Prefix 88.3%, External 0.0%
09:25:24 prompt throughput 34844 tok/s (大量预填充正在发生)
Prefix 88.3%, External 0.0%
store_bytes=20646236160
09:25:30 Prefix 88.5%, External 0.0%
```
GPU KV 占用仅 0.5%、同时有 3.4 万 tok/s 的预填充在跑,正是 L2 最该出手的时刻,External 依然是 0.0%。启动后第一分钟的日志形态也一样:`lookup_sync_delay_seconds_count` 有计数,说明 lookup 被调用过,只是结果全是 miss。
### 3.5 根因
生产挂载的 `patch/scheduler.py` 中,`_lookup` 先查全量 MLA 组,再逐个查滑动窗口组与 eagle 组:
```
if num_hit_chunks == 0:
return 0
```
任意一组零命中,整次 L2 load 直接放弃,已经写到 CPU 的 MLA 前缀也不会被 promote。调度器侧将该结果记入 External 统计(`hits = num_external_computed_tokens`),lookup 返回 0External 便恒为 0%。
而 DeepSeek-V4-Flash + DSpark 的 KV 结构恰好必然触发这个否决。模型 `config.json``sliding_window=128``compress_ratios` 含大量 4 / 128`dspark_target_layer_ids=[40,41,42]`vLLM 据此拆出多组 KV
| Group | 内容 | block 大小 | 落盘行为 |
| --- | --- | --- | --- |
| 0 | 全量 MLA | 256 | 正常 store,即那几十 GB/s 的写入 |
| 1+ | compressor 滑动窗口(C4 / C128 | 4 或 8 | 每个 256-token 段只保留尾部,前段按设计跳过 |
| 2 | DSpark/MTP,启动时标记为 eagle | 同上 | **decode 阶段故意不存最后一块** |
最后一条在启动日志里写得很明确:
```
[scheduler.py:194] KV offloading: EAGLE/MTP draft attention groups [2] detected.
The trailing chunk of these groups will be excluded from offloading due to volatility.
```
续聊请求的前缀 = 旧 prompt + 上一轮 decode 出来的 assistant token。eagle 组的尾块按设计没有落盘,lookup 从尾部往前匹配必然对不上,于是 `return 0`,MLA 那一大坨已写入的数据随之作废。这与"缓存过期"无关:即使 CPU 中的 MLA 数据完好,调度器也不接受只加载 MLA。
### 3.6 处置决策
**本次不在生产上关闭 L2,也不热改 vLLM。** 定位清楚即可,修改 lookup 要动调度器,不适合在业务时段碰这套 8 卡服务。
两条后续路径,可二选一或并行:
1. 等上游修复。诉求是 OffloadingConnector 在混合 KV 场景下,SWA/eagle miss 时不应否决已命中的 MLA。
2. 自行修改同一处 lookupmiss 时仍 promote 全量 MLASWA 部分现场重算。
修复成功的判据:日志中出现 `kv_offload_load_bytes > 0`;且在 GPU 占用很低时喂入一条刚被挤出的长前缀,External 不再是 0%new-api 侧同类长会话的 `cache=0` 整段 miss 应明显下降。
在此之前,这 960 GB pinned 内存对命中率没有任何贡献,只消耗宿主机内存与 PCIe 写带宽。若决定先卸掉,关闭 OffloadingConnector 不会让 L1 变差。
---
## 四、问题三:回包 usage 口径与上下文压缩时机
这一项与前两项不在同一层,但用户观感是绑定的,因此一并记录。
OpenAI 兼容上游(包括本地 DeepSeek)习惯把缓存命中计入 `prompt_tokens`。网关转成 Anthropic Messages 时若原样写入 `input_tokens`Claude Code 会把整段已命中的上下文当作"新输入"。流式场景还多一层放大:`message_start` 会先带一版预估 prompt,客户端取它与终态 usage 的较大值,而预估值不含缓存信息。
Claude Code 的自动压缩正是读取这套 `input_tokens`。数值被抬高,客户端就以为窗口更满,从而比真实上下文更早触发 compact。compact 之后整段历史被摘要替换,前缀全部改变,L1 再好也接不上——这构成一个自我强化的劣化环。
8/13 合入的渠道开关 `anthropic_messages_exclude_cache`(channel 5 已开)只修改回给客户端的 usage:
- 终态 `input_tokens = prompt_tokens cache_read cache_creation`,下限 0,缓存部分单独放在 `cache_read_input_tokens`
- 流式 `message_start` 不再发送预估值,避免取 max 抬高
计费与网关后台日志不变,仍使用上游原始 usage。
值得说明的是这一项与问题一的依赖关系:在计费头问题修复之前,真实 `cache_read` 几乎为零,扣不扣区别不大,所以当时看不出效果。修复后命中率到 80% 以上,若不扣除,客户端会把十几万已命中 token 当作新输入,压缩会明显提前。两项修改必须同时到位才有意义。
---
## 五、影响
| 项 | 修复前 | 现状 / 下一步 |
| --- | --- | --- |
| Claude Code 前缀命中 | 每轮改写首块,仅约 1.8 万工具 token 能命中,整日 14% | 会话内 98%99%,整日约 83% |
| 长会话被挤出 GPU | 只能整段重算,L2 从未回读 | 根因已定位,等上游或自行修改 lookup;在此之前并发多条 20 万级会话仍会有冷启动 |
| 回包 usage | 含缓存 + 流式预估取 max,用量虚高、压缩偏早 | 按 Anthropic 语义扣除,压缩按真实新输入触发 |
对日常使用 Claude Code 的同事:同一会话内续聊应更快,因为省掉了大部分预填充;也不应再频繁出现上下文压缩问题。如果仍然频繁压缩,更可能是会话确实很长,或同时开了多个 Agent 把 GPU KV 挤满,而不是网关又把数字报错了。
---
## 六、结论与后续
问题一已闭环。这个案例的可复用之处在于定位路径:整体指标下降先按维度拆分而不是直接归因;看分布而不是均值;出现精确常数时优先从常数反推结构而不是找概率性解释;最后用转换前的原始请求做哈希对照来锁定唯一变量。整条链路上真正的排查成本集中在第三步,而它之所以可行,是因为 8858 的 Langfuse 从 8/15 起开始记录完整请求结构——8/14 其实已经出现过同一症状,当时因为没有抓到请求正文而未能定位。
问题二未修复,根因清晰,属于框架层限制而非配置错误,后续走上游或自改两条路径之一。
问题三已随问题一一并生效。
明确不做的事项:不在业务时段重启或热补 vLLM;不把"去头"做成用户可填的通用前缀编辑器;不为提升命中率而改动计费表达式。
@@ -0,0 +1,135 @@
+++
author = "FlintyLemming"
title = "Qwen3.8-27B 部署技术报告"
slug = "3c1779ab6468800eac67f555a5301e82"
date = "2026-08-19"
description = "最终驻留配置为 FP8 + TP2 + DFlash2,单流 96 tok/s"
categories = ["AI"]
tags = ["Qwen", "SGLang"]
image = "https://assets.flinty.moe/blog/posts/2026/08/Qwen3%208-27B%20%E9%83%A8%E7%BD%B2%E6%8A%80%E6%9C%AF%E6%8A%A5%E5%91%8A/pawel-czerwinski-KMICAjAH7fU-unsplash.jpg"
+++
- 硬件:4× NVIDIA RTX A6000 48GBAmpere sm_86,无原生 FP8 Tensor Core
- 服务框架:SGLang(镜像 `lmsysorg/sglang:qwen38-27b`),OpenAI 兼容 API
---
## 一、选型变迁
部署共经历四个阶段,目标从"先跑起来"逐步演进为"更快、更省卡"。
### 阶段 1BF16 + TP4 + MTP(基线)
- 模型:`Qwen3.8-27B` 原始 BF16 权重(约 52 GB),4 卡 TP=4。
- 投机解码:模型自带 MTP 头,EAGLE 模式(steps=3, topk=1, draft tokens=4)。
- 结果:单流 decode 73.4 tok/s。权重占满 4 卡,KV 池 69 万 token。
- 问题:27B BF16 权重使 decode 强内存带宽受限,4 卡全被占用。
### 阶段 2FP8 + TP4 + MTP(量化提速)
- 换用 FP8 量化权重 `Qwen3.8-27B-FP8`(约 29 GBfp8 e4m3 block-128`lm_head` 保持 BF16)。
- 关键点:A6000 是 Ampere,没有 FP8 Tensor CoreSGLang 自动走 **Marlin weight-only FP8W8A16**——权重存 FP8、计算 BF16。收益来自权重从每卡约 38 GB 降到约 7.6 GBdecode 读带宽大幅下降。
- 结果:单流 decode 109.1 tok/s**+49%**),KV 池增至 89.7 万 token。**这是全部配置中最快的一档**,但仍占 4 卡。
### 阶段 3FP8 + TP2 + MTP(省卡,缩到 GPU 2+3
- 应需求把服务收缩到物理 GPU 2+3,TP=2,释放 GPU 0+1 给其他任务。
- 结果:单流 decode 76.4 tok/s。每卡权重翻倍(约 15 GB)、单步计算量增大,比 TP4 慢,但仍略快于 4 卡 BF16 基线。
- 此为"驻留配置"的第一版。
### 阶段 4FP8 + TP2 + DFlash2(当前驻留配置)
-**DFlash2 草稿模型**[z-lab/Qwen3.8-27B-DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2),3.8 GB)替换 MTP 投机解码:`-speculative-algorithm=DFLASH`8 个 draft token。DFLASH 与 MTP/EAGLE 互斥,是替换而非叠加。
- 两个部署约束:
1. **镜像**DFlash2 支持是 SGLang PR 353712026-08-19 才合入,Docker Hub 最新 nightly08-18)还没包含。因此在 `qwen38-27b` 镜像上打了本地 overlay 包 `sglang-qwen38-27b-dflash2:local`(从合入 commit 拷贝 4 个文件,另做了 3 处 qwen38 分支兼容性修补)。
2. **不能独立放 GPU 0**:DFlash2 需要注入目标模型第 5/19/33/47/61 层的 hidden states,草稿 worker 与目标 TP rank 同卡运行,必须与目标模型共置在 GPU 2+3。
- 结果:单流 decode 96.1 tok/s(比同卡 MTP **+26%**),accept length 约 3.33.6 / 8。代价是 conc8 聚合吞吐 356 → 227verify CUDA graph 只采集到 bs≤7),KV 池 31.5 万 → 24.6 万 token(草稿模型占了同卡显存)。
### 草稿模型的放置与并行方式
草稿模型与目标模型**共置在同一组卡上,并随目标一起做 TP2 切分**,这也是 hidden-state 注入式草稿的标准跑法:
- **共置**EAGLE、MTP、DFlash 这类草稿都要消费目标模型的中间层 hidden statesDFlash2 是第 5/19/33/47/61 层),每个 decode step 都要传一次。若草稿放在独立卡上,这些激活每步都得跨卡搬运,延迟会抵消投机收益。vLLM、SGLang 的投机解码默认都与目标同卡运行。
- **TP2 切分,不是每卡一份完整副本**SGLang 中 DFlash2 的线性层(`QKVParallelLinear` / `ColumnParallelLinear` / `RowParallelLinear`)直接复用目标模型的 TP 通信组,按注意力头切分(要求头数能被 tp_size 整除)。3.8 GB 的 checkpoint 在每张卡上实际占约 2.1 GB,与启动日志一致。candidate selector 每步做一次 all-gather 合并两卡候选后取最优。
- 独立卡方案(decoupled / disaggregated speculative decoding)适用于不依赖目标 hidden states 的独立小模型草稿,靠流水线重叠 draft 与 verify,不适用于本场景:DFlash2 架构上没有 embedding 和 lm_head,本身就不是独立模型,无法单独运行。
**选型结论**:追求极限单流速度且 4 卡可用 → 阶段 2FP8 TP4 MTP109 tok/s);只用 2 卡、以单流/低并发为主 → 阶段 4(当前配置);高并发(≥8 路)场景下阶段 4 反而不如阶段 3 的 MTP。
---
## 二、对比测试
测试方法:流式 OpenAI chat completionstemperature=0`ignore_eos`decode 速率不含 TTFT。单流三组 max_tokens=256,并发两组每路 128 token、thinking 关闭。同一脚本、同一组 prompt 跑完四种配置。
### 五组吞吐对比(tok/s
| 配置 | GPU | Decode(短prompt | Think-on | Long-prefill | Conc4 聚合 | Conc8 聚合 |
| --- | --- | --- | --- | --- | --- | --- |
| BF16 + MTP | 03, TP4 | 73.4 | 63.8 | 75.5 | 176.7 | 327.1 |
| FP8 + MTP | 03, TP4 | **109.1** | **95.6** | **112.7** | **224.3** | **373.2** |
| FP8 + MTP | 23, TP2 | 76.4 | 70.9 | 77.0 | 202.7 | 356.4 |
| FP8 + DFlash2(当前) | 23, TP2 | 96.1 | 88.1 | 90.4 | 220.3 | 227.5 |
### 延迟与容量
| 配置 | TTFT(短prompt | KV 池容量(token | 每卡权重占用 |
| --- | --- | --- | --- |
| BF16 + MTP, TP4 | 101 ms | 691,584 | ~13 GB |
| FP8 + MTP, TP4 | 89 ms | 897,088 | ~7.6 GB |
| FP8 + MTP, TP2 | 94 ms | 315,136 | ~15 GB |
| FP8 + DFlash2, TP2 | 92 ms | 245,696 | ~15 GB + 2.1 GB 草稿 |
注:Long-prefill(约 1,655 token 输入)首次 TTFT 含冷缓存约 0.6–1.2 s,前缀缓存命中后约 100150 ms。
### 同卡(GPU 2+3, TP2MTP vs DFlash2 逐项
| 指标 | MTP | DFlash2 | 变化 |
| --- | --- | --- | --- |
| Decode | 76.4 | 96.1 | **+26%** |
| Think-on | 70.9 | 88.1 | +24% |
| Long-prefill decode | 77.0 | 90.4 | +17% |
| Conc4 聚合 | 202.7 | 220.3 | +9% |
| Conc8 聚合 | 356.4 | 227.5 | **36%** |
| KV 池 | 315,136 | 245,696 | 22% |
DFlash2 单流优势来自更高的平均接受长度(约 3.3–3.6 token/步 vs MTP 约 2.53.2)且草稿开销低;并发劣势来自 draft+verify 双阶段开销随 batch 增大、以及 verify CUDA graph 仅覆盖 bs=17。
---
## 三、复现配置
最终配置(FP8 TP2 + DFlash2)的关键启动参数,模型分别挂载为 `/models`FP8 目标模型)和 `/draft`[z-lab/Qwen3.8-27B-DFlash2](https://huggingface.co/z-lab/Qwen3.8-27B-DFlash2)):
```bash
sglang serve \
--model-path=/models \
--trust-remote-code \
--tp-size=2 \
--attention-backend=flashinfer \
--mem-fraction-static=0.80 \
--mamba-full-memory-ratio=0.80 \
--mamba-radix-cache-strategy=extra_buffer \
--page-size=64 \
--chunked-prefill-size=2048 \
--context-length=262144 \
--reasoning-parser=qwen3 \
--tool-call-parser=qwen3_coder \
--speculative-algorithm=DFLASH \
--speculative-draft-model-path=/draft \
--speculative-num-draft-tokens=8 \
--speculative-draft-model-quantization=unquant \
--speculative-draft-attention-backend=flashinfer
```
换回 MTP 只需删掉 5 个 `--speculative-*` 参数、换成 EAGLE 配置(steps=3, topk=1, draft tokens=4)。两卡合计显存占用约 76 GB(含 24.6 万 token 的 KV 池)。DFlash2 支持在 SGLang PR 35371 合入后的版本可用;更早的镜像需要自行从合入 commit 打 overlay 包(见下一节第 5 条)。
---
## 四、实施要点与坑
1. **Ampere 上 FP8 不是"原生 FP8 推理"**:走 Marlin W8A16,收益全部来自权重带宽,不要期待激活也变 FP8。
2. **DFlash2 与 MTP 互斥**`-speculative-algorithm=DFLASH` 替换 EAGLE/MTP,不能叠加。
3. **草稿模型必须与目标同卡,且随目标 TP 切分**:DFlash2 依赖目标模型中间层 hidden states,独立放到空闲 GPU 0 的方案不成立(解耦投机 `decoupled-spec-role` 在该分支只有 IPC 桩,未接 DFLASH)。详见"草稿模型的放置与并行方式"一节。
4. **不要用改 `config.json` 架构名的方式把 DFlash2 灌进 DFlash v1**v1 的 load_weights 会静默丢弃 `attention_conv` / `mlp_conv` / `candidate_selector` 权重,而主干是带 conv 训练的,结果会错。
5. **本地 overlay 的兼容修补**qwen38 分支 vs main):`dflash_worker_v2.py``get_schedule().page_size` 改为 `server_args.page_size`、去掉 `compute_spec_logprobs` 调用(该分支签名不同,logprobs 路径不用即可);`dflash_utils.py``sample_simulated_acc_len` 需从 `_sample_simulated_acc_len` 别名导入。
6. FP8 checkpoint 的 `lm_head` 必须保持 BF16 稠密(在 `modules_to_not_convert` 中),DFlash2 的 candidate selector 依赖它。
@@ -1,54 +0,0 @@
+++
author = "FlintyLemming"
title = "简要解释 SRC-IP"
slug = "4b935dae96ff43d595486a8cce2d42e1"
date = "2019-10-27"
description = ""
categories = ["Network"]
tags = ["Surge"]
image = ""
+++
在新版本的 Surge 中增加了个 SRC-IP 类型的分流规则,从来没听说过这个,引起了极大的兴趣。简单看了下大致理解了 SRC-IP。
简要来说,哪个程序或设备带来了流量,这个程序或设备的 IP 地址,就是这些流量的 SRC-IP。
下面,就以 Switch 内网代理为例子简单解释。至于和负载均衡搭配使用的公网用法,以后再说。
Switch 虽然可以直连,但商店访问慢,下载慢,最关键的是有的游戏直连甚至无法游玩,比如 Asphalt 9
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/1.avif)
一般来说有三种方式,加速器、路由器弄代理,以及今天用到的 HTTP 代理。哪个方式好这里不做讨论,各有优势。
首先保证 Surge 已经开启了 HTTP 代理服务
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/2.avif)
Switch 上,设置 - 互联网 - 互联网设置 - 点击已经连接的 Wi-Fi - 更改设置 - 代理服务器设置 这里点开,启用它。服务器地址一定不要填写 0.0.0.0,而是你运行 Surge 的设备的内网地址,macOS 版的可以在这里看到
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/3.avif)
端口填写的是 HTTP 代理的端口,不要填 SOCKS5 的,自动验证保持为不启用。保存后重新连接。
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/4.avif)
此时 Switch 不一定能正常加速,这是因为,Switch 的流量发送到 Surge 后,要进行规则判断,如果你的规则里没有 Switch 网络活动相关的规则,则还是直连。当然,你也可以通过抓包看域名手动添加对应的规则。但考虑到 Switch 上的服务即使直连都比较慢,不如全部都走代理,那么怎么实现呢?这就引出了 SRC-IP 的概念。在 Surge 的 Dashboard 里,你可以看到很多 Switch 的流量,但是这些流量都是又哪一个 IP 地址带来的呢?就是 Switch 机器的内网 ip
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/5.avif)
我这里就是 192.168.3.56,这就是这些流量的 SRC-IP。只要对这一个 SRC-IP 进行代理,他发来的所有流量就全部走代理了。你可以右键该设备,直接添加一个 SRC-IP 的规则。
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/6.avif)
当然,添加之前,可以创建一个专门给 Switch 用的策略组,方便出现问题后可以切回 Direct
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/7.avif)
再次检查 Dashboard 里 Switch 的流量,即可发现游戏和服务的流量,根据 SRC-IP 规则的判断,走了代理。
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/8.avif)
游戏自然也是能够正常进入
![](https://assets.flinty.moe/blog/posts/2019/10/%E7%AE%80%E8%A6%81%E8%A7%A3%E9%87%8A%20SRC-IP/9.avif)
@@ -1,56 +0,0 @@
+++
author = "FlintyLemming"
title = "Windows 10 连接 IPSec 未加密的 L2TP VPN"
slug = "683396e4936841a8b82181d9fd8ccf42"
date = "2020-04-04"
description = ""
categories = ["Windows"]
tags = ["Windows", "VPN"]
image = "https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/title.avif"
+++
配置 L2TP VPN 的时候,如果设置了 预共享秘钥 但是 IPSec 未加密(群晖的 VPN Server 套件就是),在使用 Windows 10 的时候会连不上。显示如下错误
无法建立计算机与 VPN 服务器之间的网络连接,因为远程服务器未响应。这可能是因为未将计算机与远程服务器之间的某种网络设备(如防火墙、NAT、路由器等)配置为允许 VPN 连接。请与管理员或服务提供商联系以确定哪种设备可能产生此问题。
或者是
L2TP 连接尝试失败,因为安全层初始化与远程计算机的协商时遇到一个处理错误。
下面提供两种解决方法
## 使用第三方 L2TP VPN 连接工具
这里推荐我在公司连接客户 VPN 时使用的软件,SonicWALL Global VPN Client
![](https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/1.avif)
这个软件的话,直接按照向导创建一个新的 VPN 连接即可
![](https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/2.avif)
但这个软件有个很捞的地方,就是用户名和密码保存不了,每次连接都要输入一次。所以我更加推荐下面这种方法。
## 修改注册表
首先如果是公司的电脑并且加了域的情况下,你要确认你能不能动注册表。动不了的话那你还是老老实实用上面那个软件。
1. 打开注册表编辑器,定位到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\RasMan\Parameters
2. 然后新增一个名为 ProhibitIPSec ,类型为 DWORD 32位)的键,值为 0
![](https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/3.avif)
3. 接着定位到 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\PolicyAgent
4. 然后新增一个名为 AssumeUDPEncapsulationContextOnSendRule ,类型为 DWORD 32位)的键,值为 2
![](https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/4.avif)
5. 然后将服务 IPsec Policy AgentPolicyAgent)改为手动
![](https://assets.flinty.moe/blog/posts/2020/04/Windows%2010%20%E8%BF%9E%E6%8E%A5%20IPSec%20%E6%9C%AA%E5%8A%A0%E5%AF%86%E7%9A%84%20L2TP%20VPN/5.avif)
6. 然后就能正常连接了
## 参考
[https://www.heylc.com/code/4.html](https://www.heylc.com/code/4.html)
@@ -1,64 +0,0 @@
+++
author = "FlintyLemming"
title = "【归档】Android SMS 转发到 Telegram"
slug = "720d39d0fbaa4fc1a75873c9403a23e6"
date = "2020-02-08"
description = ""
categories = ["Android", "LifeTec"]
tags = ["Telegram"]
image = "https://assets.flinty.moe/blog/posts/2020/02/Android%20SMS%20%E8%BD%AC%E5%8F%91%E5%88%B0%20Telegram/title.avif"
+++
项目地址:[https://github.com/qwe7002/telegram-sms](https://github.com/qwe7002/telegram-sms)
作者:qwe7002
最近购入了一部 Android 备用机,主要拿来开热点、收短信。但是一般都放在包里,如果需要查看两步验证短信就很麻烦。正好 qwe7002 也做了个工具,可以转发短信到 Telegram 上,这里简单介绍下配置步骤。
### 获取一只 Telegram Bot
1. 浏览器中打开这个网址:[https://telegram.me/botfather](https://telegram.me/botfather) ,会跳转到与 BotFather 的对话中。
2. 点击
/start
再点击
/newbot
3. 接下来他会询问你要创建的机器人的名称(Alright, a new bot. How are we going to call it? Please choose a name for your bot.),自己起一个名称
4. 然后会询问你机器人的ID,必须要以 "bot" 结尾(Good. Now let's choose a username for your bot. It must end in `bot`. Like this, for example: TetrisBot or tetris_bot.),自己起一个,只能用英文
5. 然后他会给你一个 token,大概是这样,记下这一串字符,冒号左右一起的
Use this token to access the HTTP API:
xxxxxxxx:xxxxxxxxxxxxxx
### 配置App
1. 从文章开头的地址下载 Android 端 apk 安装包并安装,打开,如图
![](https://assets.flinty.moe/blog/posts/2020/02/Android%20SMS%20%E8%BD%AC%E5%8F%91%E5%88%B0%20Telegram/1.avif)
2. 在“机器人令牌”处填写刚才获得的 token
3. 在 Telegram 上给机器人发一句话,内容任意(一句可能不行,多发几句x)
4. 回到 App ,点击 “获取最近的对话ID”,就会获得对话ID
5. “可信的电话号码”可以填写一个,用来使用短信操控 Bot
6. 下面可以选择附加功能,如“监控电池状态变动”,用来发送手机低电量和充电提醒
7. 最后点击“测试并保存”,Telegram Bot 上提示如下消息说明配置完毕
[系统信息]
您已成功连接到 Telegram bot 服务器。
### 其他
对机器人发送 /start 可以获取命令
[系统信息]
可用命令:
/getinfo - 获取系统信息
/sendsms - 发送短信
/sendsms2 - 使用第二个卡槽发送短信
### 效果展示
![](https://assets.flinty.moe/blog/posts/2020/02/Android%20SMS%20%E8%BD%AC%E5%8F%91%E5%88%B0%20Telegram/2.avif)
@@ -1,100 +0,0 @@
+++
author = "FlintyLemming"
title = "搭建 ss-server 返回家中网络"
slug = "72397172ace045e79cb399b83cee3673"
date = "2020-11-23"
description = ""
categories = ["HomeLab", "Network"]
tags = ["家庭宽带"]
image = "https://assets.flinty.moe/blog/posts/2020/10/%E6%90%AD%E5%BB%BA%20ss-server%20%E8%BF%94%E5%9B%9E%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C/title.avif"
+++
家里开了公网 IP 后,在外面有时需要返回到家里的局域网中,不然每个服务都 NAT 端口出去,既麻烦也不安全。
目前用的比较多的方法是 VPN,但这种方法最大的问题就是你一旦打开了 VPN,那其他的链接也会受影响。个人感觉最方便的就是使用 ss-server。下面介绍下搭建和使用。
首先我们要明确达到什么效果,比如我们要访问内网中的 192.168.3.8 这个地址,如果我们在家,就应该能直接连接,如果我们在外面,直接访问这个地址也应该能访问到,并且不影响其他地址的访问。比如我们在公司,公司网关是 192.168.4.1(简单举个例子,公司网络一般都比较复杂,掩码不太可能是 255.255.255.0),那我应该同时既能通过 192.168.4.3 访问公司内网的服务,也应该能通过 192.168.3.8 访问家里的服务,不能冲突。
## 搭建服务端
搭建我使用 Docker 进行搭建,由于我的 Windows 电脑不关机,所以我在 Windows 上的 Docker 部署。群晖也有 Docker,同样可以部署。此外,不少路由器的第三方系统中的软件中心也会提供 ss-server 的工具。(假设你已经会基本的 Docker 用法,下面不会讲的特别详细,如果需要,请评论)
### Docker 配置
Docker 镜像我这边使用的是 gists/shadowsocks-libev。
环境变量注意三个地方,SERVER_PORT 是端口号、METHOD 是加密方式、PASSWORD 是密码,根据自己的需要修改。
![](https://assets.flinty.moe/blog/posts/2020/10/%E6%90%AD%E5%BB%BA%20ss-server%20%E8%BF%94%E5%9B%9E%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C/1.avif)
端口映射这里,容器端口填写刚才 SERVER_PORT 里的,Published 端口填写需要转发出去的端口,后面路由器 NAT 要用到。如果小白不熟悉,就 SERVER_PORT、Docker Port、Published Port 三者一致也可。
![](https://assets.flinty.moe/blog/posts/2020/10/%E6%90%AD%E5%BB%BA%20ss-server%20%E8%BF%94%E5%9B%9E%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C/2.avif)
其他就没了,不需要设置路径映射。
### 路由器 NAT
这个就应该都会了,我们只需要把上面的 Published Port 在路由后台的端口转发(NAT)功能里转发到公网即可。
## 客户端配置
上面配置完后,我们现在在外面使用任何一个支持 ss 协议的软件,地址填写 `<DDNS 地址>:<上面转发的端口号>`,密码、加密方式填写环境变量里设置的,就可以连上家里的网络了。
主要是我们要做分流,我们要达到开头说的效果,这就需要一个支持分流和策略组的代理工具了,Windows 有 Clash、iOS 有 Surge 和 Quantumult、macOS 有 Clash 和 Surge。
### iOS - Quantumult X
1. 主菜单 - 节点 - 添加 可以把我们家里的节点先添加进来,比如说我这里就叫 Home
![](https://assets.flinty.moe/blog/posts/2020/10/%E6%90%AD%E5%BB%BA%20ss-server%20%E8%BF%94%E5%9B%9E%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C/3.avif)
2. 然后编辑配置文件,在 [policy] 下新增一条根据 SSID 类型判断的策略
```bash
ssid=backHome, Home, Home, FlintyLemming-Router:DIRECT
```
简单解释下什么意思,ssid=<策略名>, <蜂窝网走的代理>, <Wi-Fi下走的代理>, <特定 Wi-Fi 名下走的代理>。那我这个的意思就是,蜂窝网和 Wi-Fi 下默认都走刚才添加的 Home 代理,但在家里的路由器下(名叫 FlintyLemming-Router)走直连。
3. 最后在 [filter_local] 下添加一条规则即可
```bash
ip-cidr, 192.168.3.0/24, backHome
```
简单解释下中间的 IP 怎么来的,192.168.3.0 是根据我的网关来的(一般就是你路由器地址),我的网关地址是 192.168.3.1,那就填 192.168.3.0。 24 是子网掩码,如果你的子网掩码是 255.255.255.0(简单来说就是你网络的设备 IP 地址只有最后一位是变的),那就是 3x8 = 24。如果子网掩码是 255.255.0.0,那就是 2x8 = 16。
### macOS - Surge
> 你这 SSID 都是无线,那有线咋办呢
不慌,这就要请到 Surge 了,Surge 的 SSID 策略组不仅能够根据 SSID、BSSID 判断,也可以根据网关地址判断,这样有线环境也搞定了。
1. 首先我们还是要添加代理
```bash
Home = ss, <ddns地址>, <转发的端口>, encrypt-method=<设置的加密方式>, password=<密码>
```
2. 然后同样的,新增策略组
```bash
isHome = ssid, default = Home, "FlintyLemming-Router" = DIRECT, 192.168.3.1 = DIRECT
```
Surge 这边看的就很清楚了,除了 SSID 叫 FlintyLemming-Router 的无线,以及网关是 192.168.3.1 的有线走直连,其他都走 Home 代理。
3. 最后添加分流规则
```bash
IP-CIDR,192.168.3.0/24,isHome
```
### Windows
由于目前 Clash 尚不支持根据网关地址判断的策略,只能设置一个手动的策略组,需要的时候打开。不过 Proxifier 4 支持根据 Interface 判断。
![](https://assets.flinty.moe/blog/posts/2020/10/%E6%90%AD%E5%BB%BA%20ss-server%20%E8%BF%94%E5%9B%9E%E5%AE%B6%E4%B8%AD%E7%BD%91%E7%BB%9C/4.avif)
> Photo by [JJ Ying](https://unsplash.com/@jjying?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/network?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
@@ -1,46 +0,0 @@
+++
author = "FlintyLemming"
title = "给群晖 Docker 命令配置代理"
slug = "8c059ad1807e424d8f7059b330525443"
date = "2024-07-03"
description = ""
categories = ["Linux", "HomeLab"]
tags = ["群晖", "Docker"]
image = "https://assets.flinty.moe/blog/posts/2024/07/%E7%BB%99%E7%BE%A4%E6%99%96%20Docker%20%E5%91%BD%E4%BB%A4%E9%85%8D%E7%BD%AE%E4%BB%A3%E7%90%86/karsten-winegeart-kBVAKM4f6V0-unsplash.avif"
+++
本方法在 SA6400 机型上测试可用,该方法只作用于 Docker 命令,`docker pull` 之类的,不作用于容器
1. 首先确保内网里有一个能够访问互联网的代理,随便什么设备,随便什么客户端,只要能起 http 代理就行,注意打开 LAN 访问权限
2. 执行下面的命令创建文件夹
```bash
sudo mkdir -p /etc/systemd/system/pkg-ContainerManager-dockerd.service.d
```
3. 执行下面的命令创建配置文件
```bash
sudo vi /etc/systemd/system/pkg-ContainerManager-dockerd.service.d/http-proxy.conf
```
4. 编辑内容如下,地址根据自己的 IP 和客户端端口修改
```bash
[Service]
Environment="HTTP_PROXY=http://192.168.2.52:6152"
Environment="HTTPS_PROXY=http://192.168.2.52:6152"
Environment="NO_PROXY=localhost,127.0.0.1"
```
5. 应用配置文件
```bash
sudo systemctl daemon-reload
```
6. 重启服务,注意所有的容器都会被关闭
```bash
sudo systemctl restart pkg-ContainerManager-dockerd.service
```
> Photo by [Karsten Winegeart](https://unsplash.com/@karsten116?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash) on [Unsplash](https://unsplash.com/photos/a-black-and-white-photo-of-wavy-lines-kBVAKM4f6V0?utm_content=creditCopyText&utm_medium=referral&utm_source=unsplash)
@@ -1,73 +0,0 @@
+++
author = "FlintyLemming"
title = "【归档】ClashR 路由器安装"
slug = "8e7cc0b5b40540faac8f561bec460a72"
date = "2019-11-11"
description = ""
categories = ["HomeLab", "Network"]
tags = ["Clash"]
image = "https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/title.avif"
+++
## 下载内核
[frainzy1477/luci-app-clash](https://github.com/frainzy1477/luci-app-clash/releases)
在这里下载 Clash 的 luci app,你可以把它想象成一个壳子,不包含内核。一般下载 xxx-2_all.ipk 那个文件。
[frainzy1477/clashr](https://github.com/frainzy1477/clashr/releases/tag/v0.16.3)
在这里根据自己的处理器下载内核文件。
## 安装
1. 首先需要将 ipk 文件放到路由器中,这里使用 scp,默认就放在 tmp 文件夹下
```bash
scp <ipk文件的绝对路径> <路由器的登陆账号>@<路由器的本地IP地址>:<放入的路径>
```
就像下面这样
```jsx
scp /Users/flintylemming/Downloads/luci-app-clash_1.2.7-2_all.ipk [root@192.168.1.1](mailto:root@192.168.1.1):/tmp
```
之后提示输入密码,输入即可
![](https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/1.avif)
2. 然后通过 SSH 登陆后安装(当然也可以用网页端的那个终端,注意登陆一定要用 root 登陆,用户名不能置空)
```bash
ssh root@<路由器IP地址>
```
通过 cd 和 ls 的命令,定位和找到刚才传入的安装包
```bash
cd /tmp
ls
```
![](https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/2.avif)
3. 使用 opkg 命令安装,名字太长可以使用通配符
```bash
opkg install luci-app-clash*
```
![](https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/3.avif)
4. 安装完毕后,刷新网页,在服务下就能看到 Clash 了
![](https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/4.avif)
5. 这只是个壳子,实际上你点开客户端,内核里是没得选的,需要我们放入内核
![](https://assets.flinty.moe/blog/posts/2019/11/ClashR%20%E8%B7%AF%E7%94%B1%E5%99%A8%E5%AE%89%E8%A3%85/5.avif)
6. 通过同样的方法安装刚才下载的内核 ipk 文件,之后即可选择内核,至此安装完毕。
> Photo by [NASA](https://unsplash.com/@nasa?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText) on [Unsplash](https://unsplash.com/s/photos/global?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText)
@@ -1,73 +0,0 @@
+++
author = "FlintyLemming"
title = "国行版小米盒子3s刷国际版系统"
slug = "b053f84f0b174a088c57d4ec93b4da64"
date = "2020-02-09"
description = ""
categories = ["Android", "Consumer"]
tags = ["小米"]
image = "https://assets.flinty.moe/blog/posts/2020/02/%E5%9B%BD%E8%A1%8C%E7%89%88%E5%B0%8F%E7%B1%B3%E7%9B%92%E5%AD%903s%E5%88%B7%E5%9B%BD%E9%99%85%E7%89%88%E7%B3%BB%E7%BB%9F/title.avif"
+++
*主要内容来自[http://www.hdpfans.com/thread-777808-1-1.html](http://www.hdpfans.com/thread-777808-1-1.html),其他相关连接:*
[http://miui.vn/forum/threads/cai-dat-firmware-global-cho-mi-box-3s-mdz-19-aa.50835/](http://miui.vn/forum/threads/cai-dat-firmware-global-cho-mi-box-3s-mdz-19-aa.50835/)
## 需要注意的是
1. 本人第一次研究 Android TV,所以操作上可能存在问题
2. 扒的教程,可能并不是最简单的步骤,但是可行性较高
## 准备工具
1. 一个能科学上网的路由器,或者人在海外
2. 小米盒子3s
3. U盘(建议不大于32G,不然可能会出现问题)
4. USB双头线(可以不需要,教程也是以不用双头线为例)
## 具体步骤
### 降级
1. 格式化U盘为FAT32,如果是NTFS,在第4步可能无法正常刷机,原来是FAT32的话可以直接使用
2. 解压**MiBOX3S_queenchristina_r145.rar**里的两个文件,并将文件放入U盘根目录下
3. 将U盘插入盒子,断电,然后同时按住遥控器上Home和Menu键,此时通电开机
4. 盒子会自动进入刷机界面,如果未自动刷机,出现的是recovery界面,则要检查U盘分区格式是否为FAT32
5. 等待刷机成功后进入系统
### 再刷第二个固件(个人猜测是包含recovery的一个固件,不是特别懂)
1. 盒子上自行安装文件管理器,将**dump_16AB.img**通过U盘拷贝到盒子文件系统的**sdcard**目录下,拷贝完成后拔出U盘
> 原教程是在建立adb连接后拷贝文件,个人用adb push命令尝试拷贝文件无果,所以建议直接用U盘复制
2. 进入小米盒子系统的设置-账户与安全,在里面打开USB调试
3. 在Wi-Fi设置里获取盒子的局域网IP地址
4. 电脑上进入命令行,使用adb网络调试连接盒子,输入
```bash
adb connect <盒子的ip地址>
```
系统会自动补上端口号进行连接(默认是5555)
5. 接着依次输入如下命令
```bash
adb root
adb remount
adb shell dd if=/sdcard/dump_16AB.img of=/dev/block/mmcblk0
```
> 原教程指出第三条命令会花费相当长时间,需要耐心等待跳出新索引箭头,个人可能是因为使用网络adb原因,始终没有跳出新索引箭头,在等待一小时左右后直接关闭窗口,强制执行下面一步,也没有问题
6. 断电,然后同时按住遥控器上Home和Menu键,此时通电开机,进入recovery界面,进行双清
7. 将**MiBOX3_user_once_r750**文件夹里的文件放入U盘根目录
8. 插入U盘,选择Choose Apply update from EXT > Update from udisk,然后选择拷贝的文件,刷入国际版系统
9. 等待重启就可以进入Android TV系统了,完成相关设置即可
> 进入系统后首先要检查你是否可以正常安装第三方app,我个人遇到的情况就是无法安装绝大多数第三方app,原教程中没有提及这个问题,如果存在这个问题,请继续下一步
10. 解压**MiBOX3_userdebug_once_r454.rar**里的两个文件,并将文件放入U盘根目录下(原来的那个记得删除)
11. 将U盘插入盒子,断电,然后同时按住遥控器上Home和Menu键,此时通电开机
12. 等待刷机,成功后则进入系统,重新设置后发现新的系统可以正常安装app