0

0

如何使用Golang实现代理缓存系统_Golang代理模式请求优化示例

P粉602998670

P粉602998670

发布时间:2026-01-24 10:27:34

|

290人浏览过

|

来源于php中文网

原创

直接用 http.Transport 做代理缓存会失败,因其默认不缓存响应,且 RoundTripper 接口无法拦截并缓存响应体,多次读取 resp.Body 会导致“read on closed response body”错误。

如何使用golang实现代理缓存系统_golang代理模式请求优化示例

为什么直接用 http.Transport 做代理缓存会失败

Go 标准库的 http.Transport 默认不缓存响应,即使你设置了 Cache-Control: public, max-age=3600,它也不会自动复用。更关键的是,http.RoundTripper 接口不暴露原始请求头/响应体,无法在中间拦截并写入本地缓存。常见错误是试图在 RoundTrip 方法里读取 resp.Body 两次——第一次读完后 Body 就被关闭,第二次读会得到空内容或 http: read on closed response body 错误。

  • 必须在读取响应前,用 io.TeeReaderio.MultiReader 复制原始字节
  • 缓存键不能只依赖 req.URL.String(),要归一化:去掉 utm_* 参数、忽略查询参数顺序、统一协议和 host 大小写
  • Cache-Control 中的 no-storeno-cacheprivate 必须严格跳过缓存

http.ServeHTTP 实现可缓存的反向代理

标准库 httputil.NewSingleHostReverseProxy 是起点,但它不支持缓存逻辑注入。你需要继承并重写 Director 和响应处理流程。核心是在 proxy.ServeHTTP 调用后,立即拦截 ResponseWriter,捕获状态码、头信息和响应体。

  • httptest.NewRecorder() 拦截响应,再用 bytes.Buffer 存储原始 body
  • 缓存键生成示例:fmt.Sprintf("%s %s %s", req.Method, normalizeURL(req.URL), req.Header.Get("Accept"))
  • 缓存过期时间优先取 Cache-Control: max-age=N,其次 fallback 到 Expires
func (c *cachedProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
    key := c.cacheKey(req)
    if entry, ok := c.cache.Get(key); ok && !entry.Expired() {
        entry.WriteTo(rw)
        return
    }

    resp := httptest.NewRecorder()
    c.proxy.ServeHTTP(resp, req)

    buf := &bytes.Buffer{}
    io.Copy(buf, resp.Body)
    bodyBytes := buf.Bytes()

    cacheEntry := &cacheItem{
        StatusCode: resp.Code,
        Header:     resp.Header().Clone(),
        Body:       bodyBytes,
        CreatedAt:  time.Now(),
        TTL:        c.ttlFromHeaders(resp.Header()),
    }
    c.cache.Set(key, cacheEntry, cacheEntry.TTL)

    rw.WriteHeader(resp.Code)
    for k, vs := range resp.Header() {
        for _, v := range vs {
            rw.Header().Add(k, v)
        }
    }
    rw.Write(bodyBytes)
}

bigcacheristretto 选哪个做内存缓存

bigcache 适合高并发、大量小响应(如 JSON API),因为它用分片 map + 时间戳淘汰,避免 GC 压力;但不支持基于 TTL 的自动过期,得自己轮询清理。ristretto 支持精确 TTL、LRU+LFU 混合策略,更适合混合大小响应(含图片、HTML),但内存占用略高、初始化稍慢。

  • 如果代理目标主要是 REST API(平均响应 bigcache.NewBigCache,设置 Shards: 128
  • 如果需缓存 HTML 或压缩资源(可能 >100KB),用 ristretto.NewCache,开启 OnExit: func(key uint64, value interface{}) 清理大对象
  • 避免用 sync.Map:它不支持容量限制和淘汰,长期运行必然 OOM

如何处理 POST/PUT 请求的缓存穿透

GET 请求天然幂等,可安全缓存;但 POST/PUT 默认不可缓存。若业务明确某些 POST 是只读操作(如 /api/search),需手动放行。关键是识别“语义 GET”:检查路径是否含 searchquerylist,且请求体是 JSON 查询条件而非业务变更数据。

Trickle AI
Trickle AI

多功能零代码AI应用开发平台

下载

立即学习go语言免费学习笔记(深入)”;

  • 禁止缓存 Content-Type: application/json 且 body 含 "update""delete""status": "active" 等字段的请求
  • 对放行的 POST,缓存键必须包含 sha256(body),而非仅 URL —— 否则不同查询参数会命中同一缓存
  • 永远不缓存带 CookieAuthorization 头的请求,除非显式配置 CacheableHeaders: ["X-User-ID"]

缓存系统最难的不是存和取,而是判断“该不该缓存”——这个决策点分散在 URL 归一化、头解析、body 检查、TTL 计算多个环节,漏掉任意一个都可能把私有数据缓存成公共响应。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

180

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

341

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

209

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

393

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

198

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

191

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

273

2025.06.17

c++空格相关教程合集
c++空格相关教程合集

本专题整合了c++空格相关教程,阅读专题下面的文章了解更多详细内容。

0

2026.01.23

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3万人学习

AngularJS教程
AngularJS教程

共24课时 | 2.9万人学习

CSS教程
CSS教程

共754课时 | 23万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号