mihomo + proxychains4 Linux 服务器代理速查
适用场景:Linux 服务器无 GUI,持有机场订阅链接,需要在终端按需使用代理。核心思路:mihomo 负责节点管理与连接,proxychains4 负责让任意命令走代理。
目录
- 1. 安装
- 2. 订阅配置(含 MMDB 下载)
- 3. 服务管理
- 4. 节点管理(REST API)
- 5. proxychains4
- 6. 排查
1. 安装
1.1 检测 CPU 指令集(选版本)
grep -m1 flags /proc/cpuinfo | tr ' ' '\n' | grep -E 'avx2|sse4_2'| 输出 | 选择版本 |
|---|---|
有 avx2 | v3 |
只有 sse4_2 | v2 |
| 都没有 | v1(保底) |
1.2 下载并安装
前往 mihomo Releases,Ubuntu/Debian 选 .deb 包:
mihomo-linux-amd64-v3-v1.x.x.deb
dpkg -i mihomo-linux-amd64-v3-v1.x.x.deb装完后 mihomo 自动注册为 systemd 服务,工作目录为 /etc/mihomo/。
2. 订阅配置
2.1 拉取订阅
机场通用订阅 URL 根据 User-Agent 返回不同格式,指定 clash-meta 获取 Clash YAML:
# 先备份默认配置
mv /etc/mihomo/config.yaml /etc/mihomo/config.yaml.bak
# 拉取订阅
curl -A "clash-meta" "你的订阅URL" -o /etc/mihomo/config.yaml验证格式(开头应为 mixed-port: 等 YAML 字段):
head -30 /etc/mihomo/config.yaml2.2 下载 MMDB(首次启动前必做)
机场订阅的 config 含 GEOIP,CN,DIRECT 等规则,mihomo 启动时会解析全部规则,无论当前是什么模式。解析到 GEOIP 规则时会尝试下载 MMDB 数据库,而服务器没有代理访问不了 GitHub,导致启动失败循环。
必须在首次启动前手动下载 MMDB,且下载时服务不能在运行(运行中 mihomo 会把不完整的文件判定为无效并自动删除):
# 确认服务未运行
systemctl stop mihomo
# 通过 GitHub 代理下载
curl -L "https://gh-proxy.org/https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb" \
-o /etc/mihomo/geoip.metadb
# 确认文件存在且大小正常(约 8MB)
ls -lh /etc/mihomo/geoip.metadb2.3 代理模式
mihomo 有三种工作模式,由 config 顶部 mode 字段控制,也可通过 REST API 运行时切换:
| 模式 | 配置值 | 行为 |
|---|---|---|
| 规则模式 | rule | 按 rules 逐条匹配,命中哪条走哪条(订阅默认) |
| 全局模式 | global | 所有流量走代理,规则不参与路由 |
| 直连模式 | direct | 所有流量直连,不走代理 |
# 运行时切换模式(无需重启)
curl -s -X PUT http://127.0.0.1:9090/configs \
-H "Content-Type: application/json" \
-d '{"mode":"global"}'3. 服务管理
# 启动并设为开机自启
systemctl enable --now mihomo
# 查看状态
systemctl status mihomo
# 实时查看日志
journalctl -u mihomo -f
# 重启(修改配置后)
systemctl restart mihomo
# 验证端口已监听(默认 7890)
ss -tlnp | grep 78904. 节点管理(REST API)
mihomo 提供 REST API,默认监听 127.0.0.1:9090(由 config 中 external-controller 字段控制)。
安装 jq 处理 JSON 输出:
apt install jq4.0 查询主代理组名
curl -s http://127.0.0.1:9090/group | jq -r '.proxies[] | select(.type == "Selector" and .name != "GLOBAL") | .name' | head -1一分机场
脚本中用
GROUP=$(...)自动取值,无需手动填写。
4.1 查看当前选中节点
GROUP=$(curl -s http://127.0.0.1:9090/group | jq -r '.proxies[] | select(.type == "Selector" and .name != "GLOBAL") | .name' | head -1)
curl -s http://127.0.0.1:9090/proxies/${GROUP} | jq '.now'"自动选择"
4.2 列出代理组内所有节点
GROUP=$(curl -s http://127.0.0.1:9090/group | jq -r '.proxies[] | select(.type == "Selector" and .name != "GLOBAL") | .name' | head -1)
curl -s http://127.0.0.1:9090/proxies/${GROUP} | jq -r '.all[]'自动选择
故障转移
🇯🇵日本专线-0.1倍率
🇯🇵日本专线2-0.1倍率
...
4.3 测速并切换节点
写入脚本文件,之后直接用命令名调用,无历史折行问题,可分享给他人:
cat > /usr/local/bin/proxy-switch << 'EOF'
#!/bin/bash
TMPDIR=$(mktemp -d)
GROUP=$(curl -s http://127.0.0.1:9090/group | jq -r '.proxies[] | select(.type == "Selector" and .name != "GLOBAL") | .name' | head -1)
echo "当前组: ${GROUP},测试真连接延迟中"
curl -s http://127.0.0.1:9090/proxies/${GROUP} | jq -r '.all[]' > "$TMPDIR/nodes"
i=1
while IFS= read -r node; do
{
encoded=$(python3 -c "import urllib.parse,sys; print(urllib.parse.quote(sys.stdin.read().strip()))" <<< "$node")
result=$(curl -s -G "http://127.0.0.1:9090/proxies/${encoded}/delay" \
--data-urlencode "url=http://www.gstatic.com/generate_204" \
-d "timeout=3000")
delay=$(echo "$result" | jq -r '.delay // "超时"')
printf "%3d %-8s %s\n" "$i" "${delay}ms" "$node"
} > "$TMPDIR/result_$(printf '%04d' $i)" &
((i++))
done < "$TMPDIR/nodes"
wait
cat "$TMPDIR"/result_*
CURRENT=$(curl -s http://127.0.0.1:9090/proxies/${GROUP} | jq -r '.now')
CURRENT_NUM=$(grep -Fn "$CURRENT" "$TMPDIR/nodes" | head -1 | cut -d: -f1)
CURRENT_DELAY=$(awk '{print $2}' "$TMPDIR/result_$(printf '%04d' $CURRENT_NUM)")
read -ep "当前节点:${CURRENT},延迟:${CURRENT_DELAY},是否切换?[Y/n]: " ANSWER
if [[ "$ANSWER" =~ ^[Yy]$ ]]; then
read -ep "输入节点编号: " NUM
NODE=$(sed -n "${NUM}p" "$TMPDIR/nodes")
SELECTED_DELAY=$(awk '{print $2}' "$TMPDIR/result_$(printf '%04d' $NUM)")
echo "已选节点:${NODE},延迟:${SELECTED_DELAY}"
curl -s -X PUT http://127.0.0.1:9090/proxies/${GROUP} \
-H "Content-Type: application/json" \
-d "{\"name\":\"$NODE\"}"
echo "切换完成"
fi
rm -rf "$TMPDIR"
EOF
chmod +x /usr/local/bin/proxy-switch之后每次直接运行:
proxy-switch当前组: 一分机场,测试真连接延迟中
1 194ms 自动选择
2 256ms 故障转移
3 超时ms 🇯🇵日本专线-0.1倍率
4 47ms 🇯🇵日本专线2-0.1倍率
15 70ms 🇸🇬新加坡专线
21 287ms 🇳🇱荷兰专线
40 超时ms 🇭🇰香港-移动专属1号
...
当前节点:自动选择,延迟:194ms,是否切换?[Y/n]: Y
输入节点编号: 4
已选节点:🇯🇵日本专线2-0.1倍率,延迟:47ms
切换完成
节点名含 emoji 或中文不要手动输入,终端/SSH 层会把 emoji 编码损坏。用编号从 API 输出取名,字节序列才保证正确。
切回自动测速模式(单独使用时):
curl -s -X PUT http://127.0.0.1:9090/proxies/$(curl -s http://127.0.0.1:9090/group | jq -r '.proxies[] | select(.type == "Selector" and .name != "GLOBAL") | .name' | head -1) \
-H "Content-Type: application/json" \
-d '{"name":"自动选择"}'5. proxychains4
5.1 配置文件
编辑 /etc/proxychains4.conf,关键项:
dynamic_chain # 链中某个代理不可用时自动跳过
proxy_dns # DNS 也走代理
[ProxyList]
socks5 127.0.0.1 7890 # 注意:必须用 IP,不能写 localhost
dynamic_chain模式下非首个代理不允许使用主机名,必须写127.0.0.1。
5.2 使用方式
# 在任意命令前加前缀
proxychains4 curl https://ipinfo.io
proxychains4 wget https://example.com/file.tar.gz
proxychains4 git clone https://github.com/xxx/xxx验证出口 IP:
proxychains4 curl ip.fm6. 排查
| 现象 | 原因 | 解决 |
|---|---|---|
| 端口未监听,服务反复重启 | config 有 GEOIP 规则,MMDB 不存在 | 见 2.2:停服务 → 下载 MMDB → 启动 |
proxy localhost has invalid value | proxychains4 不允许非首个代理用主机名 | 将 localhost 改为 127.0.0.1 |
| MMDB 文件下载后消失 | mihomo 运行中检测到不完整文件自动删除 | 先 systemctl stop mihomo 再下载 |
| 节点切换无效 | 节点名与 config 不完全一致 | 用 jq -r '.all[]' 复制精确名称 |
| 启动正常但代理不通 | OKZTWO 组当前选中的是无效节点 | 切换到 自动选择 或手动测延迟后选择 |