MoreRSS

site iconSunZhongWei | 孙仲维 修改

博客名「大象笔记」,全干程序员一名,曾在金山,DNSPod,腾讯云,常驻烟台。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

SunZhongWei | 孙仲维 的 RSS 预览

Golang Gin Static 缓存大坑:embed 文件无法被 CDN 和浏览器缓存

2025-12-06 10:36:17

Golang 最爽的一点是,可以打包时将静态文件 embed 进二进制文件中,部署时只需一个可执行文件,极大简化了部署流程。 例如,网站的 js 和 css 文件,可以 embed 进二进制文件中,而无需发布时再额外拷贝一堆静态文件。

Golang Gin Static Embed Files Code

但是,最近我发现了一个浏览器缓存问题!

😓 问题现象

今天早晨,我还没起床,突然想看看最新开发的一个网页版手机租金计算器是否能在微信里正常打开,主要是担心微信对某些 js 特性的支持不好。 结果发现,页面一片空白,但是刷新了几次就正常了。有点懵,打开阿里云 ESA 的请求日志,发现访问 css 文件时,每次都回源站请求。 其中一次源站没有响应,导致页面空白。这里存在两个问题:

  1. 为何阿里云 ESA 没有缓存静态资源文件,需要回源站请求?
  2. 为何浏览器每次都重新下载静态资源文件,而不是使用缓存?
  3. CSS 文件没有加载成功为何会导致整个页面空白?按理说只是样式混乱罢了。

🔍 对比排查

使用 Golang Gin 框架时,使用 Static 方法为嵌入的静态文件提供服务时,无法正确处理 HTTP 缓存响应头,导致浏览器每次都重新下载文件。 即便使用了 CDN 缓存,CDN 也无法正确缓存这些静态文件。

而且是只有 golang gin 开发的页面才有这个问题,其他 php laravel 开发的页面没有这个问题 (laravel 的静态文件是使用 Nginx 提供服务的)。 另一个海外站用的 cloudflare 也是同样的问题。

⚠ 没有被缓存的静态文件响应头

使用浏览器的开发者工具查看,注意要取消勾选“禁用缓存”选项,然后刷新页面,查看静态文件的响应头:

ali-swift-global-savetime: 1764985449
alt-svc: h3=":443"; ma=86400
content-encoding: zstd
content-type: text/css; charset=utf-8

date: Sat, 06 Dec 2025 01:44:09 GMT
eagleid: d364082217649854493746454e
server: ESA
timing-allow-origin: *
vary: accept-encoding
via: ens-cache69.l2cn7144[22,0,DP], ens-cache69.l2cn7144[23,22,200-0,M], ens-cache20.l2cn7144[24,0], ens-cache33.cn8004[36,0,DP], ens-cache33.cn8004[36,36,200-0,M], ens-cache14.cn8004[37,0]
x-site-cache-status: BYPASS  👀👀👀 (bypass 是绕过、避开的意思)
x-swift-cachetime: 0
x-swift-savetime: Sat, 06 Dec 2025 01:44:09 GMT

可以看到,响应头里没有任何和缓存相关的字段,例如 Cache-ControlETagLast-Modified 等等。

🎯 正常被缓存的静态文件响应头

而使用 Nginx 提供服务的静态文件,响应头如下:

accept-ranges: bytes
age: 1209
ali-swift-global-savetime: 1764984157
alt-svc: h3=":443"; ma=86400
content-length: 451   👀👀👀
content-type: text/css
date: Sat, 06 Dec 2025 01:22:37 GMT
eagleid: d36408db17649853667641854e
etag: "5c45ab02-1c3"  👀👀👀
last-modified: Mon, 21 Jan 2019 11:20:34 GMT  👀👀👀
server: ESA
timing-allow-origin: *
via: ens-cache84.l2cn7144[66,0,DP], ens-cache84.l2cn7144[0,0,304-0,H], ens-cache60.l2cn7144[1,0], ens-cache31.cn8004[16,0,DP], ens-cache31.cn8004[0,0,200-0,H], ens-cache32.cn8004[8,0]
x-site-cache-status: HIT  👀👀👀 (hit 是缓存命中的意思)
x-swift-cachetime: 3600
x-swift-savetime: Sat, 06 Dec 2025 01:42:09 GMT

可以看到,响应头里有 ETagLast-Modified 字段,浏览器和 CDN 都可以根据这些字段来判断文件是否被修改,从而决定是否使用缓存。

😅 问题出在哪里?

我一开始以为是 golang gin 的 static 方法的问题,但是查看 github 上的讨论,说 static 是支持缓存的。 于是我猜测就是 embed 嵌入文件导致的,因为嵌入的文件就没有本地时间戳的概念了,而且如果没有自动计算 etag,那么浏览器和 CDN 就无法判断文件是否被修改。

直接找到了 github 上 golang 项目的 issue:

官方讨论了几年了,还在讨论中。这是 2021 年的一个讨论:

embed: modification time not set for embedded files #44854

github.com/golang/go/issues/44854

When modules are downloaded, their files are stripped of all metadata including timestamps and permission bits. That means embedded files from a module don't have that information. The only case where it would be feasible is when the embedded files are part of the main module, but then a program's behavior would subtly depend on how it was built.

This was discussed a bit in the embed proposal (#41191) (expand all comments, search for "timestamp"). The discussion there seems to recommend hashing and ETag for cache invalidation. However, ETag wasn't implemented in embed.FS or io/fs, and the proposal (#43223) was declined. It sounds like that may be reopened at some point when there's a clearer picture of how to implement that.

然后 2023 年,又开始了新的讨论:

io/fs, net/http: define interface for automatic ETag serving #60940

github.com/golang/go/issues/60940

I believe net/http/fs.go is not directly related to the new io/fs.Hash interface, so http.ServeContent should not auto add an ETag. By default, it should rely on the Last-Modified header for 304 caching. If users need a custom ETag, they should set it manually.

In HTTP, 304 caching is validated based on time or hash values, using the commonly used headers Last-Modified and ETag.
For most static files, Last-Modified alone is sufficient to enable 304 caching, and ETag is not necessary.
For embed files, since the default Last-Modified value is zero, it can be set to the app startup time and sync across multiple deploy using an env variable.

If ETag is required, the hash can be computed and set before return the response via net/http:

w.Header().Set("Content-Type", "...") // If not set, http.ServeContent will auto-detect
w.Header().Set("Etag", computeEtagFromStat(stat)) // Pass ETag to http.ServeContent
http.ServeContent(w, r, stat.Name(), stat.ModTime(), file)

划重点:

For embed files, since the default Last-Modified value is zero

即,嵌入的文件默认的 Last-Modified 时间是零值,导致浏览器和 CDN 无法使用时间戳来判断文件是否被修改,从而无法使用缓存。

✈ Nginx Etag 的计算方法

我查了一下 Nginx ETag 的计算公式:

一个典型的 Nginx ETag 值(例如上面的那个示例 "5c45ab02-1c3")由两部分组成,以连字符分隔:

ETag = {文件最后修改时间的十六进制} - {文件内容长度的十六进制}

  • 第一部分 5c45ab02:这是文件最后修改时间的十六进制表示。具体来说,这是自 Unix 纪元(1970 年 1 月 1 日)以来的秒数,转换为十六进制格式。
  • 第二部分 1c3:这是文件内容长度的十六进制表示

如果要自己在 golang gin 里实现 ETag,可以参考这个计算方法。但是 Last-Modified 时间戳具体是使用服务启动时间,还是其他部署时间,需要斟酌一下了。

🍎 解决方案

我感觉去实现一个自动计算 ETag 或者以 golang 启动时间作为 Last-Modified 时间戳的方案都不太合适。 增加了应用层的复杂度,也影响启动时间。

直接使用 Nginx,我觉得是最简单可靠的方案。唯一的麻烦就是,部署到服务器时,需要额外拷贝一堆静态文件。

但是,我想了一下,似乎也不是很复杂,因为我的后台原本就是前后端分离的。 在发布 Ant Design Pro 前端项目时,就是自动打包前端代码,然后 scp 到服务器,再自动解包。

把这个流程复制一份,来拷贝 golang gin 的静态资源文件就可以了。

总结一下,就是原来的 scp 一个二进制文件,变成 scp 一个二进制文件 + 一个静态资源文件夹的压缩包。然后 Nginx 来提供静态文件服务。

然后,开发环境继续使用 Static 非 embed 的方式调试静态文件,生产环境使用 Nginx 提供服务。

Cloudflare VS 阿里云 ESA :开发者眼中的“流量黑洞”与“免费天堂”

2025-12-03 16:38:36

最近一周同时经历了国内网站服务器和海外服务器被大规模 CC 攻击的经历。 恰好同时使用了阿里云 ESA 和 Cloudflare 两种不同的防护服务,也通过这两家的服务分别解决了国内和海外的 CC 攻击问题。 经过几天的折腾,算是有了一点点的经验收获。这里主要说一下阿里云 ESA,因为 cloudflare 大家可能都比较熟悉了。 而我则是先使用的阿里云 ESA,之后才使用的 cloudflare。我用 cloudflare 时,也是完全依据使用 ESA 时积累的经验。

先说好的地方

经过一周多的使用,算是掌握了基本的阿里云 ESA 内置的 WAF (Web Application Firewall) 规则设置方法。 也基本扛住了广东电信 PCDN 的海量 IP CC 攻击(一天 20 万独立 IP 刷流量)。

阿里云 ESA 防护历史

从阿里云 ECS 服务器的流量监控来看,ESA 开启 WAF 之后,CC 攻击流量基本被拦截在 ESA 层,ECS 服务器的流量稳定在正常访问水平。 CPU 也从日常的 25% 降低了到 3% 以下。参见下面截图:

阿里云 ECS CPU 使用率

收获的经验

不同的服务需要区分二级域名。 例如,网站和 app api 接口服务通过二级域名分离。防止网站被刷,又因为怕影响 api 接口,不敢开全站滑块验证。

个人开发者,在没有 cloudflare 这种良心服务的情况下,还是少碰网站服务器。维护成本太高,无论是时间成本,还是钱包成本。 能用 SAAS 服务的,尽量用 SAAS 服务。像现在这种情况,网站服务器被攻击,流量暴涨,导致服务器宕机,影响用户体验。最后还得自己花时间和精力去解决这些问题,得不偿失。

阿里云 ESA 的不足

  1. 阿里云客服不专业。遇到 esa 问题,在微信群里问,客服让你提工单找工程师。提工单之后,遇到的售后工程师思维逻辑都很乱,给出的 waf 规则都是错的。要是不懂行的小白,照做了,网站直接 over。这个也能理解,正常懂运维的,谁去干阿里云客服啊,这就很尴尬。这种专业的操作最后还是要靠自己,或者找网上的大佬咨询。

  2. 基础版的每月 50g 流量就是个笑话 每天还要担心自己的流量超了。纯粹给自己找不痛快。对比 cloudflare 的免费版,流量不限量,真是天壤之别。 不得不说 Cloudflare 的免费服务真的是良心服务。作为一个个人开发者,能用 cloudflare 免费版,真的是太幸福了。 而且完全不需要担心静态资源文件的流量问题。毕竟现在网站静态资源文件越来越大,图片,视频,音频这些东西,流量消耗是非常大的。

  3. WAF 规则不支持滑块验证 只有 IP 规则支持滑块验证。通过地区,referrer,URI 这些规则都无法添加滑块验证。 而 cloudflare 的防护规则是支持滑块验证的。我作为一个开发者,我完全不理解加上这个功能有什么难度。 难道是为了节省防护产生的滑块页面流量?我都是年付费用户了,这点流量都不愿意给我吗?

网站防护还是需要专业知识

无论是阿里云 ESA 还是 cloudflare,都是需要一定的专业知识才能玩转的。 里面的概念特别多,即便是我有一定的开发经验,但是运维经验还是相对少很多,还是需要花时间去学习和摸索。 对于没有任何专业知识的小白用户来说,真的很难上手。 我亲眼看到非专业出身的站长,在 cloudflare 上面折腾了好几天,靠 AI 给出的 WAF 规则,但是效果并不好。 还是需要对攻击特征有一定的了解,才能制定出有效的防护规则。

可以参考前文 海外站被攻击,cloudflare 自定义防护规则创建策略,就是一个很好的案例。 其实防护规则相对清晰简单,但是如果不是专业人士,还是很难总结概括出 WAF 规则。

对我来说,也有很多的概念需要学习和理解。奈何现在攻击基本防住了,就没有动力继续深入研究了。。。

打工人的工作月报生存指南:把周报喂给 AI,月报轻松生成的提示词

2025-12-01 13:54:43

这个周末,也是月末,刚写完工作周报,又要写月报。 感觉非常得麻烦。周报还好,把几个项目的 Git 提交记录整理一下就行了。 但是月报就麻烦了,不仅要把周报内容汇总,还要进行分析总结,编写 KPI 达成情况。

我就想能否把每个周的周报内容,从企业微信的周报里导出来,放到一个文本文件里,然后让 AI 帮我生成月报内容。

于是,我动手测试了一下。先说结论,效果不太理想。

主要不理想的地方是,AI 生成的月报虽然看起来比较正式,也比我更懂 KPI 这些东西(其实我连部门的 KPI 是啥都不知道),但是内容比较空洞,缺乏细节。 但也许这就是领导们想要的吧,毕竟领导们也不太关心细节。 对我而言,写周报也好,写月报也好,是一个自我总结和反思的过程,细节很重要。 让 AI 去写月报,虽然节省了时间,但也失去了这个过程。那内容我都不想多看一眼。 也有好的地方,抽象总结能力确实好,省去了领导再去提取重点的时间。

操作步骤

  1. 先把每个周的周报内容,复制粘贴到一个文本文件里,命名为“周报汇总.md”。或者 txt 文件也行。
  2. 然后编写提示词,告诉 AI 我要生成月报,月报需要包含哪些内容,格式是怎样的。我是在 VSCode 里操作的,主要是方便 github copilot 读取周报文件内容。(Grok Code Fast 1 还不如 gpt5 mini)

提示词

基于每月工作周报汇总,生成月度工作报告的提示词

请基于文件“周报汇总.md”文件中的内容,生成一份详细的月度工作报告。请确保报告涵盖以下几个方面:

- 部门级重点指标:
  - 绩效指标(KPI + 重点工作计划)
  - 达成目标及评分标准
    - 目标:
    - 评分标准:
  - 自评内容(实际完成情况)
- 员工级重点指标:
  - 绩效指标(KPI + 重点工作计划)
  - 达成目标及评分标准
    - 目标:
    - 评分标准:
  - 自评内容(实际完成情况)
- 部门级重点指标两项,员工级重点指标两项以上
- 每个指标设置分值,合计100分,每个指标分值为 5 的整数倍
- 月度工作报告放到“月报汇总.md”文件中
- 如果可能,参考本月的总结生成下个月的工作计划

哎,又浪费我半天时间。我觉得要是能把时间控制到 15 分钟以内就好了。

有点尝试失败的感觉。还不如测试文生图的提示词,做细节微调更有成就感。

封面图文生图提示词

文生图提示词:我想生成一张图片用于文章的封面配图,主题是一只可爱的卡皮巴拉在写工作周报,旁边是一名美女领导上司,请帮我生成一份文生图的提示词。目的是让生成的图片细节更具体,方便接下来调整细节

用 DeepSeek 生成的结果:

## 核心场景
现代极简主义办公室,柔和的自然光从百叶窗洒入,木纹办公桌上散落着咖啡杯、笔记本电脑和彩色便签。一只毛茸茸的卡皮巴拉(水豚)戴着黑框眼镜,前爪搭在键盘上,表情专注中带着困惑,盯着屏幕显示的工作月报 Excel 表格。侧后方站着一位30岁左右的东亚裔女性主管,身着浅灰色西装外套,低头怒视屏幕,手指轻点报告某处,似在指导修改。

## 细节强化
- 卡皮巴拉的毛发需呈现湿润光泽感(暗示刚泡过澡的习性),胡须根根分明
- 女性领导妆容自然,耳垂有单颗珍珠耳钉,左手无名指戴极简婚戒
- 环境细节:背景书架有翻旧的编程书籍(《PHP从入门到放弃》,《Go是最好的语言》),角落摆放着龟背竹盆栽,墙上的抽象艺术画(蒙娜丽莎的微笑)略带莫兰迪色调
- 动态捕捉:卡皮巴拉尾巴旁倒着的咖啡杯正滴落液体在报告纸上,形成幽默的污渍

## 视觉风格
低饱和度胶片摄影感,采用浅景深突出主体,光影模拟哈苏X2D相机质感。避免对称构图,使用三分法则将角色置于画面右黄金分割线。

然后将这段提示词输入到豆包 app 里,就生成了此文的封面图片

Magento 服务器清理日志,释放磁盘空间

2025-11-30 17:20:12

今天发现 Magento 服务器的磁盘空间又快满了。怕是支撑不到下周了,所以周末清理了一下。 还是做成自动化脚本比较好。

Magento Log

我发现一个 exception.log 文件占了 50G。可能是攻击请求太多导致日志特别多。

cd var/log/
ls -lah
truncate -s 0 *.log

truncate 命令专门用于调整文件的大小,可以将文件截断为指定的长度,将长度设置为 0 即可清空文件内容。

系统日志

/var/log/journal 是 Linux 系统中 systemd-journald 服务用于存储日志的目录。它记录了系统启动、内核信息、服务运行状态等重要日志。

使用之前的磁盘空间分析工具 ncdu。使用方式参考: 使用 ncdu 命令分析 linux 磁盘空间占用

cd /var/log/journal
ncdu

清除目录下 10 天前的日志

find /var/log/journal -type f -mtime +10 -delete

joomla 日志

如果还安装了 joomla,则需要去对应的 joomla 项目下的

administrator/logs

清理相关的大日志文件。

海外站被攻击,cloudflare 自定义防护规则创建策略

2025-11-29 19:59:50

周六的凌晨被一堆网站服务器宕机报警邮件吵醒。这两周刚经历了国内网站被广东电信日均 40 万 PCDN IP 轰炸攻击, 没想到海外站又被攻击了。查看 Cloudflare 仪表盘,发现流量暴增。

第一次遇到海外站的流量攻击,我感觉处理策略跟国内站有些不同,特此记录一下。 虽然大家都知道 cloudflare 很强大,但是对于非技术人员来说,要能梳理出来防护策略,我感觉还是有点难。 我这周也提交了阿里云 ESA 工单,我发现售后工程师也业余的不行,给出的策略都反逻辑的,如果是没有经验的客户直接使用,会导致正常流量被误封。这是没有办法的事情,处理攻击还是需要稍微了解一点开发和运维的知识。

首先确认流量特征

进入 cloudflare 管理后台的, 安全性 - 分析 - 流量

cloudflare 安全性 - 分析 - 流量

查看各种维度的流量情况,确认攻击流量的特征。 比如攻击流量的国家、ASN、URL 路径、User Agent 等等。

比如,如果我的大部分客户在欧洲,那么来自美国、中国、亚洲的异常流量大概率就是攻击流量。

而且,现实就是访问量排名的前几位的国家,确实就是攻击流量的来源国。 添加过滤条件之后,看到大部分都没有 referrer,且 user agent 也有一定的规律性。

添加自定义防护规则

所以,我决定,把排名前三的国家,全部封禁掉。只要不是欧洲的流量,且流量异常的,全部封禁掉。

同时,为了防止误伤来自美国的搜索引擎爬虫,可以添加一个 and 条件。即:

如果是来自中国、美国、新加坡(全是腾讯和华为云机房的流量)的请求,并且不在 cloudflare 的 known bots 列表中,则显示挑战页面。 即,那个 cloudflare 的真人验证页面。

cloudflare 挑战页面

不要直接 block,避免误伤正常用户。这个挑战页面的通过率还是挺高的,绝大部分正常用户都能通过验证。 反正我感觉,这个体验已经非常好了。而且日常访问好多国外的站,都会跳出这个挑战页面, 也不会感觉会怪异。

同时,我没法理解,为何阿里云 ESA 就不支持在自定义规则中设置挑战页面,只能 block 或者 JS challenge,真是太不人性化了。 只有那个 IP 规则支持滑块挑战。阿里云 ESA 在这点上负分。

不必关心流量超的问题

cloudflare 相对阿里云 ESA 国内版的一个好处是,即便免费版,也不需要担心流量超标的问题。

我购买了阿里云 ESA 基础套餐,一个月才 50G 流量,这简直就是开玩笑。随便一个攻击,估计我一年的流量额度都得报销。 也不知道国内全行业整天遥遥领先了什么,流量额度这么少,打法要饭的感觉。

VSCode 里自动 Code Review 太方便了,被 github copilot AI 羞辱总比被领导羞辱好

2025-11-27 15:31:20

昨天在 VSCode 里写代码,不知道乱点了哪里,突然自动出现了 Code View 建议,说我修改的几处代码存在逻辑漏洞。我定睛一看,还真是,老老实实地,按照 github copilot 的建议把代码修复了。但是,昨天太忙,就没深究到底这个功能是怎么触发的。

今天忙完手头的紧急需求,我就搜索了一下,果然找到了。

VSCode 里自动 Code Review

在左侧的 git 视图里,当代码发生变更时,就能看到这个 Code Review 的按钮。点击,就会出现 Code Review 建议。(需要等待个10几秒,官方号称 30 秒以内出结果)。例如,下面就是 AI 的代码审查建议:

VSCode 里自动 Code Review

VSCode 里自动 Code Review 一键重构

可以一键应用 AI 推荐的代码修改。跟自动补全一样方便。

同时也会提供重复代码的重构逻辑,嘿嘿,我已经习惯被 AI 鄙视了 

但是这种优化的部分,AI 并没有自动给我提供代码,还需要去手动处理一下,体验上还有优化的空间。AI 你要加油了。

这个功能非常实用,虽然代码都是 AI 写的,但是靠自动补全生成的代码,很多还是存在一些隐患,通过更长时间的 Code Review 自动分析,能有效发现这些隐患。避免提交之后,再被同事或者领导鄙视。。。反正 AI 也不会说我坏话。。。再多的 Code Review 也没有心理压力