Gzip 的一個坑

我們的項目裏爲了方便部署,swagger 文檔是通過 gzip 壓縮後,被植入到程序裏的。其實這個思路源自於 gRPC ProtoBuf fileDescriptor[1]

var fileDescriptor_308767df5ffe18af = []byte{
    // 2522 bytes of a gzipped FileDescriptorProto
    0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xcd, 0x6f, 0xdb, 0xc8,
    0x15, 0x5f, 0x7d, 0x5a, 0x7a, 0x92, 0x65, 0x7a, 0xec, 0x75, 0x18, 0xef, 0x47, 0x1c, 0xed, 0x66,
    0xe3, 0x24, 0xbb, 0xca, 0xc2, 0x49, 0x9c, 0xac, 0x53, 0x6c, 0x2b, 0x4b, 0x8c, 0x57, 0xa9, 0xbe,

每次大家更新完 swagger 文檔後,都需要手動生成下這個 fileDescriptor,大概這樣:

$ gzip -c swagger.yaml | xxd -p -c 16 | sed -e 's/../0x&,/g'

但是在多人協作的時候,總有個奇怪的問題:swagger 並沒有更新,但是這個 fileDescriptor 有時卻生成的不一樣。並且每次都是文件頭的第 5-6 個字節的位置:

根據 gzip 的 RFC 1952[2] 文檔來看:

+---+---+---+---+---+---+---+---+---+---+
|ID1|ID2|CM |FLG|     MTIME     |XFL|OS | (more-->)
+---+---+---+---+---+---+---+---+---+---+

第 5-8 個字節爲文檔的修改時間(timestamp),使用小端模式編碼(這也解釋了爲啥總是前兩個字節變化)。動手來驗證下:

$ gzip -c swagger.yaml | xxd -p -c 4 | sed -n '2p'
86672361

先轉換爲大端模式:61236786,再換算成 10 進制:1629710214,繼續轉換爲本地時間:2021-08-23 17:16:54,發現和文檔修改時間一致:

$ stat swagger.yaml
  File: ‘swagger.yaml’
  Size: 16434         Blocks: 40         IO Block: 4096   regular file
Device: fd00h/64768d    Inode: 205340662   Links: 1
Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
Access: 2021-08-23 17:16:54.217753946 +0800
Modify: 2021-08-23 17:16:54.148753750 +0800
Change: 2021-08-23 17:16:54.148753750 +0800
 Birth: -

問題原因梳理明白了,解決就好辦了,就像 gRPC 那樣直接把這個時間戳置 0 就好了,好在 gzip 已經提供了這樣一個選項:

$ gzip -n -c swagger.yaml | xxd -p -c 4 | sed -n '2p'
00000000

引用鏈接

[1] gRPC ProtoBuf fileDescriptor: https://github.com/gogo/protobuf/blob/226206f39bd7276e88ec684ea0028c18ec2c91ae/protoc-gen-gogo/descriptor/descriptor.pb.go#L2705-L2709
[2] RFC 1952: https://datatracker.ietf.org/doc/html/rfc1952#page-5

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/EBY6eQcuKS_EhDhtDO6GbQ