chore: 添加 .gitignore,更新 CLAUDE.md,新增网络基础设施科普博客

- 新建 .gitignore:忽略 workspace.json、.superpowers/、OS 文件
- 更新 CLAUDE.md:新增博客发布流程、Halo CLI、Club-Service-Guide 说明
- 新增博客:从输入网址到看见博客——我的网络基础设施全揭秘
- 停止跟踪 .obsidian/workspace.json
This commit is contained in:
2026-07-23 22:01:05 +08:00
parent 04d899ca89
commit ffa6cbe4d1
4 changed files with 379 additions and 279 deletions
+20
View File
@@ -0,0 +1,20 @@
# ===== Obsidian 个人状态(不共享) =====
.obsidian/workspace.json
.obsidian/workspace-mobile.json
# ===== 临时生成文件 =====
.superpowers/
docs/superpowers/
# ===== OS 系统文件 =====
.DS_Store
Thumbs.db
Desktop.ini
$RECYCLE.BIN/
# ===== IDE =====
.vscode/
.idea/
*.swp
*.swo
*~
-278
View File
@@ -1,278 +0,0 @@
{
"main": {
"id": "5324373015726ba8",
"type": "split",
"children": [
{
"id": "4509724f8bf84da7",
"type": "tabs",
"children": [
{
"id": "e7a7b303c61786dc",
"type": "leaf",
"state": {
"type": "empty",
"state": {},
"icon": "lucide-file",
"title": "新标签页"
}
}
]
}
],
"direction": "vertical"
},
"left": {
"id": "2040fa35d3c780cc",
"type": "split",
"children": [
{
"id": "44f0f6c3e50b6b6b",
"type": "tabs",
"children": [
{
"id": "b87fe94622087e57",
"type": "leaf",
"state": {
"type": "file-explorer",
"state": {
"sortOrder": "alphabetical",
"autoReveal": false
},
"icon": "lucide-folder-closed",
"title": "文件列表"
}
},
{
"id": "fd8c91a5051afb92",
"type": "leaf",
"state": {
"type": "search",
"state": {
"query": "",
"matchingCase": false,
"explainSearch": false,
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical"
},
"icon": "lucide-search",
"title": "搜索"
}
},
{
"id": "281cb4609de43ca5",
"type": "leaf",
"state": {
"type": "bookmarks",
"state": {},
"icon": "lucide-bookmark",
"title": "书签"
}
},
{
"id": "584070af0c010375",
"type": "leaf",
"state": {
"type": "recent-files",
"state": {},
"icon": "clock",
"title": "Recent Files"
}
}
]
}
],
"direction": "horizontal",
"width": 276.5
},
"right": {
"id": "1a950cafdb3ea126",
"type": "split",
"children": [
{
"id": "54723c9a41e32b09",
"type": "tabs",
"children": [
{
"id": "e5ecfd878b991e60",
"type": "leaf",
"state": {
"type": "backlink",
"state": {
"collapseAll": false,
"extraContext": false,
"sortOrder": "alphabetical",
"showSearch": false,
"searchQuery": "",
"backlinkCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-coming-in",
"title": "反向链接"
}
},
{
"id": "3e52d289a53d24e5",
"type": "leaf",
"state": {
"type": "outgoing-link",
"state": {
"linksCollapsed": false,
"unlinkedCollapsed": true
},
"icon": "links-going-out",
"title": "出链"
}
},
{
"id": "b8e0351a55f515d4",
"type": "leaf",
"state": {
"type": "tag",
"state": {
"sortOrder": "frequency",
"useHierarchy": true,
"showSearch": false,
"searchQuery": ""
},
"icon": "lucide-tags",
"title": "标签"
}
},
{
"id": "1e67aceb8faf7653",
"type": "leaf",
"state": {
"type": "copilot-chat-view",
"state": {},
"icon": "message-square",
"title": "Copilot"
}
},
{
"id": "a89fbe0ab5883f4c",
"type": "leaf",
"state": {
"type": "outline",
"state": {
"followCursor": true,
"showSearch": true,
"searchQuery": ""
},
"icon": "lucide-list",
"title": "大纲"
}
},
{
"id": "1e741b4d78845bd1",
"type": "leaf",
"state": {
"type": "halo-sync-status",
"state": {},
"icon": "lucide-file",
"title": "同步状态"
}
},
{
"id": "2a4c1d3c641d5d1d",
"type": "leaf",
"state": {
"type": "git-view",
"state": {},
"icon": "git-pull-request",
"title": "Source Control"
}
},
{
"id": "969b17fcb8d1f364",
"type": "leaf",
"state": {
"type": "calendar",
"state": {},
"icon": "calendar-with-checkmark",
"title": "Calendar"
}
}
],
"currentTab": 3
}
],
"direction": "horizontal",
"width": 439.5
},
"left-ribbon": {
"hiddenItems": {
"halo:同步状态面板": false,
"bases:新建数据库": false,
"switcher:打开快速切换": false,
"graph:查看关系图谱": false,
"canvas:新建白板": false,
"daily-notes:打开/创建今天的日记": false,
"templates:插入模板": false,
"command-palette:打开命令面板": false,
"table-editor-obsidian:Advanced Tables Toolbar": false,
"copilot:Open Copilot Chat": false,
"obsidian-excalidraw-plugin:新建绘图文件": false,
"obsidian-git:Open Git source control": false,
"homepage:Open homepage": false,
"obsidian-importer:Open Importer": false,
"obsidian-kanban:创建新看板": false,
"mermaid-tools:Open Mermaid Toolbar": false,
"omnisearch:Omnisearch": false,
"remotely-save:Remotely Save": false,
"templater-obsidian:Templater": false,
"halo:发布当前文档到 Halo": false
}
},
"active": "1e67aceb8faf7653",
"lastOpenFiles": [
"obsidian-halo/docs/usage-guide.md",
"博客/学术与效率/AI助你轻松上手LaTeX论文写作.md",
"博客/其他/Git团队协作指南大纲.md",
"未命名.base",
"博客/其他/DeepSeek-V4博客大纲.md",
"copilot/copilot-conversations/你好@20260428_130750.md",
"copilot/copilot-conversations",
"copilot",
"copilot/copilot-conversations/分析@20260426_205456.md",
"博客/标签分类审查报告.md",
"博客/DeepSeek-V4全面解析.md",
"博客/Git团队协作指南(精简版).md",
"博客/文章列表.md",
"未命名 2.base",
"未命名 1.base",
"Excalidraw/Drawing 2026-04-26 18.22.24.excalidraw.md",
"Excalidraw",
"博客/OpenClaw介绍.md",
"博客/深度学习完全指南.md",
"博客/视觉语言模型技术综述.md",
"博客/AI助你轻松上手LaTeX论文写作.md",
"博客/CLI在AI时代的浴火重生.md",
"博客/Docker部署完全指南.md",
"博客/从全连接层到卷积.md",
"博客/博客爬取报告.md",
"博客/SEO分析报告.md",
"obsidian-halo/src/modals/search-modal.ts",
"obsidian-halo/src/commands/search-posts.ts",
"obsidian-halo/src/commands/export-post.ts",
"博客/Git团队协作指南大纲.md",
"博客/uv工具推荐博客.md",
"博客/OpenClaw安装教程.md",
"博客/LinearRegression线性回归.md",
"博客/Git团队协作指南.md",
"博客/大数据技术栈.md",
"obsidian-halo/src/commands/manage-taxonomy.ts",
"obsidian-halo-zip-test/images/pat-zh.png",
"obsidian-halo-zip-test/images/settings-zh.png",
"obsidian-halo-zip-test/images/settings-en.png",
"obsidian-halo-zip-test/images/pat-en.png",
"obsidian-halo-zip-test/images/commands-zh.png",
"obsidian-halo-zip-test/images/commands-en.png",
"obsidian-halo/images/settings-zh.png",
"obsidian-halo/images/settings-en.png",
"obsidian-halo/images/pat-en.png",
"obsidian-halo/images/pat-zh.png",
"未命名.canvas"
]
}
+83 -1
View File
@@ -4,7 +4,89 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
## 项目概述 ## 项目概述
这是一个 Obsidian vault核心项目是 `obsidian-halo` —— 一个将 Obsidian 笔记发布到 [Halo](https://github.com/halo-dev/halo) 博客平台的社区插件。vault 根目录同时包含 `博客/` 目录存放博客文章草稿。 这是一个 Obsidian vault包含两个主要项目:
- **obsidian-halo** — 将 Obsidian 笔记发布到 [Halo](https://github.com/halo-dev/halo) 博客平台的社区插件
- **Club-Service-Guide** — 河南理工大学人工智能协会内部知识库(独立 Git 仓库,托管于协会 Gitea)
vault 根目录的 `博客/` 目录存放博客文章草稿,通过 Halo CLI 发布到 `blog.liuhangyv.top`
### 博客目录结构
```text
博客/
├── covers/ # 博客封面图片(.png)
├── AI与大模型/ # AI 与大模型相关文章
├── 学术与效率/ # 学术研究与效率工具
├── 数据分析与报告/ # 数据分析与报告相关
├── 机器学习/ # 机器学习理论与应用
├── 编程与工具/ # 编程技巧与工具链
└── 其他/ # 未分类文章
```
## 博客发布
### 发布方式
两种方式均可发布博客文章,优先使用 Halo CLI:
1. **Halo CLI**(推荐)— 从 Markdown 文件直接发布
```bash
# 导入并发布文章(导入后默认为草稿,需手动 publish)
npx halo post import-markdown --file "博客/编程与工具/文章名.md"
# 更新分类、slug 并发布
npx halo post update <post-name> --categories "<category-name>" --slug "slug-name" --publish true
```
2. **obsidian-halo 插件** — 在 Obsidian 编辑器内通过命令面板发布
### Halo CLI 配置文件
当前激活的 profile 是 `aliyun`,指向生产环境 `https://blog.liuhangyv.top`。NAS 本地有 Halo 备份实例(`http://192.168.5.8:8090`profile `local`),用于测试。
```bash
npx halo post list --page 1 --size 20 # 查看文章列表
npx halo post get <name-or-id> # 查看文章详情
npx halo post create --title "标题" --content "内容" --publish true
npx halo post update <name> --categories "..." --tags "..." --publish true
npx halo post tag list # 列出所有标签
npx halo post category list # 列出所有分类
```
### 博客分类与标签(硬约束)
**发布时必须只使用现有分类和标签,禁止创建新的。** 现有 8 个分类和 11 个标签(具体列表见 memory `halo-blog-tags`)。
常用组合:
- 技术教程/工具文章 → 分类 `技术思考`,标签 `技术深潜`+ `零基础入门` 如面向新手)
- 协会活动/通知 → 分类 `协会进行时`,标签 `协会动态`
- AI 科普/入门 → 分类 `成长实验室`,标签 `零基础入门, AI智能体`
### 博客文章 Frontmatter 格式
```yaml
---
title: 文章标题
date: 2026-07-23
author: liuhangyv
category: 技术思考 # 必须从现有 8 个分类中选
tags:
- 技术深潜 # 必须从现有 11 个标签中选
- 零基础入门
excerpt: 文章摘要
---
```
## Club-Service-Guide
`Club-Service-Guide/` 是河南理工大学人工智能协会的内部知识库项目,有独立的 Git 仓库和 CLAUDE.md。
- **Git 远程**: `git@gitea.liuhangyv.top:Serendipity/Club-Service-Guide.git`
- **访问方式**: Obsidian 打开 `Club-Service-Guide/` 目录作为独立 vault
- **链接规范**: 使用标准 Markdown 相对路径(`[文本](路径.md)`),禁用 `[[wiki-link]]`Gitea Web 不渲染)
- **提交格式**: 约定式提交 `docs:` / `feat:`
## 开发命令 ## 开发命令
@@ -0,0 +1,276 @@
---
title: 从输入网址到看见博客——我的网络基础设施全揭秘
slug: post
excerpt: 用"跟随一个请求的旅程"的方式,从 DNS、反向代理到 FRP 隧道,一步步讲解你的网络基础设施是如何让全世界的人访问到你电脑上的应用的。
tags:
- 技术深潜
- 零基础入门
halo:
site: https://blog.liuhangyv.top
name: d9146db1-0d82-4e79-b7de-bfbd80304140
publish: false
---
# 从输入网址到看见博客——我的网络基础设施全揭秘
> 经常有同学问我:"你的博客是怎么搭的?""为什么输入域名就能访问?""你本地写的文章怎么发布到网上?"
>
> 这篇文章用**跟随一个真实请求的旅程**的方式,把这些问题的答案一次性说清楚。
---
## 第 1 章:前置知识——三种"地址" {#basics}
在正式开始"跟着请求走"之前,我们需要先认识四个基础概念。这些概念就像是网络世界的"路牌"和"门牌号",理解它们之后,后面每一站的故事你都能轻松看懂。
### IP 地址 = 互联网上的门牌号
你家有门牌号,快递员才能找到你;在互联网上,每一台联网的设备也有一个独一无二的"门牌号"——**IP 地址**。它的样子像这样:`192.168.5.8`(我的 NAS)或者 `101.133.128.193`(我的阿里云服务器),由四组数字组成,中间用点隔开。
无论是你的手机、电脑,还是远在千里之外的云服务器,只要连上网,就会有一个 IP 地址。有了这个地址,别人的电脑才能知道把数据发到哪儿去——就像快递小哥看着门牌号,才能准确地把包裹送到你家门口。
### 公网 IP vs 内网 IP —— 城市街道和小区楼号的区别
然而,并不是所有的"门牌号"外面的人都能找到。
**公网 IP** 就像城市里的街道地址——比如"北京市海淀区中关村大街 1 号",全世界的人都知道这是什么位置,任何人都可以按这个地址找过来。阿里云、腾讯云这些云服务器给你分配的就是公网 IP。
**内网 IP** 就不一样了。它更像是小区内部的楼号——"3 号楼 2 单元 501"。你小区里的人当然能找到你,但小区外面的人拿着"3 号楼"这个地址就傻眼了,因为他根本不知道是哪个小区的 3 号楼。你家里的电脑、NAS、手机,连 WiFi 时拿到的都是内网 IP(一般以 `192.168.x.x` 开头)。
**核心矛盾**就在这里:你的博客跑在家里的电脑上,它的"门牌号"是内网 IP。外面的人拿着这个地址,找不到你的小区在哪里。这就是我们后面要重点解决的问题。
### 域名 + DNS = 手机通讯录
现在我们已经有了"门牌号"的概念,但问题又来了——没人能记住一串数字。你会记住所有亲朋好友的电话号码吗?当然不会。你打开手机通讯录,看到"妈妈",点一下,手机自动拨出 `138xxxx1234`
在互联网上,**DNS(域名系统)** 扮演的就是这个"通讯录"的角色。你在浏览器里输入 `blog.liuhangyv.top` 这样一个好记的名字(这叫**域名**),DNS 会帮你查通讯录,然后告诉你:"这个域名对应的 IP 地址是 `101.133.128.193`,去找他吧。"
整个过程你完全无感——和你在通讯录里点"妈妈"一样,根本不用关心背后的号码是多少。
### 端口 —— 同一栋楼的不同房间门
一台服务器(一个 IP 地址)上往往跑着几十个不同的服务:博客、Git 仓库、管理面板、数据库……浏览器怎么知道你找的是哪个服务?
这就需要一个额外的标识——**端口号**。如果说 IP 是楼号,那端口就是这栋楼里不同的房间门号。举个例子,同一台阿里云服务器上:
- **HTTPS(网页)**用 `443` 号门
- **Git SSH** 用 `222` 号门
- **管理面板**用 `8090` 号门
当你访问 `https://blog.liuhangyv.top` 时,浏览器默认就去敲 `443` 这个门——这是网页服务的"固定房间号"。
但问题再次浮现:家庭网络中,运营商的宽带不仅给你的是内网 IP,还默认把大部分端口都锁死了。你连"开门"的资格都没有,外面的人再怎么能敲门也进不来。
---
> **总结一下**:别人的浏览器想访问你电脑上的应用,必须知道两样东西——你在互联网上的门牌号(公网 IP)+ 你开了哪个房间门(端口)。而大多数人家里的电脑只有内网 IP——这就是后面要解决的问题。
---
## 第 2 章:第一站——DNS 把域名变成 IP {#dns}
好了,前置知识已经就位。现在让我们化身小明,真的在浏览器地址栏里敲下 `blog.liuhangyv.top`,然后按下回车——亲自走一遍第一个环节。
---
小明在浏览器地址栏输入了 `blog.liuhangyv.top`,敲下回车。
浏览器看了一眼这个地址,心想:"`blog.liuhangyv.top`……这是一个域名,人类认得,但我只认 IP 地址。"
就像你在手机上点"妈妈",手机必须先去通讯录里查出那串电话号码才能拨号一样——浏览器也需要先去查"通讯录"。这一步,就是第 1 章里讲过的 **DNS**
浏览器转身去问 DNS 服务器:"你好,请帮我查一下,`blog.liuhangyv.top` 这个域名对应的 IP 是多少?"
DNS 服务器翻了翻记录,很快给出回答:"找到了,它的 IP 是 `101.133.128.193`。这是阿里云上的一台服务器,去吧。"
整个过程快到小明完全无感——从敲下回车到拿到 IP 地址,通常只需要几十毫秒。
### 不止一个域名指向这里
如果你接着问 DNS"那 `lhy-git.liuhangyv.top` 呢?"它给你同一个答案:`101.133.128.193`
再问 `gitea.liuhangyv.top` 呢?还是 `101.133.128.193`
没错——我的博客、个人 Git 仓库、协会 Git 仓库,三个域名,全部指向同一台阿里云服务器。同一个门牌号,同一栋楼。
### 拿到地址,出发
浏览器现在有目标了。既然小明访问的是 `https://blog.liuhangyv.top`,浏览器就直奔这个 IP 的 **443 端口**——也就是 HTTPS 网页服务的"标准房门号"。
它敲敲门:"你好,`101.133.128.193` 的 443 号房,我想看 `blog.liuhangyv.top` 的网页。"
但这里浮现出一个有意思的问题:三个域名都指向同一个 IP、同一个 443 端口,服务器收到请求后,怎么知道小明到底想看博客、Git 仓库,还是别的什么?
这个悬念,我们留给下一章揭晓。
---
## 第 3 章:第二站——OpenResty 按域名分流 {#reverse-proxy}
上一章留了一个悬念:三个域名都指向同一个 IP、同一个 443 端口,服务器怎么知道小明到底想看什么?
答案就在这里揭晓。阿里云服务器上跑着一个叫 **OpenResty** 的软件,它扮演的角色,用一句话就能说清楚:**写字楼的前台**。
想象一下这个场景——小明走进一栋写字楼(阿里云服务器),大楼里有很多家公司:一家博客公司、一家 Git 仓库公司、一家管理面板公司。但小明手里只有写字楼的统一地址(IP),没有具体公司名称。他走到一楼前台,前台姑娘问他:"您好,您找谁?"
小明说:"我找 blog.liuhangyv.top。"
前台查了一下登记表:"好的,blog 公司在这边,请跟我来。"然后把他带到了正确的房间。
这就是 OpenResty 每天在做的事情——它就是这个"前台"。
### Host 头部:前台凭什么知道你找谁?
你可能会问:小明没有亲口说"blog.liuhangyv.top"啊?他只是输入了这个网址然后按了回车。前台是怎么听到这个信息的?
答案是——每个 HTTP 请求的"头顶"上,都**死死地贴着一个标签**,上面写着请求的域名。这个标签叫 **`Host` 头部字段**。当小明的浏览器向 `101.133.128.193:443` 发送请求时,请求包里就带着一句话:
> "Host: blog.liuhangyv.top"
哪怕三个域名的请求都走到同一台服务器,OpenResty 看一眼这个 `Host`,立刻就知道:哦,你是来找 blog 的;你是来找 lhy-git 的;你是来找 gitea 的。
然后,它按照预先设定好的**分流规则**,把访客带到各自对应的目的地。这个过程在技术上就叫**反向代理**——浏览器是"正向"地发请求,而 OpenResty 站在服务器的立场"反向"地把请求分发给内部真正的服务。
### 真实的"登记表"
打开我的 OpenResty 配置,它的分流规则大概长这样(简化版):
```nginx
# 博客 → 转发到腾讯云的 Halo(端口 8090)
if ($host = "blog.liuhangyv.top") {
转发("http://127.0.0.1:8090");
}
# 个人 Git → 转发到腾讯云的 Gitea(端口 3000)
if ($host = "lhy-git.liuhangyv.top") {
转发("http://127.0.0.1:3000");
}
# 协会 Git → 转发到阿里云本地的 Gitea(端口 3000)
if ($host = "gitea.liuhangyv.top") {
转发("http://127.0.0.1:3001");
}
```
你看,前两条规则里有"转发到腾讯云"——这不是打错字了。博客和 Git 仓库虽然域名解析到了阿里云,但真正的后端服务其实是跑在另一台腾讯云的服务器上。阿里云这边只是负责"接客",接完客再通过一条秘密通道把请求送过去——这条秘密通道就是下一章的主角 **FRP 隧道**
### 一张嘴喂饱三个服务
到这里,反向代理的核心价值就一目了然了:
**你只需要一台服务器、一个公网 IP,就能在外面挂上几十个不同的域名,每个域名背后连接不同的服务。** 不需要给每个网站单独买一台云服务器——写字楼一栋就够了,前台会帮你把客人带到对的房间。
小明现在已经顺利来到了"前台"面前。对于 blog 和 lhy-git 的请求,前台要做的事不是直接带到楼上的房间,而是——走一条秘密通道,把请求送到千里之外的腾讯云。这条通道是怎么挖通的?我们进入下一章。
---
## 第 4 章:第三站——FRP 隧道穿透内网 {#frp}
等等——你可能会问:"腾讯云不是也有公网 IP 吗?为什么不让用户直接访问腾讯云,还要经阿里云绕一圈?"
好问题。事实上,腾讯云确实有自己的公网 IP(`124.221.93.161`),技术上完全可以让 DNS 直接把 `blog.liuhangyv.top` 指向腾讯云,省略阿里云这个中间环节。
那为什么我没有这样做?三个理由:
**第一,统一入口。** 我的所有域名——博客、个人 Git、协会 Git——只需要指向一个 IP(阿里云),就像所有快递只需送到一栋写字楼。如果哪天我在腾讯云上新加了一个服务,不需要去改 DNS 记录,也不用等 DNS 全球生效——在 OpenResty 里加一行转发规则就够了,秒级生效。
**第二,SSL 证书统一管理。** HTTPS 需要 SSL 证书来加密通信。如果每个服务都直接暴露公网,我得在每台服务器上分别配置证书、分别续期。而把 OpenResty 放在阿里云做统一入口,我只需要在一处管理所有域名的证书——OpenResty 自动帮你完成 HTTPS 加密和续期,省心省力。
**第三,安全隔离。** 腾讯云上的 Halo 博客和 Gitea 仓库,它们的服务端口(8090、3000)并不直接暴露在公网上。外人访问这些服务的唯一路径,就是先到阿里云的 OpenResty 前台,再通过一条加密隧道中转过去。没有隧道的另一端,就算你知道腾讯云的公网 IP,敲这些端口也没人应门。
这第三条,引出了本章真正的主角——**FRP 隧道**。
### FRP 的工作原理:总机接线员 + 内部电话
要理解 FRP,最好的比喻是一栋写字楼的电话系统:
**FRP 服务端(`frps`** 跑在阿里云上,它就像写字楼一楼前台的总机接线员。所有外面打来的电话,第一个接起来的一定是它。
**FRP 客户端(`frpc`** 跑在腾讯云上,它就像藏在各个房间里的内部电话。它的工作方式很特别——不是被动地等着前台呼叫,而是**主动打电话给前台报到**:"喂,总机你好,我是腾讯云上的 Halo 博客,我在这边 8090 号房间,如果有人来找博客,麻烦帮我转过来。"
这条报到电话一旦接通,就变成了一条**持续保持的长连接**——客户端和总机之间始终有一条话线通着。外部访客(比如小明的浏览器)来到写字楼前台,告诉总机"我要找 blog.liuhangyv.top"→ 总机看了一眼登记表 → 通过那条已经保持着的内部电话线,把请求转发到腾讯云上对应的房间。
从外面的角度看,整栋楼只有一个入口(阿里云),一个前台(OpenResty),一个总机(frps)。但实际上,这栋楼的地下连着一条秘密隧道,直通另一栋楼(腾讯云)的内部房间。外人完全看不到这条隧道的存在,更不知道后面还藏着另一个数据中心。
### 一句金句总结
> **FRP 就是在两台服务器之间打了一条加密隧道,让藏在腾讯云上的服务能被阿里云上的请求访问到。你可以把它想象成一根只有你自己知道的水管——外面的人只看到阿里云,但水管的另一头连着腾讯云。**
### 补充:你的本地电脑呢?
聪明的你可能会想到另一个问题:"你肯定是在本地电脑上写文章的对吧?那你本地电脑是不是也要装 FRP,也要有公网 IP?"
完全不需要。
我写这篇文章用的是 Obsidian 编辑器,搭配一个自己写的 **Halo 发布插件**。当我写完之后,在 Obsidian 里点一下"发布",插件直接通过 **HTTPS API** 把文章内容发到 `https://blog.liuhangyv.top`——和普通读者访问博客走的是**同一条路**。请求先到阿里云 OpenResty,再通过 FRP 隧道转到腾讯云的 Halo 服务,Halo 收到文章后存进数据库。
我的 Windows 电脑既没有公网 IP,也没有装任何 FRP 客户端——它只是这条管道另一端的一个"普通访客"而已。
---
到这里,小明从输入网址到看见博客的完整旅程已经全部走完了。DNS → OpenResty → FRP → Halo,四个环节环环相扣。但读到这里你可能会觉得有点晕——这么多台服务器,这么多条隧道,它们之间到底是什么关系?最后这一章,我们用一张全景图把所有东西串起来。
---
## 第 5 章:全景回顾——一张图说清楚 {#overview}
好了,我们从第 1 章一路走到了第 4 章,每一站都讲清楚了。现在让我们退后一步,用三个层次把整件事印进脑子里。
### 一句话走完全程
把小明从输入网址到看见博客的完整链路,用一句话就能说完:
小明输入 `blog.liuhangyv.top` 回车 → DNS 告诉浏览器去 `101.133.128.193` → 阿里云 OpenResty 前台看了一眼 `Host` 头部,发现是博客域名 → 通过 FRP 加密隧道转发请求 → 腾讯云上的 Halo 收到请求、渲染博客页面 → 原路返回小明浏览器,小明看到博客。
这一秒不到的旅程里,数据跑过了三座城市(本地浏览器、阿里云杭州、腾讯云广州)、穿过了两条加密隧道、被四个软件组件接力传递——而你全程只需要输入一个域名。
---
### 全景架构图
用一张 ASCII 图把所有主角画出来:
```
👤 你的同学 / 朋友
输入 blog.liuhangyv.top
① DNS 解析(域名 → IP)
☁️ 阿里云 101.133.128.193(唯一对外入口)
┌─────────────────────────────────────────┐
│ OpenResty(反向代理 / 写字楼前台) │
│ │
│ blog.liuhangyv.top ──→ FRP 隧道 → 腾讯云 Halo
│ lhy-git.liuhangyv.top ──→ FRP 隧道 → 腾讯云 Gitea
│ gitea.liuhangyv.top ──→ 本地转发 → 协会 Gitea
│ │
│ frps(FRP 服务端 / 总机接线员) │
└────────────┬────────────────────────────┘
│ ② FRP 加密隧道
☁️ 腾讯云 124.221.93.161
┌─────────────────────────────────────────┐
│ frpc(FRP 客户端 / 内部电话) │
│ 📝 Halo 博客 :8090 │
│ 📦 Gitea 个人仓库 :3000 │
└─────────────────────────────────────────┘
💻 本地 WindowsObsidian + 发布插件)
→ 通过 HTTPS API 发布文章
→ 和普通读者走同一条路
```
---
### 给想自己搭建的同学
如果你也想让别人访问你电脑上的应用,你只需要这四样东西:
1. **一个域名** — 让你有一个好记的名字,不用每次说 IP 地址。几十块钱一年,建议去 Cloudflare 或腾讯云买。
2. **一台云服务器** — 作为对外入口。阿里云、腾讯云、AWS 都可以——我选择阿里云的原因是网络稳定,低配学生价一年不到一百块。
3. **一个反向代理** — 让一台服务器能服务多个域名。Nginx、OpenResty、Caddy 都可以。OpenResty 本质上就是 Nginx 的超集,配置逻辑完全一样。
4. **一个 FRP 隧道** — 如果你不想把服务直接放在公网服务器上,或者有两台服务器需要打通。开源免费,GitHub 上直接下载,配置十几行就搞定。
这四样东西配齐之后,你的电脑就不再是一座孤岛。全世界任何一个浏览器,都能通过你精心铺设的这条数据高速公路,敲开你的门。
下次有人问你"你博客咋搭的",你就把这篇丢给他 😄