内网穿透实战:SSH 反向隧道让外网随时随地访问家里的服务器

一条隧道,把 NAT 后的服务器接到公网 家里有台 debian 服务器,跑着 mihomo、Docker、kind 集群,人在公司或外地的时候想连上去干活——但它在家庭局域网后面,没有公网 IP,外面根本摸不到它。某开发者的解法:让它自己主动"爬"出去,在唯一有公网 IP 的阿里云 ECS 上挂一个隧道入口。从此不管在哪,一条命令直达家里的服务器,而且安全到脚本小子无从下手。 这篇文章完整记录这个方案:先讲透原理(NAT 为什么挡人、反向隧道为什么能钻出去),再对比工具选型,然后给出全部配置过程和真实踩过的三个坑。 这次要做什么 目标:通过公网 ECS 中转,实现在任意网络 SSH 访问位于家庭 NAT 后的 debian 服务器 产出:ssh debian-lan 一条命令直达;局域网内原有直连不受影响 安全:至少挡住脚本小子——不暴露额外公网端口、隧道账号无 shell、全链路密钥认证 原理:NAT 挡住了什么,隧道就钻什么 第一步:理解 NAT 的"单向门" NAT(Network Address Translation,网络地址转换)让内网设备共享一个公网出口,但它是一扇单向门:内网设备主动出站,路由器放行并记住映射;公网侧想主动连进来,路由器没有对应记录,直接丢弃。 flowchart TD subgraph WAI["公网侧"] U["笔记本\n(任意网络)"] S["ECS 公网 IP"] end subgraph LAN["家庭局域网 (NAT 后)"] D["debian 服务器\n192.168.x.x"] end U -->|"① 出站可达"| S S -.->|"② 入站被 NAT 丢弃"| D D -->|"③ 出站可达"| S style S fill:#0f172a,stroke:#3b82f6,stroke-width:2.5px,color:#ffffff,font-weight:bold style U fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#ffffff style D fill:#1e1e24,stroke:#6b7280,stroke-width:2px,color:#ffffff 图中 ② 那条虚线就是死路:别人永远无法主动找到你。但注意 ① 和 ③——出站永远是通的。这就是全部突破口。 ...

十一月 16, 2023 · 4 分钟 · 735 字 · yaomingye

Maven Surefire 测试中的 MalformedInputException:当 JVM 默认编码与 Nacos 配置编码不一致时

又见编码问题:mvn test 报 MalformedInputException,IDE 却好好的 某开发者最近在项目中遇到一个奇怪的现象:mvn test 跑 Spring Boot 集成测试时,应用上下文启动失败,控制台抛出一串 MalformedInputException: Input length = 1。但同样的代码,在 IDE 里直接点"运行"按钮,启动得丝般顺滑。 这看起来像是 Nacos 配置中心的问题——因为错误信息里提到了 nacos:mall-auth-api-dev.yaml 配置文件找不到。但用 curl 请求 Nacos API,配置明明存在,内容也是合法的 YAML。 到底是哪里出了问题? 问题复现 执行命令: mvn test -pl mall-auth 控制台输出类似: *************************** APPLICATION FAILED TO START *************************** Description: Config data resource 'NacosConfigDataResource{...}' via location 'nacos:mall-auth-api-dev.yaml' does not exist 但此刻如果打开浏览器访问 Nacos 控制台,或者用 curl 直接拉取: curl "http://localhost:8848/nacos/v1/cs/configs?dataId=..." 配置内容完整返回,HTTP 状态码 200。所以配置是存在的,但 Nacos 客户端在 Spring Boot 中读取失败了。 翻到堆栈深处,真正的异常是: Caused by: org.yaml.snakeyaml.error.YAMLException: java.nio.charset.MalformedInputException: Input length = 1 at com.alibaba.cloud.nacos.parser.NacosDataParserHandler.parseNacosData(...) at com.alibaba.cloud.nacos.configdata.NacosConfigDataLoader.pullConfig(...) Caused by: java.nio.charset.MalformedInputException: Input length = 1 at java.base/java.nio.charset.CoderResult.throwException(...) at java.base/sun.nio.cs.StreamDecoder.implRead(...) 这就清楚了——不是配置"不存在",而是配置内容解析时遇到了字符编码问题。SnakeYAML 在读取 YAML 时,接收到了一个无法解码的字节序列。 ...

六月 19, 2023 · 4 分钟 · 726 字 · yaomingye

Knife4j 文档聚合:微服务里那些「不请自来」的 API 分组

隔壁服务的 API,怎么跑我这儿来了? 某天启动 auth 服务,打开 http://localhost:8021/doc.html ,想看一眼自己刚调好的三个 API 分组——等等,下拉框里怎么还有 message、order 的分组?点过去全是 404,auth 服务上根本就没有这些接口。 flowchart LR subgraph USER["👤 开发者"] A(["打开 auth 的\ndoc.html"]) end subgraph ACTUAL["期望"] B([只显示自己\n3 个分组]) end subgraph REAL["现实"] C([显示了 8 个\n不同服务的分组]) end A --> B A --> C C --> D{other groups\ntap 404} D -->|yes| E[「这就很烦了」] 代码是同一套代码,springdoc 和 knife4j 版本都是统一管理的,为什么 auth 的文档页面里会出现其他服务的痕迹?某开发者决定,今天不修好不下班。 第一反应:去 knife4j 找配置 这种"在一个服务里看到另一个服务的 API",第一感觉就是 knife4j 的网关聚合功能 在作祟。毕竟 knife4j 有个专门的 knife4j-aggregation-spring-boot-starter ,专门用来在 gateway 上聚合所有微服务的文档。 二话不说,给每个服务的 application.yml 加上: knife4j: enableAggregation: false 重启,刷新——没变化,其他分组稳如泰山地挂在下拉框里。 ...

三月 14, 2023 · 3 分钟 · 607 字 · yaomingye

Sentinel Dashboard 容器化部署踩坑全记录:从端口映射到认证配置的血泪史

被一个社区镜像折磨的 24 小时 目标说明 这篇博客的目标很朴素:让读者用 10 分钟把 Sentinel Dashboard 跑起来,而不是花一整天跟一个社区镜像死磕。 某开发者在搭建微服务治理平台时,需要部署 Sentinel Dashboard 作为流量治理控制台。本以为 docker run 一把梭,结果被 bladex/sentinel-dashboard:1.8.6 这个社区镜像折腾了整整一天——端口改不掉、环境变量传了等于没传、配置文件挂载不生效、启动就崩溃、浏览器打开 401……踩了个遍。 本文将完整记录这 7 个坑的根因、排查过程和最终解决方案。所有配置已在 Debian 13 + Docker 26+ 下验证通过。 📌 前置知识:需要了解基本 Docker 操作和 Spring Boot 配置文件概念。 前置条件 项目 要求 操作系统 Linux(本文基于 Debian 13 WSL2) Docker 26+ Docker Compose v2+ 目标端口 9903(按需调整) 验证命令: docker --version # Docker version 26.x.x docker compose version # Docker Compose version v2.x.x 环境搭建 创建一个部署目录,后续所有文件都在此目录下操作: mkdir -p ~/dev-env/sentinel && cd ~/dev-env/sentinel 先简单拉个镜像试试水: ...

三月 4, 2023 · 5 分钟 · 855 字 · yaomingye

Live2D 模型渲染崩溃:游戏提取动作文件的 TotalPointCount 元数据错误排查与修复

Live2D 调包侠踩坑记:一个「少算 44 个点」引发的渲染崩溃 故事的开端 某开发者最近在折腾网页 Live2D 看板娘。项目基于 naihe-live2d-widget-v3,它对标的是经典的 live2d-widget,但底层换成了最新的 Cubism SDK for Web v5,专门渲染 .moc3 格式的新版模型。 一切看起来都很顺利——直到把从游戏里提取的一个猫耳角色模型(编号 416)丢进去。 模型加载了,但动作一播放就崩。刷新,又崩。时好时坏,但大部分时间页面一片空白,控制台躺着一行红字: Uncaught (in promise) TypeError: Cannot set properties of undefined (setting 'time') 对调包侠来说,这可能就是那一刻想关电脑的信号。 定位问题:不是模型坏了,是 Meta 骗了 SDK 报错指向 live2d-sdk.js 中 parse 函数的某个 .time 赋值操作。顺着调用栈往上翻: parse → create → loadMotion → preLoadMotionGroup → setupModel → loadAssets ——动作文件解析时崩溃了。 📌 前置知识:Live2D 的 .motion3.json 文件描述了一条条动画曲线。每条曲线包含一串「段」(segments),每个段由若干个「控制点」(points)定义。Meta 段会预先声明总点数和总段数,方便 SDK 预分配内存。 某个开发者的直觉是:会不会是 Meta 里声明的数量跟实际数据对不上? 写了一段简单的验证脚本跑了一下: let calculatedPoints = 0; for (const curve of curves) { const segs = curve.Segments; let pos = 0, first = true; while (pos < segs.length) { if (first) { calculatedPoints++; pos += 2; first = false; } const segType = segs[pos]; switch (segType) { case 0: calculatedPoints++; pos += 3; break; // 线性 case 1: calculatedPoints += 3; pos += 7; break; // 贝塞尔 case 2: calculatedPoints++; pos += 3; break; // 步进 case 3: calculatedPoints++; pos += 3; break; // 反向步进 } } } 结果一看——全都对不上。 ...

二月 26, 2023 · 3 分钟 · 614 字 · yaomingye

第2步:让 Pod 活得久一点 —— 探针、资源和配置注入实战

让 Pod 活得久一点 一、目标说明 上一篇文章成功部署了第一个 K8s 应用。但现实是——Pod 不会永远乖乖 Running。第二天打开监控一看:一个 Pod 被 OOMKilled,一个在 CrashLoopBackOff 无限重启,还有一个 Pending 了 3 小时没人管。 这篇文章要解决的就是:怎么让 Pod 活得久、死得明白、配置配得清楚。 读完这篇文章,读者能: 区分三种探针的适用场景,写出正确的探针配置 给容器设置合理的 resources 限制,避免 OOMKilled 和 CPU 被偷 掌握环境变量注入的 3 种方式及其选型标准 用 Volume Mount 把配置文件挂进 Pod 看懂 Pod 最常见的 6 种异常状态及其排查方向 二、前置条件 前置条件 要求 验证命令 已完成第 1 步 本地 K8s 能正常 deploy kubectl get deploy -n my-first-app 理解 Pod 基本概念 知道 Pod 里跑容器 看一眼第 0 步速查表即可 理解 Deployment 基本概念 知道 replicas、selector 看一眼第 1 步 Deployment YAML 即可 三、环境准备 沿用第 1 步的环境,先重新部署一遍做基准: ...

一月 7, 2023 · 8 分钟 · 1551 字 · yaomingye

MySQL 实战优化:从 EXPLAIN 到 NULL 陷阱

从 EXPLAIN 到 NULL 陷阱——优化其实有章可循 📌 前置知识:这篇是系列最后一篇,面向日常开发的实战视角。前四篇的理论基础——B+树、索引结构、MVCC、锁机制——这篇会直接引用而不重复展开。建议至少读过第一篇 B+树索引体系再看这篇。 1. EXPLAIN:优化器的自白 EXPLAIN 是 SQL 优化的第一工具。它不会替你优化 SQL,但它告诉你 MySQL 打算怎么优化你的 SQL——用了哪个索引、扫描多少行、做了什么额外操作。理解了它的输出,慢查询的根因通常一目了然。 EXPLAIN SELECT * FROM users WHERE name = 'Zhang' AND age > 20 ORDER BY id; 输出如下(省略部分列): +----+------+---------------+------+---------+-------+------+-------------------+ | id | type | possible_keys | key | key_len | ref | rows | Extra | +----+------+---------------+------+---------+-------+------+-------------------+ | 1 | ref | idx_name | idx | 102 | const | 120 | Using index cond | +----+------+---------------+------+---------+-------+------+-------------------+ 逐字段解读: ...

十二月 31, 2022 · 6 分钟 · 1208 字 · yaomingye

事务消息 + 本地消息表 + 生产踩坑

事务消息 + 本地消息表 📖 前置阅读:本文是分布式事务系列的第四篇——假设你已经理解了 CAP/BASE 理论、Seata AT 的 undo_log 机制、TCC 的 Try/Confirm/Cancel 三阶段和 Saga 的补偿链。如果这些概念还陌生——先读 分布式事务本质——CAP、BASE 与四大方案、Seata AT 模式——undo_log 与二阶段原理 和 TCC + Saga——补偿型分布式事务。 一、⚡ 同步方案的瓶颈——为什么还需要异步方案 先回顾前面三篇文章我们做了什么: Seata AT:下单 → 扣库存 → 扣余额——三个操作在一个 @GlobalTransactional 中——同步执行 TCC:Try 预留 → Confirm 确认 → Cancel 回滚——三个阶段——同步执行 Saga:正向执行 → 失败逆补偿——协调者串联——同步执行 它们有一个共同特征:调用方要等所有分支都执行完——才返回结果。 order-service 调用 product-service 扣库存: → 发起 RPC 调用 → 等待 product-service 处理 → 等待 product-service 返回结果 → 拿到结果——继续下一步 如果 product-service 很慢——比如库存要查 3 个 Redis + 2 个 DB: → order-service 的线程就等着 → 线程池撑爆 → 整个链路超时 同步方案的根本矛盾:事务参与方的响应时间——直接影响调用方的吞吐量。 ...

十二月 30, 2022 · 17 分钟 · 3414 字 · yaomingye

开发常用 100 条 Linux 指令全解析

🐧 开发常用 100 条 Linux 指令全解析:从系统监控到性能调优 引言:为什么要掌握这些指令 在日常开发和运维工作中,服务器出现问题时的第一反应往往是 SSH 登录上去排查。能不能在最短的时间内定位到根本原因,取决于对 Linux 诊断指令的熟练程度。这些指令不仅是敲几个字母的组合,更重要的是—— 能看懂输出里每一个数字和字段代表什么 。 下图展示了从服务器出现异常到定位根因的完整诊断链路,以及各个环节对应的核心指令分类: flowchart TD PROBLEM([🚨 服务器异常]) --> CHECK_LOAD{"负载过高\n响应变慢?"} CHECK_LOAD -->|是| PATH_LOAD[📊 系统信息诊断] CHECK_LOAD -->|否| CHECK_MEM{"内存不足\nOOM ?"} CHECK_MEM -->|是| PATH_MEM[🧠 内存诊断] CHECK_MEM -->|否| CHECK_IO{"磁盘问题\nIO 等待?"} CHECK_IO -->|是| PATH_IO[💾 磁盘诊断] CHECK_IO -->|否| CHECK_NET{"网络异常\n连接失败?"} CHECK_NET -->|是| PATH_NET[🌐 网络诊断] CHECK_NET -->|否| CHECK_PROC[🔍 进程级排查] PATH_LOAD --> CMD1["uptime / top / vmstat"] PATH_MEM --> CMD2["free / sar / /proc/meminfo"] PATH_IO --> CMD3["iostat / iotop / df"] PATH_NET --> CMD4["ss / ping / tcpdump"] CHECK_PROC --> CMD5["ps / strace / lsof / journalctl"] style PROBLEM fill:#701a4c,stroke:#e11d48,stroke-width:2px,color:#ffffff,font-weight:bold style CHECK_LOAD fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ffffff,font-weight:bold style CHECK_MEM fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ffffff,font-weight:bold style CHECK_IO fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ffffff,font-weight:bold style CHECK_NET fill:#2a1147,stroke:#a855f7,stroke-width:1.5px,color:#ffffff,font-weight:bold style PATH_LOAD fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#ffffff style PATH_MEM fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#ffffff style PATH_IO fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#ffffff style PATH_NET fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#ffffff style CHECK_PROC fill:#1e1e24,stroke:#6b7280,stroke-width:1.5px,color:#ffffff style CMD1 fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#ffffff,font-weight:bold style CMD2 fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#ffffff,font-weight:bold style CMD3 fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#ffffff,font-weight:bold style CMD4 fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#ffffff,font-weight:bold style CMD5 fill:#052e16,stroke:#16a34a,stroke-width:1.5px,color:#ffffff,font-weight:bold 本文按照 10 大分类 组织 100 条指令,每一条都包含常用选项、实际输出示例、输出参数逐列解读,以及能从这些数据中看出服务器的什么状态。 ...

十月 14, 2022 · 32 分钟 · 6673 字 · yaomingye

WSL2 Docker 数据持久化

☸️ WSL2 Docker 数据持久化:docker-desktop-data 缺失导致容器丢失的诊断与修复 📌 一、问题场景 在日常开发中,使用 Docker Desktop + WSL2 后端是一个常见组合。然而部分开发者在执行 wsl --shutdown 后,重新打开终端时发现一个严重问题: 之前创建的所有容器、镜像、数据卷全部消失 。 以下是一个典型的问题复现过程: # 1. 正常使用 Docker,创建测试容器 $ docker run -d --name my-app -p 8080:80 nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx ... cc3c2e0be814: Pull complete Status: Downloaded newer image for nginx:latest a1b2c3d4e5f6... # 容器启动成功 # 2. 确认容器正在运行 $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 5 seconds ago Up 5 seconds 0.0.0.0:8080->80/tcp my-app # 3. 手动执行 WSL 关闭(或系统重启触发) $ wsl --shutdown # 4. 重新打开终端,检查容器 $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES # 输出为空 —— 所有容器消失! 这个场景的核心问题在于:Docker Desktop 在 WSL2 中的持久化数据没有被正确保存,导致 wsl --shutdown 后所有状态丢失。本文将深入分析根因并提供完整的修复方案。 ...

十月 10, 2022 · 7 分钟 · 1291 字 · yaomingye
Cat Radio