Compare commits
10
Commits
4f47e1c7e4
...
5d0cd97707
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d0cd97707 | ||
|
|
57c095d074 | ||
|
|
e21cfbc246 | ||
|
|
ba6f048319 | ||
|
|
7afe446054 | ||
|
|
6bf89079d3 | ||
|
|
59f714de06 | ||
|
|
808df3fd3a | ||
|
|
d1806c4def | ||
|
|
9f547b0103 |
@@ -61,7 +61,7 @@ nav a:first-of-type img {
|
|||||||
--hx-color-dark: #000;
|
--hx-color-dark: #000;
|
||||||
--hextra-max-page-width: 76rem;
|
--hextra-max-page-width: 76rem;
|
||||||
--hextra-max-content-width: 76rem;
|
--hextra-max-content-width: 76rem;
|
||||||
--hextra-max-navbar-width: 58rem;
|
--hextra-max-navbar-width: 57rem;
|
||||||
--hextra-max-footer-width: 57rem;
|
--hextra-max-footer-width: 57rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -11,5 +11,5 @@ layout: hextra-home
|
|||||||
{{< /hextra-hero-headline >}}
|
{{< /hextra-hero-headline >}}
|
||||||
|
|
||||||
{{< hextra-hero-subtitle >}}
|
{{< hextra-hero-subtitle >}}
|
||||||
一周更新两次,让脑袋泡泡冰浴
|
不忙就更新,忙就不更新。
|
||||||
{{< /hextra-hero-subtitle >}}
|
{{< /hextra-hero-subtitle >}}
|
||||||
|
|||||||
+5
-11
@@ -9,21 +9,15 @@ poet: |
|
|||||||
|
|
||||||
我是傅堃。
|
我是傅堃。
|
||||||
|
|
||||||
央企TL,个人开发者,新晋奶爸。当然大部分时间精力还是贡献给了体制内的职业发展,干过IT运维智能化、营业计费系统建设、大规模IT资源池安全能力建设、企业身份账号权限治理,挺有意思的。
|
上班族,平时写写代码,带带娃。工作这些年干过营业系统建设、运维自动化、安全系统、身份治理之类的事情,踩的坑挺多。
|
||||||
|
|
||||||
这个站点主要讨论的不是道与术,是本应凌驾于身份角色之上的**自我**。总觉得人其实很难承认自己被所谓靠自己努力成为的身份角色定义,从出生起人的单一身份很难未维持超过5年,情绪有所触动时与五岁时其实没多大区别。
|
这个网站没什么宏大主题,就是记录一些折腾过的技术、翻过的书、和偶尔冒出来的想法。如果你也在日常的琐碎里给自己留了一小块空间,这里或许能找到点共鸣。
|
||||||
|
|
||||||
如果你也想在日常的繁杂中找一个属于自我的空间,挑战落地一些奇思妙想,或是研究一些稀奇古怪,这里的文章会给你启发。
|
公司规定不接商务合作。
|
||||||
|
|
||||||
由于公司管理制度,我不可以参与盈利项目,所以无法接洽商务合作。
|
|
||||||
|
|
||||||
|
|
||||||
Hey, I'm FuKun.
|
Hey, I'm FuKun.
|
||||||
|
|
||||||
Solo developer. Ex-product manager. China state-owned enterprise manager. New dad. Most of my waking hours go to my career — I've done IT operations automation at scale, built billing systems handling massive transaction volumes, architected security for enormous resource pools, and untangled enterprise identity governance. It's been fun.
|
I work in tech management at a state-owned enterprise. I code on the side and raise a kid. Over the years I've bounced around ops automation, billing systems, security architecture, identity governance — nothing too fancy, just enough to learn what not to do.
|
||||||
|
|
||||||
This site isn't about methodology or technique. It's about the **self** that should sit above all the roles we play. I think people struggle to admit how much their identity gets defined by their roles — and yet, no identity you've held has lasted more than five years since birth. When emotions hit, you still react the same way you did at five.
|
This site has no grand agenda. It's where I stash things I've tinkered with, books I've read, and thoughts that surfaced between meetings. If you've carved out a corner of your own amid the noise, maybe you'll find something here.
|
||||||
|
|
||||||
If you're trying to carve out space for your own self amid the chaos — chasing down a wild idea, researching something obscure, building something that's just yours — the writing here might spark something.
|
|
||||||
|
|
||||||
A note on logistics: due to company policy, I can't participate in for-profit projects, so I don't take on business collaborations.
|
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
---
|
|
||||||
title: "从 5G 到算力网络:通信行业的技术演进观察"
|
|
||||||
date: 2026-05-12T23:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
沉舟侧畔千帆过,
|
|
||||||
病树前头万木春。
|
|
||||||
——刘禹锡,中国,772–842
|
|
||||||
description: "5G 商用五年,哪些超出预期,哪些没达预期。以及下一个五年通信行业的关键方向:云网融合、通感算一体化、AI 原生网络和低空经济。运营商面临的结构性挑战和机会。"
|
|
||||||
tags:
|
|
||||||
- 通信
|
|
||||||
- 5G
|
|
||||||
- 行业观察
|
|
||||||
---
|
|
||||||
|
|
||||||
入行通信这些年,看着技术代际更迭,有些感触想记下来。
|
|
||||||
|
|
||||||
## 5G 的这五年
|
|
||||||
|
|
||||||
5G 商用五年,回头看当初的 hype 和现实之间的差距很有意思:
|
|
||||||
|
|
||||||
**超预期**的部分:
|
|
||||||
- 网络能力和覆盖远超 4G
|
|
||||||
- 行业应用从概念走到落地
|
|
||||||
- 切片、边缘计算等特性开始产生价值
|
|
||||||
|
|
||||||
**没达到预期的部分**:
|
|
||||||
- C 端杀手级应用迟迟未出现
|
|
||||||
- 资费下降快,增量不增收
|
|
||||||
- 数字化转型需求 vs 实际付费意愿之间的鸿沟
|
|
||||||
|
|
||||||
## 下一个五年:算力网络
|
|
||||||
|
|
||||||
行业现在提的更多的是「算力网络」—— 把通信网络和计算资源统一调度。
|
|
||||||
|
|
||||||
几个值得关注的方向:
|
|
||||||
|
|
||||||
1. **云网融合** — 网络即服务,算力即服务
|
|
||||||
2. **通感算一体化** — 通信、感知、计算在基站层面融合
|
|
||||||
3. **AI 原生网络** — 用 AI 管网络,从辅助运维到自动运维
|
|
||||||
4. **低空经济** — 无人机、飞行汽车对网络覆盖的新需求
|
|
||||||
|
|
||||||
## 运营商面临的挑战
|
|
||||||
|
|
||||||
- 传统语音+流量模式见顶
|
|
||||||
- 云业务面临互联网厂商竞争
|
|
||||||
- 组织流程迭代跟不上技术迭代速度
|
|
||||||
- 人才结构转型压力大
|
|
||||||
|
|
||||||
不过换个角度看,通信基础设施作为数字经济的「水电煤」,长期价值不会消失,只是价值形态在变。关键是找到自己在价值链中的新位置。
|
|
||||||
@@ -1,116 +1,61 @@
|
|||||||
---
|
---
|
||||||
title: "善假说"
|
title: "养虾试玩报告"
|
||||||
date: 2026-05-16T01:30:00+08:00
|
date: 2026-05-16T01:30:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
poet: |
|
poet: |
|
||||||
君子生非异也,
|
君子生非异也,
|
||||||
善假于物也。
|
善假于物也。
|
||||||
——《荀子·劝学》
|
——《荀子·劝学》
|
||||||
description: "近以AI智能体为幕僚,五日亲历,颇有所得。录之以证荀子之言。"
|
description: "搭了个 AI 智能体,试了五天,记录一下它能干什么。"
|
||||||
tags:
|
tags:
|
||||||
- AI
|
- AI
|
||||||
- 效率
|
- 效率
|
||||||
- 杂文
|
|
||||||
---
|
---
|
||||||
|
|
||||||
{{< figure src="/images/fusheng.jpg" caption="杜堇《伏生授经图》,明,大都会艺术博物馆藏" alt="杜堇《伏生授经图》" >}}
|
{{< figure src="/images/fusheng.jpg" caption="杜堇《伏生授经图》,明,大都会艺术博物馆藏" alt="杜堇《伏生授经图》" >}}
|
||||||
|
|
||||||
荀子说,君子生非异也,善假于物也。我以前读完也就过了,没细想。
|
荀子有句话:"君子生非异也,善假于物也。"说白了就是,人和人差别没那么大,关键看你会不会用工具。以前我觉得这话挺对,但也没多想。
|
||||||
|
|
||||||
AI 这两年用得不少,ChatGPT、Claude、DeepSeek 都试过。问问题、写代码、润文字,确实方便。但模式始终是单向的——它等你问,你问它答,问完结束。
|
AI 这两年没少用,ChatGPT、Claude、DeepSeek 都试过。问问题、写代码、润文字,确实方便。但模式一直是你问它答,问完就完了。
|
||||||
|
|
||||||
直到我用上一个能自己干活的智能体。它叫"爪子",跑在腾讯云一台轻量服务器上,4 核 4G,配置不高。搭起来很简单:在服务器上装了一个叫 OpenClaw 的智能体框架,绑好微信,配上 Todoist 和 Git 的 API 密钥,就能远程使唤了。这东西驻扎在服务器上,能直接执行 Shell 命令、读写文件、操作 Git 仓库、浏览网页。从有想法到跑起来,花了半晚上。
|
直到最近搭了个能自己干活的 AI 智能体,体验不太一样。
|
||||||
|
|
||||||
## 役物和任人
|
它叫"爪子",跑在腾讯云一台轻量服务器上,4 核 4G。说起来也是薅羊毛——一百块拿下的,跑着网站、Git 仓库、密码管理一堆服务,性能还绰绰有余。在服务器上装了 OpenClaw,绑好微信、Todoist 和 Git 的 API,就能远程指挥了。这东西驻扎在服务器上,能跑 Shell 命令、读写文件、操作 Git、浏览网页。从有想法到跑起来,花了半个晚上。
|
||||||
|
|
||||||
同样是 AI,心态不同,用法天差地别。
|
## 它和 ChatGPT 的区别在哪
|
||||||
|
|
||||||
**役物**——问一句答一句,答错了你纠,纠完再问。你是指挥,还得盯着每一步。
|
以前用 AI:你问一句,它答一句。答错了你纠正,纠完继续问。你得盯着。
|
||||||
|
|
||||||
**任人**——说清楚方向,交给它去做。做完验收就行,中间过程不必过问。
|
现在用智能体:说清楚要干什么,剩下的它自己弄。做完告诉你一声。
|
||||||
|
|
||||||
前者把你绑在操作台前,后者让你从执行中抽身。区别不在技术,在心法。
|
前者把你绑在屏幕前,后者让你可以走开。区别不在技术,在用它的方式。
|
||||||
|
|
||||||
## 三件事
|
## 试了五天,举三个例子
|
||||||
|
|
||||||
试了五天,挑三件说说。
|
### 搭博客
|
||||||
|
|
||||||
### 博客
|
午休时突然想建个站,跟它说了一句。它自己 SSH 上服务器开始干活:装 Hugo、配 Nginx、设 HTTPS、修 404,全自己折腾完。后来换主题换了三次,都是睡前丢一句话,第二天醒来网站已经换好了。
|
||||||
|
|
||||||
午间忽想建站,交代了一句。它 SSH 上服务器就开始干活。
|
### 每天早上的资讯推送
|
||||||
|
|
||||||
```mermaid
|
我让它写了个脚本,每天早上 8:30 把国际新闻、科技动态、通信消息编译成简报,推送到微信。
|
||||||
flowchart LR
|
|
||||||
A[我:建个博客] --> B[SSH 登录服务器]
|
|
||||||
B --> C[安装 Hugo + Nginx]
|
|
||||||
C --> D[配置 HTTPS]
|
|
||||||
D --> E{测试通过?}
|
|
||||||
E -->|否| F[自修权限问题]
|
|
||||||
F --> D
|
|
||||||
E -->|是| G[回复就绪]
|
|
||||||
```
|
|
||||||
|
|
||||||
装 Hugo、配 Nginx、设 HTTPS、修 404 错误,全是它自己弄的。后来主题换了三次——PaperMod、terminal、Hextra——都是半夜一句话的事。第二天醒来网站已经跑在新主题上了。
|
第三天推送没来。没等我问,它自己查了日志发现 DeepSeek API 响应变慢超时了,自动调长时限重发了。
|
||||||
|
|
||||||
### 资讯推送
|
### 工作对话转待办
|
||||||
|
|
||||||
每天早上想看国际新闻、科技动态和通信消息。我让它写了个定时脚本,每天 8:30 推送微信。
|
同事发的聊天截图丢给它,说一句"入待办"。它读图、认人、提取待办事项、写入 Todoist、标优先级和截止日。原来一分钟的事,现在十秒。
|
||||||
|
|
||||||
```mermaid
|
## 试着放了更多权限
|
||||||
flowchart LR
|
|
||||||
A[Cron 8:30] --> B[拉取待办 + 新闻]
|
|
||||||
B --> C[编译简报]
|
|
||||||
C --> D[推送微信]
|
|
||||||
D --> E{成功?}
|
|
||||||
E -->|失败| F[自查超时]
|
|
||||||
F --> G[延长时限重发]
|
|
||||||
G --> D
|
|
||||||
E -->|成功| H[记入日志]
|
|
||||||
```
|
|
||||||
|
|
||||||
第三天推送没来。没等我问,它已经查了日志,发现 DeepSeek API 响应变慢导致超时,自动调长时限重发了。
|
一开始不敢给太多权限,只让它看看日志。后来发现它出错能自己修、操作有日志可查、半夜发消息秒回——慢慢就放心了。
|
||||||
|
|
||||||
### 工单管理
|
后来给了它邮箱权限,让它每天扫收件箱:找出只发给我没抄送下属的邮件,拆附件、正文转 markdown、写摘要、关联已有资料,归档到本地工作目录。第二天上班打开文件夹,摘要和附件已经摆好了。
|
||||||
|
|
||||||
工作对话截图丢过去,说一句"入待办"。
|
## 最后
|
||||||
|
|
||||||
```mermaid
|
有人问这和 ChatGPT 有什么区别。我觉得可以这么理解:ChatGPT 像字典,能查东西但不会替你干活。智能体像秘书,不一定比你懂行,但能替你跑腿。
|
||||||
flowchart LR
|
|
||||||
A[我:发聊天截图] --> B[读取并提取信息]
|
|
||||||
B --> C[创建 Todoist 任务]
|
|
||||||
C --> D[标优先级 + 截止日]
|
|
||||||
D --> E[回复确认]
|
|
||||||
```
|
|
||||||
|
|
||||||
读图、识人、提取待办、写入 Todoist、标优先级、定截止期。原来要折腾一分钟的事,现在十秒。
|
投入也不大:薅腾讯云羊毛,一百块拿下 4 核 4G,几套 API 凭证,半晚上就搭好了。
|
||||||
|
|
||||||
## 信任是试出来的
|
回头想荀子那句话——"善假于物"。最好的工具不一定是最强的,是能让你少干杂事的。用着用着你会发现,真正要琢磨的不是"怎么问 AI",而是"什么可以交给 AI"。
|
||||||
|
|
||||||
一开始不放心,只让它看看日志、查查状态。后来发现出错能自愈、操作有日志可查、半夜发消息也能秒回——慢慢就放手了。
|
|
||||||
|
|
||||||
**邮件过滤。** 信任更进一步,我给了它 IMAP 权限,让它每天扫收件箱。
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
A[每日扫描收件箱] --> B{发给我但<br>没给下属?}
|
|
||||||
B -->|是| C[建文件夹存档]
|
|
||||||
C --> D[附件存 / 正文转 md]
|
|
||||||
D --> E[撰写摘要]
|
|
||||||
E --> F[关联已有文件信息]
|
|
||||||
F --> G[入待办跟进]
|
|
||||||
B -->|否| H[跳过]
|
|
||||||
```
|
|
||||||
|
|
||||||
筛出发给我但没抄送下属的邮件,拆附件、正文转 markdown、写摘要、关联已有资料,分类归档到我本地工作目录的 inbox。第二天上班打开文件夹,摘要和附件已经摆好了。
|
|
||||||
|
|
||||||
## 写在最后
|
|
||||||
|
|
||||||
有人问:这和 ChatGPT 有什么区别?
|
|
||||||
|
|
||||||
我说:**ChatGPT 是字典,这是秘书。** 字典再全,不会替你写材料。秘书不一定比你懂行,但会替你跑腿。
|
|
||||||
|
|
||||||
需要什么投入?一台低配云服务器,一套 API 凭证,花半晚上搭环境。门槛不高。
|
|
||||||
|
|
||||||
回头看荀子那句话——"善假于物"。最好的工具不是功能最强的,而是能让你从琐事中脱身的。用到某个程度你会发现,真正费脑子的不是"怎么问 AI",而是"什么可以交给 AI"。
|
|
||||||
|
|
||||||
这念头一转,效率的瓶颈就从工具变成了自己的想象力。
|
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
---
|
||||||
|
title: "Vibe-coding工具栈的别样用途"
|
||||||
|
date: 2026-06-17T22:00:00+08:00
|
||||||
|
draft: false
|
||||||
|
description: "分享两个日常使用的 AI 辅助办公场景:如何让 AI 帮助梳理待办事项,以及如何利用 AI 进行数据模型分析。"
|
||||||
|
poet: |
|
||||||
|
工欲善其事,
|
||||||
|
必先利其器。
|
||||||
|
——《论语》
|
||||||
|
tags:
|
||||||
|
- AI
|
||||||
|
- 效率
|
||||||
|
- 教程
|
||||||
|
---
|
||||||
|
|
||||||
|
给部门同事做了一次内部分享,讲了两个我在日常办公中高频使用 AI 的场景。两个场景:一是用 AI 管理工作任务,二是用 AI 分析数据模型。都是真实工作场景。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 场景一:培养一个越来越熟悉你工作的助手
|
||||||
|
|
||||||
|
### 不只是记待办
|
||||||
|
|
||||||
|
一开始我只用它记录待办事项,后来发现它的作用远不止于此——它逐渐变成了一个熟悉我工作的助手。
|
||||||
|
|
||||||
|
怎么做到的?不是靠"训练",是靠积累。我的工作任务目录里逐步沉淀了几十个任务文件夹,每个里面都有 AI 协助整理的 README,记录了任务背景、执行思路和产出物的位置。当接到临时取数需求时,它知道参考历史口径。当上级布置多项并行工作时,它能协助理清各条线的推进重点。
|
||||||
|
|
||||||
|
它不需要反复解释业务术语和项目背景——因为此前的任务记录中都有上下文。每次新任务启动,它会先在存档中检索相关材料,提示"这个和之前的某次任务有关联,可以借鉴当时的处理方式"。
|
||||||
|
|
||||||
|
换句话说,它不是每次从零开始。它是带着你的工作积累在辅助你。
|
||||||
|
|
||||||
|
### 具体操作方法
|
||||||
|
|
||||||
|
**第一步:建立目录结构。** 规则很简单,每个任务一个文件夹,命名格式为 `日期@任务主题`:
|
||||||
|
|
||||||
|
```
|
||||||
|
Todo/
|
||||||
|
├── @持续关注/ ← 持续跟进的事项,不归档
|
||||||
|
├── 20260616@某业务创新方案/ ← 每个任务独立文件夹
|
||||||
|
├── 20260616@某系统升级改造/
|
||||||
|
└── 20260617@某数据治理专项/
|
||||||
|
```
|
||||||
|
|
||||||
|
为什么要这样做?几个好处:
|
||||||
|
|
||||||
|
- **状态一目了然**:看目录名就知道有哪些活跃任务,不需要打开任何文件
|
||||||
|
- **上下文自包含**:每个任务的所有内容(需求背景、沟通记录、产出文档)都在同一个文件夹里,不会散落在微信、邮件、本地文件各处
|
||||||
|
- **历史可追溯**:三个月前处理过的任务,文件夹还在,README 还在,随时可以回顾当时的思路
|
||||||
|
|
||||||
|
**第二步:将新任务交给它。** 拿到一个新任务时,不需要自己先想一遍再告诉它。直接交给它即可。例如——
|
||||||
|
|
||||||
|
> "年底前请协助排查全年重点工作,逐项对照当前进展,梳理出存在延误风险的事项"
|
||||||
|
|
||||||
|
它会自动完成以下步骤:
|
||||||
|
|
||||||
|
1. **定位信息源**:先找到重点工作清单(可能是快捷方式指向的 Excel 表格,也可能在某个 Project 目录下)
|
||||||
|
2. **逐项比对**:读取清单中的每个事项,再逐一检查各任务文件夹中的 README 状态和已有产出物
|
||||||
|
3. **输出风险清单**:自动生成一份延误风险报告,按优先级列出——哪些尚未启动、哪些推进受阻、哪些已完成但缺少收尾文档
|
||||||
|
|
||||||
|
整个过程只需要在看结果时复核确认,不必亲自去翻阅几十个文件夹。
|
||||||
|
|
||||||
|
**第三步:日常运转。** 每次接到新任务,操作流程如下:
|
||||||
|
|
||||||
|
1. 把任务信息告知 AI(可以口述、粘贴聊天记录、或直接给一个快捷方式)
|
||||||
|
2. AI 自动创建 `日期@主题` 格式的文件夹,写一份 README 把任务背景、关键信息、初步思路固化下来
|
||||||
|
3. 看一遍 README,确认理解无误,补充或修正
|
||||||
|
4. AI 开始执行,过程中产出的分析、方案、报告都放在这个文件夹里
|
||||||
|
5. 任务完成后,AI 提醒:"这份产出应归档至 Project 目录,那份应归入 Archive 存档,临时文件可删除"
|
||||||
|
|
||||||
|
持续使用一段时间后,能发现一个明显的变化:它越来越不需要做背景解释。因为它已阅读过所有的 README、了解项目结构、知道此前同类任务的处理方式。用得越多,它对工作理解越深。
|
||||||
|
|
||||||
|
就像一位经验丰富的助手——不需要每次都从头交代。
|
||||||
|
|
||||||
|
### 为什么比传统方式好
|
||||||
|
|
||||||
|
传统工作方式的问题不在于"记不住",而在于信息的**碎片化**:微信聊两句、邮件发一份、会议上提一嘴、桌面还扔个快捷方式。出一个任务,信息散落在四五个地方,过两周谁都拼不回来。
|
||||||
|
|
||||||
|
这个方案的核心就是**强制归集**——所有信息收拢到一个文件夹里,由 AI 协助整理成结构化的 README。从此任何任务只需要看这一个文件,就知道全貌。
|
||||||
|
|
||||||
|
### 三个核心原则
|
||||||
|
|
||||||
|
1. **一事一夹**:每个任务一个独立文件夹,不混放
|
||||||
|
2. **文档驱动**:AI 协助整理思路并记录,人确认后执行
|
||||||
|
3. **自动关联**:由 AI 检索历史相关材料,避免重复劳动
|
||||||
|
|
||||||
|
### 前后对比
|
||||||
|
|
||||||
|
```
|
||||||
|
之前:多渠道接收 → 人脑跟进 → 可能遗漏 → 被动应对 → 临时赶工
|
||||||
|
现在:任何来源 → 告知 AI → 自动建档+记录 → 状态可追溯 → 闭环归档
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 场景二:让 AI 分析数据模型
|
||||||
|
|
||||||
|
### 背景
|
||||||
|
|
||||||
|
工作中需要维护一套数据模型文件(.ndm2 格式),共十多个模型,近两千张表。跨模型关联关系复杂,过去需要逐个打开模型文件,查看表结构,手动记录关联关系,一个模块就需要小半天。
|
||||||
|
|
||||||
|
.ndm2 文件本质上是 JSON 格式,每个文件内部完整记录了该模型的所有表定义、字段结构、视图定义、注释信息。AI 可以直接读取其完整结构——不需要做任何格式转换。下面按照实际分析流程展开。
|
||||||
|
|
||||||
|
### 分析前的准备
|
||||||
|
|
||||||
|
第一步不是让 AI 分析,而是让它**了解你有哪些素材**。把数据模型目录的路径告诉它,它会先列一遍目录,确认有几个文件、每个文件多大,做到心中有数。
|
||||||
|
|
||||||
|
然后才是分析。
|
||||||
|
|
||||||
|
### 第一步:快速摸底——谁大谁小
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "分析数据模型目录下所有 .ndm2 文件,汇总每个模型包含的表数量和视图数量,按表数降序排列"
|
||||||
|
|
||||||
|
AI 编写脚本遍历所有 JSON 文件,提取关键统计指标,几秒输出结果。一眼就能看出:
|
||||||
|
|
||||||
|
- **核心模块**:表数最多、视图数也最多的那个,通常是整个系统的数据中枢
|
||||||
|
- **中量模块**:表数在 100-300 之间,各有独立业务领域
|
||||||
|
- **轻量模块**:表数只在两位数甚至个位数,通常是配置管理、服务注册等辅助模块
|
||||||
|
- **特殊模块**:某个模块的视图数特别少(甚至为零),说明它可能是纯数据存储层,业务逻辑在其他地方
|
||||||
|
|
||||||
|
有了这个总览,就知道分析的重心该放在哪些模块上。
|
||||||
|
|
||||||
|
### 第二步:画架构拓扑图——谁和谁有关
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "对比所有 .ndm2 文件,找出同名表在多个模型中同时出现的情况。按共享表的数量从高到低排列,列出每对模型之间共享的表名和数量"
|
||||||
|
|
||||||
|
AI 逐个比对,输出一张关联关系矩阵。实际案例中会发现几种典型模式:
|
||||||
|
|
||||||
|
**模式一:主数据源 + 下游副本。** 比如模块 A 和模块 B 共享了二十多张表(用户主表、资源映射表、角色表、菜单表、密码策略表……)。一看就知道模块 A 是主数据源,模块 B 是其数据副本,用于运营分析或对外接口。
|
||||||
|
|
||||||
|
**模式二:基础版 + 多租户版。** 发现模块 C 和模块 D 共享了六十多张表——资产、部署主机、用户、权限、标签……且字段数几乎一致。这说明模块 D 就是模块 C 的多租户部署版本,两者维护同一套核心表结构。
|
||||||
|
|
||||||
|
**模式三:基础设施跨模块复用。** 定时调度框架的表在所有模块中完全一致(表名、字段数都相同)。这说明了系统的技术选型,也为后续运维评估提供了信息——比如调度框架升级时要同时关注六个模块。
|
||||||
|
|
||||||
|
有了这张拓扑图,以后做数据变更时,事先就能知道改一张表会波及哪些模块。
|
||||||
|
|
||||||
|
### 第三步:定位核心表——按字段数排名
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "找出所有表中字段数量最多的前 20 张表,列出表名、所属模块、字段数和业务注释"
|
||||||
|
|
||||||
|
AI 输出结果后,能看到清晰的层级:
|
||||||
|
|
||||||
|
```
|
||||||
|
xxx_performance_data 模块X 298 字段 ← 性能指标宽表,数据分析类
|
||||||
|
u_user 模块A 140 字段 ← 用户主表,系统最核心实体
|
||||||
|
u_user_encrypt 模块A 134 字段 ← 加密用户信息(与主表同构)
|
||||||
|
u_user_his 模块A 134 字段 ← 用户变更历史
|
||||||
|
u_resource 模块A 127 字段 ← IT 资源主表
|
||||||
|
u_entry_resource 模块A 118 字段 ← 入口资源
|
||||||
|
u_res_account 模块A 95 字段 ← 资源-账号映射
|
||||||
|
u_res_account 模块B 90 字段 ← 同上的运营副本
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
从这个排名里能获得很多信息:
|
||||||
|
|
||||||
|
- **用户主表 140 字段**:这是整个系统的核心实体,任何对它的变更影响面最大,需要最严格的评审
|
||||||
|
- **性能宽表 298 字段**:虽然字段最多,但属于数据分析类,业务变更影响相对小,更重要的是关注其查询性能
|
||||||
|
- **资源-账号映射表在两个模块中出现**:印证了上一步的架构结论——模块 B 是模块 A 的下游副本
|
||||||
|
- **加密/历史表与主表字段数接近**:说明数据安全策略完善,账号数据有加密存储和历史追溯
|
||||||
|
|
||||||
|
### 第四步:按业务语义理解模块职责
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "分析某核心模块中所有有注释的表,按业务语义分组归纳该模块管理了哪些类型的实体。列出每个实体类型的代表表名和职责说明"
|
||||||
|
|
||||||
|
AI 提取所有含注释字段的表,归纳出清晰的业务分类:
|
||||||
|
|
||||||
|
| 实体类型 | 代表表 | 职责说明 |
|
||||||
|
|----------|--------|----------|
|
||||||
|
| 用户身份 | u_user, u_user_encrypt, u_user_his | 账号全生命周期管理,含加密存储和历史追溯 |
|
||||||
|
| IT 资源 | u_resource, u_res_account, u_entry_resource | 被管 IT 资源及账号-资源映射 |
|
||||||
|
| 权限控制 | u_role, u_menu, u_role_menu_rel | RBAC 角色权限模型,菜单功能点定义 |
|
||||||
|
| 密码策略 | u_pwd_policy, u_history_pwd | 密码复杂度策略与历史密码管理 |
|
||||||
|
| 审计日志 | u_portal_log, u_oper_log | 用户操作审计追踪,满足合规要求 |
|
||||||
|
| 机构人员 | u_organization, u_user_group | 组织架构与用户组管理 |
|
||||||
|
| 定时调度 | 调度框架相关表 | 定时任务配置与管理 |
|
||||||
|
|
||||||
|
不需要看一行代码,仅通过表结构和注释就能理解模块管理了七类业务实体,每类的职责边界清晰。这对于接手维护一个新模块、或者给新同事做技术交接,价值巨大。
|
||||||
|
|
||||||
|
同样的方法可以应用到其他模块。比如资源管理模块,AI 会归纳出"资产管理、部署管理、权限管理、监控管理"四类实体,告诉你这个模块管的是 IT 基础设施而非用户身份。
|
||||||
|
|
||||||
|
### 第五步:逐模块深度拆解
|
||||||
|
|
||||||
|
到了这一步,已经知道谁大谁小、谁和谁有关、核心表是哪些。接下来让 AI 逐个模块出详细报告:
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "针对每个模块,分别列出:1) 其核心业务表(非日志/历史/运维类)的清单及字段数;2) 其关系映射表的数量和主要关联实体;3) 其历史/日志表的数量和审计覆盖范围;4) 对照全局统计,分析该模块在整体架构中的角色"
|
||||||
|
|
||||||
|
AI 逐个模块生成分析报告。以实际中最复杂的模块为例:
|
||||||
|
|
||||||
|
- 核心业务表 25 张,涵盖用户、资源、权限、密码、审计五条业务线
|
||||||
|
- 关系映射表 62 张,远超其他模块——因为 RABC 权限模型需要大量的多对多关联(用户↔角色↔菜单、资源↔账号↔权限、组织↔用户……)
|
||||||
|
- 历史/日志表 128 张,占比最高——系统重度依赖审计追踪
|
||||||
|
- 运维辅助表 19 张,主要是调度任务
|
||||||
|
|
||||||
|
而对比资源管理模块:
|
||||||
|
|
||||||
|
- 核心业务表 22 张,聚焦资产管理和部署管理
|
||||||
|
- 关系映射表仅 14 张,且都是 2-5 字段的轻量关联——说明其数据模型更扁平,权限控制由账号管理模块统一负责
|
||||||
|
- 运维辅助表 56 张,远超业务表——因为这个模块负责日常运维监控
|
||||||
|
|
||||||
|
两个模块一对比,各自的定位就非常清楚了:一个管"人"(身份与权限),一个管"物"(资产与设备)。
|
||||||
|
|
||||||
|
### 第六步:变更影响分析
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "如果需要对某张核心表增加一个字段,列出所有受影响的模块、各自的引用方式(主表/副本/轻量引用)、以及对应的字段数"
|
||||||
|
|
||||||
|
AI 交叉检索后,能给出精确的影响范围:
|
||||||
|
|
||||||
|
- **模块 A(主表)**:140 字段 —— 变更在此执行
|
||||||
|
- **模块 B(副本)**:87 字段 —— 持有用户信息运营副本,需同步变更
|
||||||
|
- **模块 C(轻量引用)**:2 字段 —— 仅存储用户标识,不需要同步
|
||||||
|
|
||||||
|
有了这份评估,变更方案一目了然:先改主表,再同步副本,轻量引用模块无需操作。
|
||||||
|
|
||||||
|
### 第七步:全局分类统计
|
||||||
|
|
||||||
|
**对 AI 说:**
|
||||||
|
|
||||||
|
> "按表的命名规则将所有表分类,统计每个模块中各类表的数量和占比"
|
||||||
|
|
||||||
|
AI 的分类统计结果告诉我们一个关键事实:
|
||||||
|
|
||||||
|
| 分类 | 识别规则 | 大致占比 |
|
||||||
|
|------|----------|----------|
|
||||||
|
| 核心业务表 | 业务主实体及扩展 | ~5% |
|
||||||
|
| 关联映射表 | _rel 等关系表 | ~6% |
|
||||||
|
| 历史/日志表 | _his/_log/_record 结尾 | ~12% |
|
||||||
|
| 运维辅助表 | 调度/监控/临时/通知类 | ~9% |
|
||||||
|
| 其他业务表 | 非以上规则 | ~68% |
|
||||||
|
|
||||||
|
真正需要重点关注的业务核心表占比不到 5%。接近 70% 的表属于"其他业务表"——它们是日常业务运行的表,虽然不是实体核心但承载了大量业务逻辑。12% 是历史日志表,说明系统重视审计追溯。
|
||||||
|
|
||||||
|
这个分类的最大价值在于:接手一个新模块时,不用从海量表中找重点。先把那 5% 的核心实体表搞懂,再根据业务需求逐步深入。
|
||||||
|
|
||||||
|
### 这个分析流程的可复用性
|
||||||
|
|
||||||
|
以上七步不是一次性分析。它是一个**可重复的模板**。
|
||||||
|
|
||||||
|
当你接手一个新系统、或者现有的系统做了大版本升级后,把数据模型文件重新喂给 AI,跑一遍这七步,就能快速建立对系统数据架构的全局认知。过去需要两三天人工梳理的事情,现在几十分钟就能完成。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
两个场景的底层逻辑一致:**将结构化数据提供给 AI → AI 完成聚合、比对、关联分析 → 人工判断决策。** AI 负责"算",人负责"懂"。
|
||||||
|
|
||||||
|
### 工具与门槛
|
||||||
|
|
||||||
|
不需要昂贵的企业级产品。日常使用的工具组合:
|
||||||
|
|
||||||
|
- **[Zed](https://zed.dev)** — 高性能代码编辑器,内置 AI 面板,支持与多种大模型直接对话
|
||||||
|
- **[DeepSeek V4](https://chat.deepseek.com)** — 大语言模型,理解力强,支持超长上下文
|
||||||
|
- **[Reasonix](https://reasonix.ai)** — AI 编码助手,提供文件读写、目录浏览、内容搜索、Shell 执行等工具能力
|
||||||
|
|
||||||
|
三者配合:Zed 作为交互界面,DeepSeek 作为推理引擎,Reasonix 作为执行层(操作文件、跑脚本、搜目录)。全部基于现有工具,零额外采购成本。
|
||||||
|
|
||||||
|
关键在于思维转换:不把 AI 仅仅当作问答工具,而是作为能帮助翻阅材料、统计数据、整理提纲的工作助手。只需要明确"让它做什么",具体的执行过程由它自行完成。
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "AI 生产力的唯一落点"
|
title: "AI 生产力现状"
|
||||||
date: 2026-05-17T18:10:00+08:00
|
date: 2026-02-27T18:10:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
poet: |
|
poet: |
|
||||||
We are stuck with technology when what we really want is just stuff that works.
|
We are stuck with technology when what we really want is just stuff that works.
|
||||||
|
|||||||
@@ -1,54 +0,0 @@
|
|||||||
---
|
|
||||||
title: "2026 年 AI 工具链实用观察"
|
|
||||||
date: 2026-05-12T21:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
The mind is its own place, and in itself
|
|
||||||
Can make a Heaven of Hell, a Hell of Heaven.
|
|
||||||
— John Milton, UK, 1608–1674
|
|
||||||
description: "2026 年 AI 工具进入务实阶段。实测 DeepSeek V4 Flash 和 Pro 模型的不同场景表现,Agent 智能体的四个落地方向,以及怎么用更少 token 做更多事。"
|
|
||||||
tags:
|
|
||||||
- AI
|
|
||||||
- 工具
|
|
||||||
- DeepSeek
|
|
||||||
---
|
|
||||||
|
|
||||||
2026 年的 AI 工具生态可以用一个词形容——**务实**。经过前两年的概念爆发,现在已经进入落地阶段。
|
|
||||||
|
|
||||||
## DeepSeek 实测
|
|
||||||
|
|
||||||
最近深度体验了 DeepSeek V4 系列模型,几点感受:
|
|
||||||
|
|
||||||
### V4-Flash
|
|
||||||
日常使用首选。响应快、性价比高,适合:
|
|
||||||
- 代码辅助和 debug
|
|
||||||
- 文档撰写和润色
|
|
||||||
- 信息检索和整理
|
|
||||||
- 日常问答
|
|
||||||
|
|
||||||
### V4-Pro
|
|
||||||
复杂场景下表现出色,适合:
|
|
||||||
- 长文分析和总结
|
|
||||||
- 多步推理任务
|
|
||||||
- 代码架构设计
|
|
||||||
- 领域专业知识问答
|
|
||||||
|
|
||||||
账户余额 ¥74.31,按目前用量够跑一阵子。
|
|
||||||
|
|
||||||
## 智能体(Agent)趋势
|
|
||||||
|
|
||||||
今年 Agent 是主题词。从单纯对话到能独立执行任务的智能体,有几个方向值得关注:
|
|
||||||
|
|
||||||
1. **编码 Agent** — 从写代码到提 PR,全流程自动化
|
|
||||||
2. **文档 Agent** — 自动生成周报、会议纪要、汇报材料
|
|
||||||
3. **运维 Agent** — 日志分析、异常告警、故障排查
|
|
||||||
4. **个人助理 Agent** — 日程管理、信息聚合、任务委派
|
|
||||||
|
|
||||||
## 怎么用更划算
|
|
||||||
|
|
||||||
- 大部分日常任务用 Flash 就行
|
|
||||||
- 复杂任务才上 Pro,避免资源浪费
|
|
||||||
- 善用上下文缓存,减少重复 token 消耗
|
|
||||||
- 配合工具链(MCP、Function Calling)发挥更大价值
|
|
||||||
|
|
||||||
AI 工具的价值不在于它能做什么,而在于你用它做了什么。
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "子弹日记三年记:极简记录的力量"
|
title: "子弹日记三年记:极简记录的力量"
|
||||||
date: 2026-05-12T22:00:00+08:00
|
date: 2025-10-22T22:00:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
poet: |
|
poet: |
|
||||||
纸上得来终觉浅,
|
纸上得来终觉浅,
|
||||||
|
|||||||
@@ -1,101 +0,0 @@
|
|||||||
---
|
|
||||||
title: "CI/CD Patterns with GitHub Actions"
|
|
||||||
date: 2026-05-15T09:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
大音希声,大象无形。
|
|
||||||
——老子,中国,约前571–约前471
|
|
||||||
description: "Build matrices, caching strategies, environment-specific deployments, and reusable workflows. Patterns that scale from solo projects to team repos."
|
|
||||||
tags:
|
|
||||||
- CI/CD
|
|
||||||
- GitHub
|
|
||||||
- DevOps
|
|
||||||
---
|
|
||||||
|
|
||||||
GitHub Actions is now the default CI for most open-source projects. Build matrices save config lines, caching `node_modules` and Go modules saves minutes per run. Reusable workflows keep things DRY across repos.
|
|
||||||
|
|
||||||
## Build Matrices
|
|
||||||
|
|
||||||
Don't copy-paste job definitions for different Node versions or OS targets. Use a matrix:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node: [18, 20, 22]
|
|
||||||
os: [ubuntu-latest, macos-latest]
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/setup-node@v4
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node }}
|
|
||||||
- run: npm test
|
|
||||||
```
|
|
||||||
|
|
||||||
This single definition expands to 6 parallel jobs. Add `fail-fast: false` if you want all runs to complete even when one fails — helpful for catching platform-specific bugs in one CI run.
|
|
||||||
|
|
||||||
## Smart Caching
|
|
||||||
|
|
||||||
Caching is the easiest way to cut CI minutes:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
path: ~/.npm
|
|
||||||
key: npm-${{ runner.os }}-${{ hashFiles('package-lock.json') }}
|
|
||||||
restore-keys: npm-${{ runner.os }}-
|
|
||||||
```
|
|
||||||
|
|
||||||
The `restore-keys` fallback is important: even a partial cache hit saves time. For Go modules, cache `~/go/pkg/mod`. For Docker builds, use BuildKit's registry cache (`--cache-from type=registry`) for layer caching across CI runs.
|
|
||||||
|
|
||||||
## Environment-Specific Deployments
|
|
||||||
|
|
||||||
Use environments to gate production deploys:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
deploy-prod:
|
|
||||||
needs: [test, build]
|
|
||||||
environment: production
|
|
||||||
steps:
|
|
||||||
- run: ./deploy.sh production
|
|
||||||
```
|
|
||||||
|
|
||||||
The `environment: production` lets you set required reviewers, wait timers, and environment-specific secrets in the GitHub UI. No more accidentally deploying to prod from a feature branch.
|
|
||||||
|
|
||||||
## Reusable Workflows
|
|
||||||
|
|
||||||
When you maintain multiple repos, reusable workflows prevent drift:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# .github/workflows/deploy.yml in a shared repo
|
|
||||||
on:
|
|
||||||
workflow_call:
|
|
||||||
inputs:
|
|
||||||
environment:
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
secrets:
|
|
||||||
AWS_ROLE:
|
|
||||||
required: true
|
|
||||||
```
|
|
||||||
|
|
||||||
Call it from any repo:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
uses: org/shared-workflows/.github/workflows/deploy.yml@main
|
|
||||||
with:
|
|
||||||
environment: staging
|
|
||||||
secrets:
|
|
||||||
AWS_ROLE: ${{ secrets.AWS_ROLE }}
|
|
||||||
```
|
|
||||||
|
|
||||||
Update the shared workflow once, and every repo benefits. This pattern scales well for orgs with 10+ services.
|
|
||||||
|
|
||||||
## Practical Tips
|
|
||||||
|
|
||||||
- Set `timeout-minutes` on every job — the default is 360 minutes, and you don't want a hung test burning your quota.
|
|
||||||
- Use `concurrency` to cancel redundant runs when pushing to the same PR multiple times.
|
|
||||||
- Pin action versions to SHA hashes for supply-chain security, not just tags.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
title: "数据库索引优化实战"
|
|
||||||
date: 2026-05-10T16:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
众里寻他千百度,
|
|
||||||
蓦然回首,那人却在,灯火阑珊处。
|
|
||||||
——辛弃疾,中国,1140–1207
|
|
||||||
description: "慢查询分析、联合索引的最左前缀原则、覆盖索引与回表的性能差异。几个真实 SQL 优化案例,从几秒降到毫秒级。"
|
|
||||||
tags:
|
|
||||||
- 数据库
|
|
||||||
- SQL
|
|
||||||
- 性能
|
|
||||||
---
|
|
||||||
|
|
||||||
`EXPLAIN` 是 DBA 的眼睛。看 `type` 字段从 `ALL` 变成 `ref` 是最有成就感的事。
|
|
||||||
|
|
||||||
## 从慢查询开始
|
|
||||||
|
|
||||||
一切优化从定位问题开始。打开 MySQL 的慢查询日志(`slow_query_log`),设置 `long_query_time = 0.1`,跑一段时间后分析。PostgreSQL 用 `pg_stat_statements` 扩展,同样有效。
|
|
||||||
|
|
||||||
拿到慢查询,先 `EXPLAIN` 看执行计划。关注几个关键字段:
|
|
||||||
|
|
||||||
| 字段 | 含义 | 目标 |
|
|
||||||
|------|------|------|
|
|
||||||
| type | 访问类型 | 至少到 range,最好到 ref/const |
|
|
||||||
| key | 使用的索引 | 不为 NULL |
|
|
||||||
| rows | 扫描行数 | 越小越好 |
|
|
||||||
| Extra | 额外信息 | 避免 Using filesort / Using temporary |
|
|
||||||
|
|
||||||
## 最左前缀原则
|
|
||||||
|
|
||||||
联合索引是最容易出错的地方。假设索引是 `(a, b, c)`:
|
|
||||||
|
|
||||||
- `WHERE a = 1` ✅ 用到索引
|
|
||||||
- `WHERE a = 1 AND b = 2` ✅ 用到索引
|
|
||||||
- `WHERE b = 2` ❌ 用不到
|
|
||||||
- `WHERE a = 1 AND c = 3` ⚠️ 只用到了 a 列
|
|
||||||
|
|
||||||
排序也受这个规则约束。`ORDER BY a, b` 没问题,`ORDER BY b, c` 就需要额外排序。
|
|
||||||
|
|
||||||
## 覆盖索引
|
|
||||||
|
|
||||||
如果查询需要的所有列都在索引里,就不需要回表取数据。`Extra` 列显示 `Using index` 就是覆盖索引。对一个高频查询,覆盖索引能减少大量随机 I/O。
|
|
||||||
|
|
||||||
代价是索引变大了。不要为了覆盖索引把所有列都塞进索引——算一笔写入性能和存储空间的账。
|
|
||||||
|
|
||||||
## 真实案例
|
|
||||||
|
|
||||||
某个订单列表查询,原始 SQL 跑了 8 秒。`EXPLAIN` 一看,`type: ALL`,全表扫描 200 万行。加了 `(user_id, created_at)` 联合索引后,`type: ref`,扫描 20 行,耗时 12ms。
|
|
||||||
|
|
||||||
**索引不是越多越好**——每个索引都会拖慢写入速度并占用磁盘空间。建索引前问自己:这个查询的执行频率值得一个索引吗?
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Go Concurrency Patterns in Practice"
|
|
||||||
date: 2026-05-15T10:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
To see a World in a Grain of Sand
|
|
||||||
And a Heaven in a Wild Flower
|
|
||||||
— William Blake, UK, 1757–1827
|
|
||||||
description: "Go concurrency beyond the basics. Worker pools for bounded parallelism, context propagation for cancellation, select loops for channel coordination — three patterns that hold up in production."
|
|
||||||
tags:
|
|
||||||
- Go
|
|
||||||
- Concurrency
|
|
||||||
- Programming
|
|
||||||
---
|
|
||||||
|
|
||||||
I've been writing Go for a few years now, and concurrency remains one of those topics where textbook knowledge doesn't quite prepare you for production. Here are a few patterns I've found genuinely useful.
|
|
||||||
|
|
||||||
## The Worker Pool
|
|
||||||
|
|
||||||
The classic. Useful when you need bounded parallelism:
|
|
||||||
|
|
||||||
```go
|
|
||||||
func workerPool(jobs <-chan Job, results chan<- Result, count int) {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
for i := 0; i < count; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
for job := range jobs {
|
|
||||||
results <- process(job)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
wg.Wait()
|
|
||||||
close(results)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Context Propagation
|
|
||||||
|
|
||||||
Probably the single most important practice I've adopted: **every long-running goroutine should accept a context**. It makes cancellation and timeout handling explicit rather than an afterthought.
|
|
||||||
|
|
||||||
```go
|
|
||||||
func fetchWithTimeout(ctx context.Context, url string) ([]byte, error) {
|
|
||||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## The Select Loop
|
|
||||||
|
|
||||||
When you need to coordinate multiple channels, `select` is your friend. A common pattern:
|
|
||||||
|
|
||||||
```go
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case msg := <-messages:
|
|
||||||
handle(msg)
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
case <-ticker.C:
|
|
||||||
flush()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Things I Wish I Knew Earlier
|
|
||||||
|
|
||||||
1. **Channel ownership** — the goroutine that writes should also close the channel
|
|
||||||
2. **Buffered channels aren't a fix for deadlocks** — they just push the problem further out
|
|
||||||
3. **`sync.WaitGroup` copies by value** — pass it by pointer, always
|
|
||||||
4. **The race detector is free** — `go test -race` should be part of your CI
|
|
||||||
|
|
||||||
Concurrency in Go is *easy to write* but *hard to get right*. The compiler won't save you from logical races. That's on us.
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
---
|
|
||||||
title: "家庭网络与 NAS 搭建方案"
|
|
||||||
date: 2026-05-17T15:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
此心安处是吾乡。
|
|
||||||
——苏轼,中国,1037–1101
|
|
||||||
description: "软路由、Wi-Fi 6 AP、自建 NAS 的硬件选型和软件方案。Plex 媒体服务器 + Syncthing 文件同步 + Tailscale 远程访问,一套低成本高品质的家庭网络实践。"
|
|
||||||
tags:
|
|
||||||
- NAS
|
|
||||||
- 网络
|
|
||||||
- 硬件
|
|
||||||
---
|
|
||||||
|
|
||||||
折腾家庭网络的终点是克制。和 Linux 桌面美化一样,容易用力过猛。本文记录一套低成本但够用的家庭网络方案。
|
|
||||||
|
|
||||||
## 网络拓扑
|
|
||||||
|
|
||||||
```
|
|
||||||
光猫 → 软路由(OpenWrt)→ 交换机
|
|
||||||
├── Wi-Fi 6 AP ×2
|
|
||||||
├── NAS(TrueNAS)
|
|
||||||
└── 有线设备
|
|
||||||
```
|
|
||||||
|
|
||||||
软路由选 J4125 或 N100 这类低功耗 x86,刷 OpenWrt,负责拨号、DHCP、科学上网。AP 选 Wi-Fi 6 的,TP-Link XDR 系列性价比不错,刷 OpenWrt 或者用原厂固件都可以。
|
|
||||||
|
|
||||||
两个 AP 组 Mesh,不需要折腾 AC 控制器。家里 120 平米基本无死角覆盖。
|
|
||||||
|
|
||||||
## NAS 硬件与系统
|
|
||||||
|
|
||||||
旧电脑加几块硬盘就是一台 NAS。电费要算:不要拿老 i7 加独显当 NAS,待机功耗四五十瓦,一个月电费够买半块硬盘。
|
|
||||||
|
|
||||||
推荐方案:J4125 或 N100 小板 + 8GB 内存 + 2~4 块 4TB 硬盘组 RAID-Z1。系统选 TrueNAS Scale,基于 Linux,容器和虚拟机支持比 Core 版更好。待机功耗 15W 左右,一年电费不到一百块。
|
|
||||||
|
|
||||||
## 软件方案
|
|
||||||
|
|
||||||
三个核心应用:
|
|
||||||
|
|
||||||
| 软件 | 用途 | 替代方案 |
|
|
||||||
|------|------|----------|
|
|
||||||
| **Plex** | 媒体服务器,自动刮削海报和元数据 | Jellyfin(开源免费) |
|
|
||||||
| **Syncthing** | 文件同步,多设备自动备份照片和文档 | Resilio Sync |
|
|
||||||
| **Tailscale** | 虚拟局域网,外面也能访问家里的服务 | ZeroTier、WireGuard 裸配 |
|
|
||||||
|
|
||||||
Plex 装在 TrueNAS 的 App 里,显卡直通给 Plex 做硬件转码——手机上用较低码率看片时自动转码,画质损失不大。Syncthing 每台设备装一个,照片目录设为"仅发送",电脑上的文档文件夹设为"双向同步"。
|
|
||||||
|
|
||||||
Tailscale 是真正的体验质变。不需要公网 IP,不需要端口转发,不需要记 IP 地址。手机、笔记本、NAS 在同一个虚拟局域网里,出门在外访问 Plex 和 NAS 文件就像在家一样。免费版支持最多 100 台设备,个人用完全够。
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
方案的核心思路:**够用即可**。不要想着一步到位,先从旧硬件开始跑起来,哪里不够补哪里。家庭网络的价值在于稳定无声地跑着,不是折腾本身。
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Linux 桌面美化指北"
|
|
||||||
date: 2026-05-11T10:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
My house, my house, though you are small,
|
|
||||||
You are a mansion to me after all.
|
|
||||||
— Robert Herrick, UK, 1591–1674
|
|
||||||
description: "从默认 Gnome 到一套顺手的桌面环境。GTK 主题、图标包、终端配色、窗口管理器的选择与配置思路。不追求花哨,追求每天盯着看不累。"
|
|
||||||
tags:
|
|
||||||
- Linux
|
|
||||||
- 桌面
|
|
||||||
- 效率
|
|
||||||
---
|
|
||||||
|
|
||||||
Linux 桌面环境的选择很多,但真正好用的配置往往很简单。本文记录从 Ubuntu 默认桌面开始,逐步调整到一套耐看、高效的配置。
|
|
||||||
|
|
||||||
## 主题选择
|
|
||||||
|
|
||||||
白天用亮色主题降低反光,晚上切暗色保护眼睛。推荐 **Nord** 和 **Catppuccin** 两套配色——前者冷峻克制,后者柔和温暖,每天盯着看八小时不累。
|
|
||||||
|
|
||||||
GTK 主题建议选择维护活跃的项目,避免 Gnome 大版本升级后样式错乱。Qt 应用在 Gnome 下可以用 `qt5ct` 统一风格。
|
|
||||||
|
|
||||||
## 字体配置
|
|
||||||
|
|
||||||
终端字体一定要等宽且清晰。推荐 **Fira Code** 做编程字体,连字(ligatures)让 `=>`、`!=`、`>=` 这类符号组合更易读。中文字体用 **Noto Sans CJK**,和西文搭配自然。
|
|
||||||
|
|
||||||
## 终端与 Shell
|
|
||||||
|
|
||||||
终端模拟器推荐 **Alacritty**——GPU 加速,配置文件清晰。配合 zsh + **starship** 提示符,日常使用效率提升不少。Starship 的优势在于跨 shell 兼容,换到 fish 或 nu 不用重新配置。
|
|
||||||
|
|
||||||
## 窗口管理
|
|
||||||
|
|
||||||
Gnome 默认的三指滑动切换工作区已经很好用。如果追求更极致的键盘操作,可以试试 **Pop Shell** 平铺扩展,或者直接上 **i3/sway**。但对大多数人来说,Gnome 的默认交互已经够用。
|
|
||||||
|
|
||||||
关键原则:**不追求花哨,追求每天盯着看不累。**
|
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Hugo 站点中嵌入视频与图片的最佳实践"
|
|
||||||
date: 2026-05-16T12:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
一花一世界,
|
|
||||||
一叶一菩提。
|
|
||||||
——《华严经》
|
|
||||||
description: "静态站点不等于只能放文字。B 站视频的自适应嵌入方案、图片懒加载与响应式处理、以及如何在 Hugo 里保持多媒体内容的可维护性。"
|
|
||||||
---
|
|
||||||
|
|
||||||
静态博客常被认为只能承载纯文本,但实际上 Hugo 对多媒体内容的支持相当完整。本文记录本站的视频嵌入和图片排版方案,供有类似需求的读者参考。
|
|
||||||
|
|
||||||
## B 站视频嵌入
|
|
||||||
|
|
||||||
Hugo 的 `unsafe: true` 配置允许直接插入 `<iframe>`,这让嵌入第三方播放器变得简单。下面是本站文章中使用的 B 站视频嵌入代码:
|
|
||||||
|
|
||||||
<div style="position: relative; padding-bottom: 75%; height: 0; overflow: hidden; margin: 1.5rem 0;">
|
|
||||||
<iframe
|
|
||||||
src="//player.bilibili.com/player.html?bvid=BV1AWRVB2EAz&page=1&high_quality=1&autoplay=0"
|
|
||||||
scrolling="no"
|
|
||||||
border="0"
|
|
||||||
frameborder="no"
|
|
||||||
framespacing="0"
|
|
||||||
allowfullscreen="true"
|
|
||||||
style="position: absolute; top: 0; left: 0; width: 100%; height: 100%;">
|
|
||||||
</iframe>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
几个关键参数:`high_quality=1` 默认启用高清画质,`autoplay=0` 禁用自动播放(避免对读者造成打扰)。外层容器的 `padding-bottom: 75%` 是 4:3 比例,如果视频是 16:9 改成 `56.25%` 即可。
|
|
||||||
|
|
||||||
## 图片排版实践
|
|
||||||
|
|
||||||
### 宽幅配图
|
|
||||||
|
|
||||||
对于需要烘托氛围的宽幅图片,直接使用 Markdown 默认语法即可,Hugo 会自动处理响应式缩放:
|
|
||||||
|
|
||||||

|
|
||||||
{{< caption >}}宽幅配图适合放在文章开头,作为视觉呼吸点。{{< /caption >}}
|
|
||||||
|
|
||||||
这类图片适合放在文章开头或章节之间作为视觉呼吸点。
|
|
||||||
|
|
||||||
### 多图并列
|
|
||||||
|
|
||||||
需要对比展示时,可以将图片连续排列。配合 Hugo 的渲染引擎,相邻图片会自动获得合适的间距:
|
|
||||||
|
|
||||||

|
|
||||||
{{< caption >}}编程与思考。{{< /caption >}}
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
### 配图居中
|
|
||||||
|
|
||||||
适量使用图片比堆砌更能提升阅读体验。一张位置恰当的配图可以给段落之间留出自然的停顿:
|
|
||||||
|
|
||||||

|
|
||||||
{{< caption >}}一张位置恰当的配图,给段落之间留出自然的停顿。{{< /caption >}}
|
|
||||||
|
|
||||||
## 多媒体文件的组织
|
|
||||||
|
|
||||||
本站将所有图片和字体统一放入 `static/` 目录,引用时使用绝对路径 `/images/xxx.jpg`。这样做的好处是路径清晰、不受页面层级影响,也方便后续迁移到 CDN。
|
|
||||||
|
|
||||||
如果你也在用 Hugo 搭建个人站点,多媒体内容的处理原则就一条:**路径统一、格式克制、体积有数**。祝搭建愉快。
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Mermaid 图表语法示例"
|
|
||||||
date: 2026-05-19T10:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
description: "Mermaid 官方语法示例合集:流程图、时序图、类图、状态图、甘特图、饼图、Git 图等全部图表示范。"
|
|
||||||
tags:
|
|
||||||
- Mermaid
|
|
||||||
- 图表
|
|
||||||
- 语法
|
|
||||||
poet: |
|
|
||||||
大音希声,大象无形。
|
|
||||||
——老子,中国,约前571–约前471
|
|
||||||
---
|
|
||||||
|
|
||||||
> 原文:[Mermaid Examples](https://mermaid.js.org/syntax/examples.html)
|
|
||||||
|
|
||||||
# Examples
|
|
||||||
|
|
||||||
This page contains a collection of examples of diagrams and charts that can be created through mermaid and its myriad applications.
|
|
||||||
|
|
||||||
**If you wish to learn how to support mermaid on your webpage, read the [Beginner's Guide](../config/usage.md?id=usage).**
|
|
||||||
|
|
||||||
**If you wish to learn about mermaid's syntax, Read the [Diagram Syntax](../syntax/flowchart.md?id=flowcharts-basic-syntax) section.**
|
|
||||||
|
|
||||||
## Basic Pie Chart
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
pie title NETFLIX
|
|
||||||
"Time spent looking for movie" : 90
|
|
||||||
"Time spent watching it" : 10
|
|
||||||
```
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
pie title What Voldemort doesn't have?
|
|
||||||
"FRIENDS" : 2
|
|
||||||
"FAMILY" : 3
|
|
||||||
"NOSE" : 45
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic sequence diagram
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
Alice ->> Bob: Hello Bob, how are you?
|
|
||||||
Bob-->>John: How about you John?
|
|
||||||
Bob--x Alice: I am good thanks!
|
|
||||||
Bob-x John: I am good thanks!
|
|
||||||
Note right of John: Bob thinks a long<br/>long time, so long<br/>that the text does<br/>not fit on a row.
|
|
||||||
|
|
||||||
Bob-->Alice: Checking with John...
|
|
||||||
Alice->John: Yes... John, how are you?
|
|
||||||
```
|
|
||||||
|
|
||||||
## Basic flowchart
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph LR
|
|
||||||
A[Square Rect] -- Link text --> B((Circle))
|
|
||||||
A --> C(Round Rect)
|
|
||||||
B --> D{Rhombus}
|
|
||||||
C --> D
|
|
||||||
```
|
|
||||||
|
|
||||||
## SequenceDiagram: Loops, alt and opt
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
loop Daily query
|
|
||||||
Alice->>Bob: Hello Bob, how are you?
|
|
||||||
alt is sick
|
|
||||||
Bob->>Alice: Not so good :(
|
|
||||||
else is well
|
|
||||||
Bob->>Alice: Feeling fresh like a daisy
|
|
||||||
end
|
|
||||||
|
|
||||||
opt Extra response
|
|
||||||
Bob->>Alice: Thanks for asking
|
|
||||||
end
|
|
||||||
end
|
|
||||||
```
|
|
||||||
|
|
||||||
## SequenceDiagram: Message to self in loop
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant Alice
|
|
||||||
participant Bob
|
|
||||||
Alice->>John: Hello John, how are you?
|
|
||||||
loop HealthCheck
|
|
||||||
John->>John: Fight against hypochondria
|
|
||||||
end
|
|
||||||
Note right of John: Rational thoughts<br/>prevail...
|
|
||||||
John-->>Alice: Great!
|
|
||||||
John->>Bob: How about you?
|
|
||||||
Bob-->>John: Jolly good!
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sequence Diagram: Blogging app service communication
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
participant web as Web Browser
|
|
||||||
participant blog as Blog Service
|
|
||||||
participant account as Account Service
|
|
||||||
participant mail as Mail Service
|
|
||||||
participant db as Storage
|
|
||||||
|
|
||||||
Note over web,db: The user must be logged in to submit blog posts
|
|
||||||
web->>+account: Logs in using credentials
|
|
||||||
account->>db: Query stored accounts
|
|
||||||
db->>account: Respond with query result
|
|
||||||
|
|
||||||
alt Credentials not found
|
|
||||||
account->>web: Invalid credentials
|
|
||||||
else Credentials found
|
|
||||||
account->>-web: Successfully logged in
|
|
||||||
|
|
||||||
Note over web,db: When the user is authenticated, they can now submit new posts
|
|
||||||
web->>+blog: Submit new post
|
|
||||||
blog->>db: Store post data
|
|
||||||
|
|
||||||
par Notifications
|
|
||||||
blog--)mail: Send mail to blog subscribers
|
|
||||||
blog--)db: Store in-site notifications
|
|
||||||
and Response
|
|
||||||
blog-->>-web: Successfully posted
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
## A commit flow diagram.
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
gitGraph:
|
|
||||||
commit "Ashish"
|
|
||||||
branch newbranch
|
|
||||||
checkout newbranch
|
|
||||||
commit id:"1111"
|
|
||||||
commit tag:"test"
|
|
||||||
checkout main
|
|
||||||
commit type: HIGHLIGHT
|
|
||||||
commit
|
|
||||||
merge newbranch
|
|
||||||
commit
|
|
||||||
branch b2
|
|
||||||
commit
|
|
||||||
```
|
|
||||||
|
|
||||||
<!--- cspell:ignore Ashish newbranch --->
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "极简主义的实用指南:少即是多"
|
title: "极简主义的实用指南:少即是多"
|
||||||
date: 2026-05-12T17:00:00+08:00
|
date: 2025-12-05T17:00:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
description: "极简不等于空无一物。从手机关通知、统一信息入口、减少收藏即学到,到物品的一年法则和精神层面的少承诺、少比较、少内耗,一套可立刻上手的实用方法。"
|
description: "极简不等于空无一物。从手机关通知、统一信息入口、减少收藏即学到,到物品的一年法则和精神层面的少承诺、少比较、少内耗,一套可立刻上手的实用方法。"
|
||||||
poet: "驾驶教练机拉杆起飞的那一刻,宇治城的小溪边被斜阳刺痛双眼的那一刻,在广汕国道上骑自行车听到路旁大爷大喊加油的那一刻,我清楚的感受到我是我自己"
|
poet: "驾驶教练机拉杆起飞的那一刻,宇治城的小溪边被斜阳刺痛双眼的那一刻,在广汕国道上骑自行车听到路旁大爷大喊加油的那一刻,我清楚的感受到我是我自己"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "用宝可梦解释 Prolog 基础"
|
title: "用宝可梦解释 Prolog 基础"
|
||||||
date: 2026-05-18T13:30:00+08:00
|
date: 2026-01-18T13:30:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
description: "通过宝可梦对战系统理解逻辑编程语言 Prolog 的核心概念——事实、规则、查询,以及它为什么比 SQL 和电子表格更灵活。"
|
description: "通过宝可梦对战系统理解逻辑编程语言 Prolog 的核心概念——事实、规则、查询,以及它为什么比 SQL 和电子表格更灵活。"
|
||||||
tags:
|
tags:
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Python Async: Beyond the Basics"
|
|
||||||
date: 2026-05-12T11:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
They also serve who only stand and wait.
|
|
||||||
— John Milton, UK, 1608–1674
|
|
||||||
description: "Understanding the event loop, writing proper async context managers, and avoiding the most common pitfalls with asyncio.gather and cancel scopes."
|
|
||||||
tags:
|
|
||||||
- Python
|
|
||||||
- Async
|
|
||||||
- Programming
|
|
||||||
---
|
|
||||||
|
|
||||||
Async Python is no longer optional if you're doing any kind of I/O-bound work. Beyond the basics of `async`/`await`, understanding the event loop helps debug elusive issues.
|
|
||||||
|
|
||||||
## The Event Loop in Practice
|
|
||||||
|
|
||||||
The event loop runs one coroutine at a time, switching at `await` points. This is cooperative multitasking — a coroutine that never awaits blocks everything else. If you have CPU-bound work inside an async function, offload it to a thread pool with `asyncio.to_thread()`.
|
|
||||||
|
|
||||||
```python
|
|
||||||
import asyncio
|
|
||||||
|
|
||||||
# Bad: blocks the event loop
|
|
||||||
async def bad():
|
|
||||||
result = heavy_computation() # CPU-bound, no await
|
|
||||||
return result
|
|
||||||
|
|
||||||
# Good: offloads to a thread
|
|
||||||
async def good():
|
|
||||||
result = await asyncio.to_thread(heavy_computation)
|
|
||||||
return result
|
|
||||||
```
|
|
||||||
|
|
||||||
## Async Context Managers
|
|
||||||
|
|
||||||
Writing proper async context managers is essential for managing connections and locks:
|
|
||||||
|
|
||||||
```python
|
|
||||||
class AsyncConnection:
|
|
||||||
async def __aenter__(self):
|
|
||||||
self.conn = await create_connection()
|
|
||||||
return self.conn
|
|
||||||
|
|
||||||
async def __aexit__(self, *args):
|
|
||||||
await self.conn.close()
|
|
||||||
```
|
|
||||||
|
|
||||||
The `contextlib.asynccontextmanager` decorator makes this even cleaner for simple cases.
|
|
||||||
|
|
||||||
## Common Pitfalls with `asyncio.gather`
|
|
||||||
|
|
||||||
`gather` runs tasks concurrently, but one failing task doesn't cancel the others by default. If you need all-or-nothing semantics, use `asyncio.TaskGroup` (Python 3.11+):
|
|
||||||
|
|
||||||
```python
|
|
||||||
async with asyncio.TaskGroup() as tg:
|
|
||||||
t1 = tg.create_task(fetch(url1))
|
|
||||||
t2 = tg.create_task(fetch(url2))
|
|
||||||
# Both succeeded, or both were cancelled
|
|
||||||
```
|
|
||||||
|
|
||||||
## Cancel Scopes
|
|
||||||
|
|
||||||
Cancellation in asyncio is cooperative: `asyncio.CancelledError` is raised at the next `await`. If your coroutine catches and suppresses it, the task becomes uncancellable — usually a bug. Use `asyncio.shield()` sparingly to protect critical cleanup code.
|
|
||||||
|
|
||||||
## Structured Concurrency
|
|
||||||
|
|
||||||
The trend across languages (Trio in Python, Kotlin coroutines, Swift concurrency) is toward structured concurrency: every task has a clear parent scope that determines its lifetime. `TaskGroup` is Python's step in this direction. Embrace it over bare `create_task()` calls.
|
|
||||||
|
|
||||||
Async Python has matured significantly. Most of the old footguns have been addressed, but the fundamentals — understand the event loop, respect cancellation, and structure your concurrency — remain essential.
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
title: "2026 上半年阅读清单与推荐"
|
|
||||||
date: 2026-05-12T16:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
书卷多情似故人,
|
|
||||||
晨昏忧乐每相亲。
|
|
||||||
——于谦,中国,1398–1457
|
|
||||||
description: "上半年读过值得推荐的几本书。《软件设计的哲学》讲复杂度管理,《深度工作》讲专注力训练,《凤凰项目》用小说讲 DevOps。附我的阅读节奏安排。"
|
|
||||||
tags:
|
|
||||||
- 阅读
|
|
||||||
- 书单
|
|
||||||
- 推荐
|
|
||||||
---
|
|
||||||
|
|
||||||
上半年读了十几本书,挑几本值得推荐的记一下。
|
|
||||||
|
|
||||||
## 技术类
|
|
||||||
|
|
||||||
### 《软件设计的哲学》
|
|
||||||
不是讲设计模式的,而是讲**复杂度管理**。核心观点:软件设计的本质是降低复杂度。写得深入浅出,适合有几年经验想突破瓶颈的开发者。
|
|
||||||
|
|
||||||
### 《凤凰项目》
|
|
||||||
用小说的形式讲 DevOps 转型。IT 运维的日常在书里无比真实,读起来很有代入感。
|
|
||||||
|
|
||||||
## 效率类
|
|
||||||
|
|
||||||
### 《深度工作》
|
|
||||||
Cal Newport 的代表作。核心论点:在这个随时被打断的时代,深度专注的能力越来越稀缺,也越来越有价值。
|
|
||||||
|
|
||||||
书里给了一些具体的训练方法,比如:
|
|
||||||
- 安排固定的深度工作时间段
|
|
||||||
- 社交媒体断舍离
|
|
||||||
- 用仪式感帮助进入状态
|
|
||||||
|
|
||||||
## 人文类
|
|
||||||
|
|
||||||
### 《你以为你以为的就是你以为的吗?》
|
|
||||||
一本有意思的思维训练书,帮助识别日常思考中的逻辑漏洞。适合碎片时间翻几页。
|
|
||||||
|
|
||||||
## 阅读习惯
|
|
||||||
|
|
||||||
今年调整了阅读方式:
|
|
||||||
|
|
||||||
- **早上的时间给深度阅读** — 30 分钟,不碰手机
|
|
||||||
- **通勤时间给信息类阅读** — RSS、Newsletter、技术文章
|
|
||||||
- **周末下午给整本书** — 泡杯茶,一次读一两章
|
|
||||||
|
|
||||||
读书不求多,读完、消化、能用上,一本顶十本。
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
---
|
|
||||||
title: "REST API Design: Mistakes I've Made So You Don't Have To"
|
|
||||||
date: 2026-05-13T09:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
Form follows function.
|
|
||||||
— Louis Sullivan, US, 1856–1924
|
|
||||||
description: "Lessons from real-world API design. Why version early, paginate everything, standardize error responses, and add idempotency keys. Plus a practical deprecation strategy with Sunset headers."
|
|
||||||
tags:
|
|
||||||
- API
|
|
||||||
- REST
|
|
||||||
- Backend
|
|
||||||
---
|
|
||||||
|
|
||||||
I've designed, built, and maintained enough REST APIs to have made most of the classic mistakes. Here's what I wish someone had told me upfront.
|
|
||||||
|
|
||||||
## 1. Versioning Early
|
|
||||||
|
|
||||||
Don't wait until you need it. Version your API from day one, either in the URL (`/v1/users`) or via the `Accept` header. Retrofitting versioning onto an unversioned API is painful for everyone involved.
|
|
||||||
|
|
||||||
My preference: **URL-based versioning**. It's visible, impossible to forget, and trivially cacheable.
|
|
||||||
|
|
||||||
## 2. Pagination by Default
|
|
||||||
|
|
||||||
Every list endpoint should be paginated. Every. Single. One.
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"data": [...],
|
|
||||||
"pagination": {
|
|
||||||
"cursor": "eyJpZCI6MTIzfQ==",
|
|
||||||
"has_more": true,
|
|
||||||
"total": 847
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
I've moved from offset-based to cursor-based pagination almost everywhere. Cursors handle real-time data changes gracefully and perform better on large datasets.
|
|
||||||
|
|
||||||
## 3. Error Responses Should Be Consistent
|
|
||||||
|
|
||||||
A predictable error shape means less client-side code:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error": {
|
|
||||||
"code": "INSUFFICIENT_BALANCE",
|
|
||||||
"message": "Account balance too low for this transaction",
|
|
||||||
"details": {
|
|
||||||
"required": 29.99,
|
|
||||||
"available": 12.50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Never expose stack traces in production. Never leak internal IDs without intent.
|
|
||||||
|
|
||||||
## 4. Idempotency Keys
|
|
||||||
|
|
||||||
For any mutating endpoint, support an `Idempotency-Key` header. Your payment team will thank you, and so will your users when a retry doesn't double-charge them.
|
|
||||||
|
|
||||||
## 5. Think in Terms of Deprecation
|
|
||||||
|
|
||||||
Add a `Deprecation` and `Sunset` header to old endpoints:
|
|
||||||
|
|
||||||
```
|
|
||||||
Deprecation: true
|
|
||||||
Sunset: Sat, 01 Aug 2026 00:00:00 GMT
|
|
||||||
```
|
|
||||||
|
|
||||||
Give consumers time to migrate, then actually follow through on removal. Keeping deprecated endpoints around "just in case" is technical debt with interest.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Good API design is mostly about **empathy for the consumer**. Ask yourself: would *you* enjoy integrating with this?
|
|
||||||
@@ -1,99 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Rust Error Handling: From unwrap to anyhow"
|
|
||||||
date: 2026-05-16T13:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
If you can meet with Triumph and Disaster
|
|
||||||
And treat those two impostors just the same;
|
|
||||||
— Rudyard Kipling, UK, 1865–1936
|
|
||||||
description: "The journey from panic-driven development to proper error types. When to use thiserror for library crates and anyhow for applications."
|
|
||||||
tags:
|
|
||||||
- Rust
|
|
||||||
- Error Handling
|
|
||||||
- Programming
|
|
||||||
---
|
|
||||||
|
|
||||||
Rust forces you to think about errors. Starting with `unwrap()` everywhere is fine for prototypes, but production code needs proper error types.
|
|
||||||
|
|
||||||
## Stage 1: The Unwrap Phase
|
|
||||||
|
|
||||||
```rust
|
|
||||||
let config = read_config().unwrap();
|
|
||||||
let db = connect(&config).unwrap();
|
|
||||||
let result = query(&db).unwrap();
|
|
||||||
```
|
|
||||||
|
|
||||||
Every Rust beginner goes through this. It's fine for scripts and prototypes — if something fails, the panic message with a line number is good enough. The problem starts when you need graceful error handling or meaningful error messages for users.
|
|
||||||
|
|
||||||
## Stage 2: Box<dyn Error>
|
|
||||||
|
|
||||||
The quick upgrade path:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn do_thing() -> Result<(), Box<dyn std::error::Error>> {
|
|
||||||
let config = read_config()?;
|
|
||||||
let db = connect(&config)?;
|
|
||||||
query(&db)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `?` operator works with any error type that implements `From<T>`. `Box<dyn Error>` catches everything. But you lose type information — the caller can't match on specific error variants.
|
|
||||||
|
|
||||||
## Stage 3: Proper Error Types with thiserror
|
|
||||||
|
|
||||||
For libraries, define explicit error types:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use thiserror::Error;
|
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
|
||||||
pub enum AppError {
|
|
||||||
#[error("configuration error: {0}")]
|
|
||||||
Config(String),
|
|
||||||
#[error("database error: {0}")]
|
|
||||||
Database(#[from] sqlx::Error),
|
|
||||||
#[error("not found: {0}")]
|
|
||||||
NotFound(String),
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The `#[from]` attribute auto-implements `From<T>`, so `?` works seamlessly. Callers can match on variants:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
match do_thing().await {
|
|
||||||
Err(AppError::NotFound(id)) => { /* handle 404 */ }
|
|
||||||
Err(e) => { /* log and return 500 */ }
|
|
||||||
Ok(data) => { /* success */ }
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Stage 4: Application Errors with anyhow
|
|
||||||
|
|
||||||
For binaries, `anyhow` provides flexible error handling without the ceremony:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
|
||||||
let config = read_config()
|
|
||||||
.with_context(|| "failed to read config file")?;
|
|
||||||
let db = connect(&config)
|
|
||||||
.context("database connection failed")?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`context()` and `with_context()` attach human-readable messages at each fallible step. The error output is a chain of contexts, making debugging straightforward.
|
|
||||||
|
|
||||||
## The Ecosystem Split
|
|
||||||
|
|
||||||
| Crate | Use Case | Key Feature |
|
|
||||||
|-------|----------|-------------|
|
|
||||||
| `thiserror` | Library crates | Derive macro, typed errors |
|
|
||||||
| `anyhow` | Application binaries | Context chains, easy `?` |
|
|
||||||
| `eyre` | Applications (alternative) | Colorful error reports, custom hooks |
|
|
||||||
|
|
||||||
The ecosystem has settled on this pragmatic split: **thiserror for libraries, anyhow for binaries**. Libraries expose typed errors so callers can react programmatically; applications benefit from rich, human-readable error chains.
|
|
||||||
|
|
||||||
Remember: `unwrap()` and `expect()` still have their place — in tests, in examples, and when an invariant violation truly merits a panic. But for production code paths, make errors part of your type signature.
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "从零搭建个人博客:Hugo + PaperMod 实践记录"
|
title: "从零搭建个人博客:Hugo + PaperMod 实践记录"
|
||||||
date: 2026-05-12T20:00:00+08:00
|
date: 2025-09-08T20:00:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
poet: |
|
poet: |
|
||||||
千门万户曈曈日,
|
千门万户曈曈日,
|
||||||
|
|||||||
@@ -1,286 +0,0 @@
|
|||||||
---
|
|
||||||
title: "域名折腾记录"
|
|
||||||
date: 2026-05-21T15:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
description: "基于腾讯云的域名申请、工信部 ICP 备案、SSL 证书部署、Nginx HTTPS 配置、公安备案的全流程实操记录。"
|
|
||||||
tags:
|
|
||||||
- 教程
|
|
||||||
- 建站
|
|
||||||
- Nginx
|
|
||||||
- HTTPS
|
|
||||||
poet: |
|
|
||||||
千门万户曈曈日,
|
|
||||||
总把新桃换旧符。
|
|
||||||
——王安石,中国,1021–1086
|
|
||||||
---
|
|
||||||
|
|
||||||
把个人网站从 `http://IP` 变成一个正经的 `https://域名`,中间要走的路比想象中多。这篇文章记录我的实际操作过程,供有同样需求的朋友参考。
|
|
||||||
|
|
||||||
## 前提
|
|
||||||
|
|
||||||
- 服务器:腾讯云 Lighthouse,Ubuntu 24.04,4 核 4G
|
|
||||||
- 网站:Hugo 静态博客,Nginx 提供服务
|
|
||||||
- 域名注册商:腾讯云
|
|
||||||
|
|
||||||
## 一、域名申请
|
|
||||||
|
|
||||||
**控制台路径**:腾讯云控制台 → 产品 → 域名注册 → 域名注册
|
|
||||||
|
|
||||||
在腾讯云域名注册页面搜索想要的域名,`.com` 早就被抢光了,`.net` 还有一些。我选了 `fukun.net`,首年 90 元。
|
|
||||||
|
|
||||||
购买后进入 **控制台 → 产品 → 域名注册 → 我的域名**,找到刚买的域名,点击「实名认证」。上传身份证正反面,审核大约半小时到两小时。通过后域名状态变为「正常」。
|
|
||||||
|
|
||||||
回到我的域名页面,点击域名进入 **域名管理 → DNS 解析 → 添加记录**:
|
|
||||||
|
|
||||||
| 主机记录 | 记录类型 | 记录值 |
|
|
||||||
|---------|---------|--------|
|
|
||||||
| @ | A | 62.234.90.54 |
|
|
||||||
| www | A | 62.234.90.54 |
|
|
||||||
|
|
||||||
TTL 默认 600(即 10 分钟)即可。关于 TTL 的设置经验:
|
|
||||||
|
|
||||||
- **日常使用**:600 足够。DNS 修改后最迟 10 分钟全球生效,实际通常更快。
|
|
||||||
- **迁移前夕**:如果计划更换服务器 IP,提前 24 小时把 TTL 降到 60–120,这样迁移时 DNS 切换更快。
|
|
||||||
- **不要设太短**:TTL 过短(如 10 秒)会增加 DNS 查询量,对个人站点没必要。
|
|
||||||
- **不要设太长**:TTL 过长(如 86400)会导致修改后一天才能生效,出问题时无法快速切换。
|
|
||||||
|
|
||||||
生效很快,几分钟后 `ping fukun.net` 就能看到正确 IP。
|
|
||||||
|
|
||||||
## 二、工信部 ICP 备案
|
|
||||||
|
|
||||||
这是整个流程里最耗时的环节。根据《互联网信息服务管理办法》,中国大陆境内提供互联网信息服务的网站必须取得 ICP 备案号。
|
|
||||||
|
|
||||||
**控制台路径**:腾讯云控制台 → 产品 → 网站备案 → 开始备案
|
|
||||||
|
|
||||||
### 操作流程
|
|
||||||
|
|
||||||
1. 进入备案系统,选择「首次备案」
|
|
||||||
2. 填写主体信息(个人姓名、身份证号、详细通信地址)
|
|
||||||
3. 填写网站信息:
|
|
||||||
- 网站名称:注意不能包含「中国」「中华」等字样,个人备案也不能用太过商业化的名称(我填了「FuKun 技术笔记」)
|
|
||||||
- 网站域名:`fukun.net`
|
|
||||||
- 网站简介:如实描述,不要写「论坛」「商城」等需要前置审批的内容
|
|
||||||
4. 上传证件照片(身份证正反面、手持身份证照)
|
|
||||||
5. 进行人脸识别验证(微信扫码完成)
|
|
||||||
6. 提交初审
|
|
||||||
|
|
||||||
### 时间线
|
|
||||||
|
|
||||||
| 阶段 | 耗时 | 说明 |
|
|
||||||
|------|------|------|
|
|
||||||
| 腾讯云初审 | 1 个工作日 | 审核材料完整性,不合格会退回修改 |
|
|
||||||
| 提交管局 | 3 个工作日 | 初审通过后提交至通信管理局 |
|
|
||||||
| 工信部短信核验 | 即时 | 收到短信后 24 小时内点击链接确认 |
|
|
||||||
| 通信管理局审核 | 10–15 个工作日 | 各地管局速度不同,广东大约 12 天 |
|
|
||||||
|
|
||||||
我从提交到拿到备案号大约用了 **12 个工作日**。通过后会在 **控制台 → 网站备案 → 我的备案** 看到备案号和电子证书。
|
|
||||||
|
|
||||||
### 关键注意事项
|
|
||||||
|
|
||||||
- 备案期间网站**必须关闭**(Nginx 先别配域名,或者返回 403)
|
|
||||||
- 个人备案不得涉及企业、商品、新闻等内容
|
|
||||||
- 备案号需要在网站底部展示,链接到 `beian.miit.gov.cn`
|
|
||||||
- 备案通过后 30 天内需完成公安备案
|
|
||||||
|
|
||||||
## 三、SSL 证书申请
|
|
||||||
|
|
||||||
备案通过后域名可以解析了,此时网站还是 HTTP。下一步申请 SSL 证书开启 HTTPS。
|
|
||||||
|
|
||||||
**控制台路径**:腾讯云控制台 → 产品 → SSL 证书 → 我的证书 → 申请免费证书
|
|
||||||
|
|
||||||
我使用的是腾讯云「SSL 单域名证书(一年期)」,TrustAsia DV 品牌,RSA 2048 位加密,SHA256 签名算法。证书覆盖主域名 `fukun.net` 和 `www.fukun.net`。
|
|
||||||
|
|
||||||
证书链:`fukun.net` → TrustAsia DV TLS RSA CA 2025 → DigiCert Global Root G2。
|
|
||||||
|
|
||||||
### 操作步骤
|
|
||||||
|
|
||||||
1. 在 SSL 证书控制台点击「申请免费证书」(或购买对应类型的证书,¥64.6/年)
|
|
||||||
2. 填写申请信息:
|
|
||||||
- 证书绑定域名:`fukun.net`
|
|
||||||
- 选择「自动添加 DNS 验证记录」— 腾讯云会自动添加一条 TXT 记录
|
|
||||||
3. 提交申请,等待验证通过(通常 1–5 分钟)
|
|
||||||
4. 验证通过后状态变为「已签发」,点击「下载」
|
|
||||||
5. 选择「Nginx」格式下载,得到一个 zip 包
|
|
||||||
|
|
||||||
解压后得到四个文件:
|
|
||||||
|
|
||||||
| 文件 | 用途 |
|
|
||||||
|------|------|
|
|
||||||
| `fukun.net.csr` | 证书请求文件,申请时自动生成,部署不需要 |
|
|
||||||
| `fukun.net.key` | 私钥,Nginx ssl_certificate_key 使用 |
|
|
||||||
| `fukun.net_bundle.crt` | CRT 格式证书,含中间证书,Nginx 使用 |
|
|
||||||
| `fukun.net_bundle.pem` | PEM 格式证书,与 CRT 同内容,备用 |
|
|
||||||
|
|
||||||
## 四、Nginx SSL 配置
|
|
||||||
|
|
||||||
### 上传证书到服务器
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 将证书文件传到服务器
|
|
||||||
scp fukun.net.key fukun.net_bundle.crt ubuntu@62.234.90.54:~/
|
|
||||||
|
|
||||||
# SSH 登录服务器
|
|
||||||
ssh ubuntu@62.234.90.54
|
|
||||||
|
|
||||||
# 移动到 Nginx SSL 目录并设置权限
|
|
||||||
sudo mv ~/fukun.net.key ~/fukun.net_bundle.crt /etc/nginx/ssl/
|
|
||||||
sudo chown root:root /etc/nginx/ssl/fukun.net*
|
|
||||||
sudo chmod 600 /etc/nginx/ssl/fukun.net.key
|
|
||||||
```
|
|
||||||
|
|
||||||
### Nginx 站点配置
|
|
||||||
|
|
||||||
编辑 `/etc/nginx/sites-available/hugo-blog`:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
# HTTP → HTTPS 重定向
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name fukun.net www.fukun.net;
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
# HTTPS 站点
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name fukun.net www.fukun.net;
|
|
||||||
|
|
||||||
ssl_certificate /etc/nginx/ssl/fukun.net_bundle.crt;
|
|
||||||
ssl_certificate_key /etc/nginx/ssl/fukun.net.key;
|
|
||||||
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
ssl_session_cache shared:SSL:10m;
|
|
||||||
ssl_session_timeout 10m;
|
|
||||||
|
|
||||||
root /home/hugo/hugo-blog/public;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ =404;
|
|
||||||
}
|
|
||||||
|
|
||||||
# 静态资源缓存
|
|
||||||
location ~* \.(css|js|jpg|jpeg|png|gif|ico|svg|webp)$ {
|
|
||||||
expires 30d;
|
|
||||||
add_header Cache-Control "public, immutable";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 启用站点
|
|
||||||
sudo ln -sf /etc/nginx/sites-available/hugo-blog /etc/nginx/sites-enabled/
|
|
||||||
|
|
||||||
# 测试配置
|
|
||||||
sudo nginx -t
|
|
||||||
|
|
||||||
# 重载 Nginx
|
|
||||||
sudo systemctl reload nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
## 五、备案号悬挂
|
|
||||||
|
|
||||||
工信部要求备案号须挂在网站首页底部。在 Hugo 的 `hugo.yaml` 配置:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
params:
|
|
||||||
footer:
|
|
||||||
icp: "粤ICP备2026061516号"
|
|
||||||
```
|
|
||||||
|
|
||||||
在布局文件 `layouts/_partials/footer.html` 中渲染:
|
|
||||||
|
|
||||||
```go-html-template
|
|
||||||
{{- with .Site.Params.footer.icp }}
|
|
||||||
<div class="hx:mt-2 hx:text-xs">
|
|
||||||
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer">
|
|
||||||
{{ . }}
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
{{- end }}
|
|
||||||
```
|
|
||||||
|
|
||||||
## 六、公安部网站备案
|
|
||||||
|
|
||||||
ICP 备案通过后 30 天内,还需进行公安机关互联网站备案。注意需要先完成主体备案,再完成网站备案,两步都要做。
|
|
||||||
|
|
||||||
**网站**:www.beian.gov.cn → 全国公安机关互联网站安全服务平台
|
|
||||||
|
|
||||||
**第一步:主体备案**
|
|
||||||
|
|
||||||
1. 注册账号并登录
|
|
||||||
2. 进入「办事大厅」→「开办主体管理」→「新增主体」
|
|
||||||
3. 填写主体信息(个人姓名、身份证号、地址、联系方式等)
|
|
||||||
4. 提交审核,通过后可进行网站备案
|
|
||||||
|
|
||||||
**第二步:网站备案**
|
|
||||||
|
|
||||||
1. 进入「办事大厅」→「新办网站申请」
|
|
||||||
2. 填写网站信息:网站名称、域名、IP 地址、服务器所在地
|
|
||||||
3. 选择网络接入服务商:腾讯云
|
|
||||||
4. 提交审核(约 7–10 个工作日)
|
|
||||||
5. 审核通过后获取公安备案号,格式为「粤公网安备 XXXXXXX 号」
|
|
||||||
|
|
||||||
公安备案号也需要悬挂在网站上(通常放在 ICP 备案号旁边)。
|
|
||||||
|
|
||||||
## 七、新增二级域名
|
|
||||||
|
|
||||||
网站上线后如需新增二级域名(如 `gallery.fukun.net`),二级域名与主域名共用同一个 ICP 备案号,公安备案同理,不需要重复办理。只需要做好DNS解析、证书申请、Nginx配置。
|
|
||||||
|
|
||||||
**DNS 解析**:在域名管理 → DNS 解析中新增 A 记录,主机记录填 `gallery`,指向同一 IP。
|
|
||||||
|
|
||||||
**SSL 证书**:单域名证书覆盖不到新二级域名,需要为新二级域名单独申请一张(流程同第三章,约 5 分钟)。
|
|
||||||
|
|
||||||
**Nginx 配置**:新增站点文件 `/etc/nginx/sites-available/gallery`:
|
|
||||||
|
|
||||||
```nginx
|
|
||||||
server {
|
|
||||||
listen 443 ssl http2;
|
|
||||||
server_name gallery.fukun.net;
|
|
||||||
|
|
||||||
ssl_certificate /etc/nginx/ssl/gallery_bundle.crt;
|
|
||||||
ssl_certificate_key /etc/nginx/ssl/gallery.key;
|
|
||||||
|
|
||||||
ssl_protocols TLSv1.2 TLSv1.3;
|
|
||||||
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
|
|
||||||
ssl_prefer_server_ciphers on;
|
|
||||||
|
|
||||||
root /home/hugo/blog-site/public;
|
|
||||||
index index.html;
|
|
||||||
|
|
||||||
location / {
|
|
||||||
try_files $uri $uri/ =404;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# HTTP 跳转 HTTPS
|
|
||||||
server {
|
|
||||||
listen 80;
|
|
||||||
server_name gallery.fukun.net;
|
|
||||||
return 301 https://$host$request_uri;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
启用并重载:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo ln -sf /etc/nginx/sites-available/gallery /etc/nginx/sites-enabled/
|
|
||||||
sudo nginx -t
|
|
||||||
sudo systemctl reload nginx
|
|
||||||
```
|
|
||||||
|
|
||||||
| 事项 | 耗时 | 费用 | 控制台路径 |
|
|
||||||
|------|------|------|-----------|
|
|
||||||
| 云服务器 | 1 天 | ¥99/月 | 控制台 → Lighthouse |
|
|
||||||
| 域名注册 | — | ¥90/年 | 控制台 → 域名注册 |
|
|
||||||
| 实名认证 | — | — | 控制台 → 域名注册 → 我的域名 |
|
|
||||||
| DNS 解析 | — | — | 域名管理 → DNS 解析 |
|
|
||||||
| ICP 备案 | 10–15 工作日 | — | 控制台 → 网站备案 |
|
|
||||||
| SSL 证书 | — | ¥64.6/年 | 控制台 → SSL 证书 → 我的证书 |
|
|
||||||
| CDN 加速 | — | ¥14/年 | 控制台 → CDN |
|
|
||||||
| 云硬盘扩容 | — | ¥70 | 控制台 → Lighthouse → 云硬盘 |
|
|
||||||
| Nginx 配置 | — | — | 无 |
|
|
||||||
| 公安备案 | 7–10 工作日 | — | beian.gov.cn |
|
|
||||||
| **合计** | **约 25–33 天** | **¥337.6** | |
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
title: "SSH 隧道实用指南"
|
title: "SSH 隧道实用指南"
|
||||||
date: 2026-05-09T14:00:00+08:00
|
date: 2025-07-15T14:00:00+08:00
|
||||||
draft: false
|
draft: false
|
||||||
poet: |
|
poet: |
|
||||||
山重水复疑无路,
|
山重水复疑无路,
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Svelte 初体验:不一样的思路"
|
|
||||||
date: 2026-05-10T09:30:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
Do not go gentle into that good night.
|
|
||||||
Rage, rage against the dying of the light.
|
|
||||||
— Dylan Thomas, UK, 1914–1953
|
|
||||||
description: "试用了 Svelte 5 的 runes 语法和 SvelteKit 框架。没有虚拟 DOM,编译时直接把组件转成 DOM 操作代码,打包体积小得惊人。"
|
|
||||||
tags:
|
|
||||||
- Svelte
|
|
||||||
- 前端
|
|
||||||
- JavaScript
|
|
||||||
---
|
|
||||||
|
|
||||||
用惯了 React 和 Vue,第一次接触 Svelte 的时候确实有种新鲜感。最直观的感受:**代码量少了很多**。
|
|
||||||
|
|
||||||
## Runes 语法
|
|
||||||
|
|
||||||
Svelte 5 引入了 runes 语法,让响应式声明更显式:
|
|
||||||
|
|
||||||
- **`$state`** — 声明响应式变量,替代了之前靠赋值触发响应式的隐式规则。
|
|
||||||
- **`$derived`** — 衍生值,类似 Vue 的 computed。
|
|
||||||
- **`$effect`** — 副作用,自动追踪依赖并在变化时执行。
|
|
||||||
|
|
||||||
```svelte
|
|
||||||
<script>
|
|
||||||
let count = $state(0);
|
|
||||||
let doubled = $derived(count * 2);
|
|
||||||
|
|
||||||
$effect(() => {
|
|
||||||
console.log(`count is now ${count}`);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
```
|
|
||||||
|
|
||||||
逻辑清晰,没有 `useEffect` 的依赖数组心智负担。
|
|
||||||
|
|
||||||
## 编译时魔法
|
|
||||||
|
|
||||||
Svelte 的核心理念:没有虚拟 DOM,编译器在构建时直接把组件转成高效的 DOM 操作代码。打包体积小得惊人,一个简单的组件可能只有几百字节。对移动端场景尤其友好。
|
|
||||||
|
|
||||||
## SvelteKit
|
|
||||||
|
|
||||||
SvelteKit 是 Svelte 的全栈框架,文件路由设计简洁。约定优于配置——新建一个 `src/routes/about/+page.svelte`,就自动有了 `/about` 路由。内置 SSR、SSG、CSR 多种渲染模式,服务器端数据加载用 `load` 函数,类型安全做得不错。
|
|
||||||
|
|
||||||
## 总结
|
|
||||||
|
|
||||||
Svelte 适合想要减少样板代码、追求小打包体积的项目。React 和 Vue 的生态更成熟,但 Svelte 的思路值得关注。下一步打算用 SvelteKit 重写个人博客试试看。
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Migrating to Tailwind CSS v4"
|
|
||||||
date: 2026-05-13T08:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
苟日新,日日新,又日新。
|
|
||||||
——《礼记·大学》,中国
|
|
||||||
description: "CSS-first configuration, the new @theme directive, container queries, and what actually changed from v3. A practical migration log."
|
|
||||||
tags:
|
|
||||||
- CSS
|
|
||||||
- Tailwind
|
|
||||||
- Frontend
|
|
||||||
---
|
|
||||||
|
|
||||||
Tailwind v4 drops the JavaScript config file in favor of CSS-based configuration. The migration is mostly mechanical but there are gotchas with the `@apply` directive and custom variants.
|
|
||||||
|
|
||||||
## CSS-First Configuration
|
|
||||||
|
|
||||||
The biggest change: `tailwind.config.js` is replaced by CSS. Your design tokens live in the `@theme` directive:
|
|
||||||
|
|
||||||
```css
|
|
||||||
@import "tailwindcss";
|
|
||||||
|
|
||||||
@theme {
|
|
||||||
--color-primary: #2563eb;
|
|
||||||
--color-primary-dark: #1d4ed8;
|
|
||||||
--font-sans: "Inter", sans-serif;
|
|
||||||
--spacing-container: 1280px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
VSCode IntelliSense still works — the Tailwind CSS extension reads theme values from your stylesheet.
|
|
||||||
|
|
||||||
## The Migration Path
|
|
||||||
|
|
||||||
1. Install v4: `npm install tailwindcss@next`
|
|
||||||
2. Run the automatic migration tool: `npx @tailwindcss/upgrade`
|
|
||||||
3. Manually check `@apply` usages — the tool handles most cases, but deeply nested `@apply` with custom utilities sometimes breaks.
|
|
||||||
4. Replace `theme.extend` in your old config with `@theme` blocks in CSS.
|
|
||||||
|
|
||||||
## New Features Worth Using
|
|
||||||
|
|
||||||
### Container Queries
|
|
||||||
|
|
||||||
No more media queries for component-level responsiveness. Tailwind v4 ships container query utilities:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="@container">
|
|
||||||
<div class="grid @lg:grid-cols-2">...</div>
|
|
||||||
</div>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Dynamic Utility Values
|
|
||||||
|
|
||||||
Arbitrary values are simpler: `w-(--sidebar-width)` references a CSS custom property. No more bracket syntax for common cases.
|
|
||||||
|
|
||||||
### Native Cascade Layers
|
|
||||||
|
|
||||||
Tailwind v4 uses `@layer` to control specificity. Base, components, and utilities are layered in the correct order — `@apply` in component layer no longer risks specificity wars.
|
|
||||||
|
|
||||||
## Gotchas
|
|
||||||
|
|
||||||
- Custom variants defined in old JS config need to be re-registered via `@variant` in CSS.
|
|
||||||
- The `important` config option now needs an explicit CSS strategy.
|
|
||||||
- Some plugins haven't updated yet — check compatibility before migrating production projects.
|
|
||||||
|
|
||||||
Overall, the migration took about two hours for a mid-sized project. The CSS-first approach feels more natural, and the build is noticeably faster thanks to the new Oxide engine.
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Deploying to AWS ECS with Terraform"
|
|
||||||
date: 2026-05-14T10:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
If you have built castles in the air,
|
|
||||||
your work need not be lost;
|
|
||||||
that is where they should be.
|
|
||||||
Now put the foundations under them.
|
|
||||||
— Henry David Thoreau, US, 1817–1862
|
|
||||||
description: "Infrastructure as code from scratch: VPC, subnets, security groups, ECR repository, ECS service with Fargate, and an ALB in front. Every resource explained."
|
|
||||||
tags:
|
|
||||||
- AWS
|
|
||||||
- Terraform
|
|
||||||
- DevOps
|
|
||||||
---
|
|
||||||
|
|
||||||
ECS with Fargate is the sweet spot between managing servers and going full serverless. Terraform makes it reproducible.
|
|
||||||
|
|
||||||
## The Stack
|
|
||||||
|
|
||||||
Here's what we need to provision:
|
|
||||||
|
|
||||||
| Resource | Purpose |
|
|
||||||
|----------|---------|
|
|
||||||
| VPC | Isolated network |
|
|
||||||
| Public & private subnets | Multi-AZ placement |
|
|
||||||
| Security groups | Network access control |
|
|
||||||
| ECR repository | Container image storage |
|
|
||||||
| ECS cluster + service | Run containers |
|
|
||||||
| ALB + target group | Traffic distribution |
|
|
||||||
|
|
||||||
## Security Groups: Getting Them Right
|
|
||||||
|
|
||||||
The key is getting the security groups right — one misconfigured rule and your containers can't talk to each other, or worse, your database is exposed to the internet.
|
|
||||||
|
|
||||||
```hcl
|
|
||||||
resource "aws_security_group" "ecs_service" {
|
|
||||||
name = "ecs-service-sg"
|
|
||||||
vpc_id = aws_vpc.main.id
|
|
||||||
|
|
||||||
ingress {
|
|
||||||
from_port = 3000
|
|
||||||
to_port = 3000
|
|
||||||
protocol = "tcp"
|
|
||||||
security_groups = [aws_security_group.alb.id]
|
|
||||||
}
|
|
||||||
|
|
||||||
egress {
|
|
||||||
from_port = 0
|
|
||||||
to_port = 0
|
|
||||||
protocol = "-1"
|
|
||||||
cidr_blocks = ["0.0.0.0/0"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
The principle: **allow the minimum**. The ALB security group allows inbound from the internet on 80/443. The ECS security group only allows inbound from the ALB. The database security group only allows inbound from ECS.
|
|
||||||
|
|
||||||
## Fargate Configuration
|
|
||||||
|
|
||||||
Fargate abstracts the EC2 instances. You specify CPU and memory, and AWS handles the rest. For most web services, 0.25 vCPU / 512 MB is a good starting point — scale up based on load testing, not guessing.
|
|
||||||
|
|
||||||
## CI/CD Integration
|
|
||||||
|
|
||||||
After Terraform creates the infrastructure, your CI pipeline needs to:
|
|
||||||
|
|
||||||
1. Build and tag the Docker image
|
|
||||||
2. Push to ECR
|
|
||||||
3. Update the ECS service with the new task definition
|
|
||||||
|
|
||||||
Use a Terraform backend with state locking — S3 + DynamoDB is the standard approach. Without state locking, two people running `terraform apply` at the same time can corrupt your infrastructure state.
|
|
||||||
|
|
||||||
## Cost Notes
|
|
||||||
|
|
||||||
Fargate is more expensive per vCPU-hour than raw EC2, but you save the operational cost of patching instances and managing capacity. For small-to-medium workloads, the difference is negligible compared to the engineering time saved.
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
---
|
|
||||||
title: "我的效率工具链 2026"
|
|
||||||
date: 2026-05-12T18:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
磨刀不误砍柴工。
|
|
||||||
——中国谚语,中国
|
|
||||||
description: "折腾多年终于稳定下来的工具链。子弹日记做规划,Todoist 管任务,DeepSeek V4 当主力 AI,VS Code 写代码。选工具的原则就四个字:够用就好。"
|
|
||||||
tags:
|
|
||||||
- 工具
|
|
||||||
- 效率
|
|
||||||
- 推荐
|
|
||||||
---
|
|
||||||
|
|
||||||
持续折腾工具多年,今年终于稳定下来。记录一下目前的工具链,给想优化工作流的朋友参考。
|
|
||||||
|
|
||||||
## 笔记与知识管理
|
|
||||||
|
|
||||||
**子弹日记(纸笔)** — 每日规划和自我对话的核心载体。具体用法之前写过,不再重复。
|
|
||||||
|
|
||||||
**Hugo + Hextra** — 个人博客,用于对外输出。纯静态,零维护成本。
|
|
||||||
|
|
||||||
**GitHub** — 技术笔记和代码片段的存档。Markdown 文件 + 搜索,比任何笔记软件都好用。
|
|
||||||
|
|
||||||
## 任务管理
|
|
||||||
|
|
||||||
**Todoist** — 短期任务和提醒工具。优点是快速、跨平台、自然语言识别能力强。「今天要做的事」用它就够了。
|
|
||||||
|
|
||||||
## AI 工具
|
|
||||||
|
|
||||||
**DeepSeek V4** — 主力 AI 助手。Flash 模型日常用,Pro 模型处理复杂任务。API 接入各种场景。
|
|
||||||
|
|
||||||
## 开发相关
|
|
||||||
|
|
||||||
- **VS Code** — 主力编辑器
|
|
||||||
- **Hugo** — 静态博客生成器
|
|
||||||
- **Nginx** — Web 服务器
|
|
||||||
- **Git** — 版本控制
|
|
||||||
|
|
||||||
## 选工具的原则
|
|
||||||
|
|
||||||
1. **够用就好** — 不追求功能最全,追求用得顺手
|
|
||||||
2. **数据可控** — 优先选能导出、能自托管的方案
|
|
||||||
3. **学习成本低** — 花在学工具上的时间要能赚回来
|
|
||||||
4. **跨平台** — 手机和电脑都能用
|
|
||||||
|
|
||||||
好工具的标准不是功能多,而是让你忘记工具的存在,专注于事情本身。
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
---
|
|
||||||
title: "Vim Motions That Actually Save Time"
|
|
||||||
date: 2026-05-14T14:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
Practice does not make perfect.
|
|
||||||
Only perfect practice makes perfect.
|
|
||||||
— Vince Lombardi, US, 1913–1970
|
|
||||||
description: "Which Vim motions are worth the muscle memory investment? A practical three-tier ranking from non-negotiable (ci, %, dot) to nice-to-have (macros, jump list), based on three years of daily use."
|
|
||||||
tags:
|
|
||||||
- Vim
|
|
||||||
- Productivity
|
|
||||||
- Editor
|
|
||||||
---
|
|
||||||
|
|
||||||
Everyone tells you to learn Vim, but nobody tells you *which* motions are worth the muscle memory investment. After three years of daily use, here's my honest ranking.
|
|
||||||
|
|
||||||
## Tier 1: Non-Negotiable
|
|
||||||
|
|
||||||
These pay for themselves within a week:
|
|
||||||
|
|
||||||
| Motion | What it does | Why it matters |
|
|
||||||
|--------|-------------|----------------|
|
|
||||||
| `ci"` | Change inside quotes | Editing strings, attributes |
|
|
||||||
| `ci(` / `ci{` | Change inside brackets | Refactoring arguments |
|
|
||||||
| `%` | Jump to matching bracket | Navigating code blocks |
|
|
||||||
| `*` / `#` | Search word under cursor | Quick variable tracing |
|
|
||||||
| `.` | Repeat last change | The single biggest time-saver |
|
|
||||||
|
|
||||||
## Tier 2: Worth Practicing
|
|
||||||
|
|
||||||
These take a few weeks to internalize but compound:
|
|
||||||
|
|
||||||
- `f{char}` / `F{char}` — find character inline. Faster than `h`/`l` spam
|
|
||||||
- `t{char}` / `T{char}` — find *until* character. Perfect for deleting `until` a comma
|
|
||||||
- `ctrl+d` / `ctrl+u` — half-page scroll. Keeps your eyes on the same spot
|
|
||||||
- `zz` — center current line. Eliminates mental reorientation
|
|
||||||
|
|
||||||
## Tier 3: Nice to Have
|
|
||||||
|
|
||||||
Useful but not essential for daily work:
|
|
||||||
|
|
||||||
- `gt` / `gT` — switch tabs
|
|
||||||
- `ctrl+o` / `ctrl+i` — jump back/forward in the jumplist
|
|
||||||
- Macros (`q{register}`) — only worth it for repetitive tasks of 10+ iterations
|
|
||||||
|
|
||||||
## What I Don't Use
|
|
||||||
|
|
||||||
- **Line numbers as targets** (`:42`) — too slow mentally
|
|
||||||
- **Visual block mode** — I use multi-cursor in VS Code instead
|
|
||||||
- **`/` search with regex** — I just use `*` and `f` 90% of the time
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
The real productivity gain isn't in any single motion — it's in **not thinking about navigation**. When your fingers just move the cursor without conscious effort, that's when the payoff arrives.
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
---
|
|
||||||
title: "VPS 新手入门:从购买到上线一个网站"
|
|
||||||
date: 2026-05-12T19:00:00+08:00
|
|
||||||
draft: false
|
|
||||||
poet: |
|
|
||||||
路漫漫其修远兮,
|
|
||||||
吾将上下而求索。
|
|
||||||
——屈原,中国,约前340–前278
|
|
||||||
description: "买了一台云服务器之后怎么让它跑起来。从 SSH 登录、创建普通用户、配置密钥认证、设置 ufw 防火墙到用 Nginx 部署静态网站,一套完整的新手流程。"
|
|
||||||
tags:
|
|
||||||
- Linux
|
|
||||||
- VPS
|
|
||||||
- 教程
|
|
||||||
---
|
|
||||||
|
|
||||||
买了一台云服务器之后,怎么让它真正跑起来?这篇文章记录基本的 VPS 初始化和安全配置流程。
|
|
||||||
|
|
||||||
## 购买后的第一件事
|
|
||||||
|
|
||||||
拿到服务器 IP 和 root 密码后:
|
|
||||||
|
|
||||||
### 1. SSH 登录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh root@你的服务器IP
|
|
||||||
```
|
|
||||||
|
|
||||||
第一次登录会提示确认主机指纹,输入 `yes` 即可。
|
|
||||||
|
|
||||||
### 2. 创建普通用户
|
|
||||||
|
|
||||||
root 权限太大,日常操作应该用普通用户:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
adduser 用户名
|
|
||||||
usermod -aG sudo 用户名
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. 配置 SSH 密钥登录
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 在本地机器上生成密钥
|
|
||||||
ssh-keygen -t ed25519
|
|
||||||
|
|
||||||
# 把公钥传到服务器
|
|
||||||
ssh-copy-id 用户名@服务器IP
|
|
||||||
```
|
|
||||||
|
|
||||||
然后编辑 `/etc/ssh/sshd_config`,关闭密码登录:
|
|
||||||
|
|
||||||
```
|
|
||||||
PasswordAuthentication no
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. 防火墙配置
|
|
||||||
|
|
||||||
用 ufw 简单配置:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ufw allow OpenSSH
|
|
||||||
ufw allow 80/tcp
|
|
||||||
ufw allow 443/tcp
|
|
||||||
ufw enable
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. 安装必要的软件
|
|
||||||
|
|
||||||
```bash
|
|
||||||
apt update && apt upgrade -y
|
|
||||||
apt install -y nginx curl wget git
|
|
||||||
```
|
|
||||||
|
|
||||||
## 部署静态网站
|
|
||||||
|
|
||||||
最简单的方式是用 Nginx:
|
|
||||||
|
|
||||||
1. 把构建好的静态文件放到 `/var/www/html` 或自定义目录
|
|
||||||
2. 配置 Nginx 站点文件指向该目录
|
|
||||||
3. 运行 `nginx -t && systemctl reload nginx`
|
|
||||||
|
|
||||||
## 安全底线
|
|
||||||
|
|
||||||
- 不要用 root 直接操作日常任务
|
|
||||||
- 关闭 SSH 密码登录
|
|
||||||
- 定期更新系统包
|
|
||||||
- 非必要不开放额外端口
|
|
||||||
|
|
||||||
服务器就像一间屋子,基础装修做好才能住得安心。
|
|
||||||
Reference in New Issue
Block a user