经历心得分享:用 Web 框架 Gin 开发 API,但业务返回的 JSON 太大,调研了一圈,最后自己写了个中间件做 gzip 压缩,记录下调研和开发调优时的心得

本贴最后更新于 1588 天前,其中的信息可能已经天翻地覆

背景

最近在做一个 web 版的展示大屏,前端靠 HTTP(S)+JSON 和后端交互,部分图形是密集的地理点位和时间序列,HTTP 返回数据量较大,公网上加载速度不佳。

调研

考虑压缩 HTTP 返回,选了 gzip 这个常规选项。

gzip 在压缩时,得考虑几点:

  • 内容类型是否对压缩友好:例如 JPEG 本身已经压缩过,再次压缩收益太小,费力不讨好;
  • 内容大小是否值得压缩:太小的内容压缩效果不好(甚至有可能比原文还大,毕竟有字典等开销),另外小内容本身传输时间短,直接放过去还能节约 CPU 和内存。

先考察了 Nginx

  • Nginx 的压缩一般是用 ngx_http_gzip_module
  • 根据 Content-Type 判断内容类型是否对压缩友好,要压缩的类型由用户定义;
  • 根据返回的 Content-Length 判断内容大小是否值得压缩;

当返回的 JSON 大于 2KB 时,Golang 会使用 chunk 的形式传输,这个时候没有 Content-Lengthngx_http_gzip_module 不会进行内容压缩。

又考察了下 gin-contrib/gzip

  • 根据扩展名判断内容类型是否对压缩友好;
  • 根据 Path 判断是否启用压缩;
  • 不支持判断内容大小是否值得压缩。

根据 Path 来判断确实可行,但太死板,业务和中间件耦合了。

自己造了个轮子

特性

看了 gin-contrib/gzipCaddy 的实现后,我造了个自己的轮子,欢迎试用、Star 以及反馈:

仓库地址: https://github.com/nanmu42/gzip

文档: https://github.com/nanmu42/gzip/blob/master/README.Chinese.md

  • 支持 Gin 和 标准库 net/http ;
  • 支持基于 Content-TypeContent-Length、扩展名判断是否压缩;
  • 启用压缩的阈值用户可以自定义;
  • 压缩级别用户可以自定义;
  • 不压缩已经压缩过的返回,不压缩 Head 请求的返回,不影响 HTTP Upgrade ;
  • 中间件初始化简单,集成容易;

更进一步

还记得返回的 JSON 大于 2KB 时,Golang 会使用 chunk 的形式传输,这个时候没有 Content-Length 带来无法判断内容是否值得压缩的问题吗?

我取了个巧,如果 Content-Length 不存在,中间件会去观察 http.ResponseWriter.Write(data []byte) 的第一次调用时的 len(data),如果此时 len(data) 已经大于启用压缩的阈值,那么可以安全地开始压缩。

调优

这里分享在造轮子时的两个调优点。

此部分可配合项目各阶段 benchmark 食用: https://github.com/nanmu42/gzip/blob/a0b9dac85d4a0a72f4a2183d3b9bfadf215f2168/docs/benchmarks.md

AC 自动机

原本我使用 Strings.Contains() 配合循环来判断文件后缀 /MIME 是否在支持压缩的列表中,但 benchmark 下来效果不太好。做了一些搜索后发现 Cloudflare 实现了一个 AC 自动机来做这个事情。和维护者聊了聊之后,我用了它的一个 fork: https://github.com/signalsciences/ac

Sync.Pool

Sync.Pool 用来做对象重用,以降低系统内存分配和 Go 垃圾回收的压力,一开始我只对 gzip.Writer 做了对象重用,但发现中间件对内存的影响还有一些大,后来我用了第二个 Sync.Pool 重用 wrapper,内存使用量和 CPU 时间都有了可观的改善。

两个调优之后,CPU 时间下降为调优前的 40%,内存使用量下降为原先的一半。

  • Gin
    12 引用 • 33 回帖
  • golang

    Go 语言是 Google 推出的一种全新的编程语言,可以在不损失应用程序性能的情况下降低代码的复杂性。谷歌首席软件工程师罗布派克(Rob Pike)说:我们之所以开发 Go,是因为过去 10 多年间软件开发的难度令人沮丧。Go 是谷歌 2009 发布的第二款编程语言。

    491 引用 • 1383 回帖 • 373 关注
  • middleware
    1 引用
  • Gzip

    gzip (GNU zip)是 GNU 自由软件的文件压缩程序。我们在 Linux 中经常会用到后缀为 .gz 的文件,它们就是 Gzip 格式的。现今已经成为互联网上使用非常普遍的一种数据压缩格式,或者说一种文件格式。

    9 引用 • 12 回帖 • 111 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...