开启了http2,但是http2_max_field_size 还在用默认值吗?

开启了http2,但是http2_max_field_size 还在用默认值吗?

排查1个异常接口,学到一个降本和接口提速的新思路。另外找到1个charles的"bug"

现象

测试同学反馈在iOS13设备上请求接口报错,将请求复制为 curl 命令。分别导入 apifox 和 在终端执行:

  • 在 apifox 请求正常 ✅
  • 在终端请求失败 ❌
curl: (56) Failure when receiving data from the peer

测试同学又反馈另一个手机支持请求接口返回正常。

定位

有了正常和错误的请求curl,那直接对比二者差异就很简单了。对比发现,在终端执行失败的请求中多了一个较大的Cookie: app_token。按历史经验基本能确定是因为 Cookie 这个 header 条目太大,超过服务器的限制。 找云平台确定相关配置:

ELB
http1: header头限制 128KB,body 限制一个10G
http2: 单个 header 限制 4KB

curl 加 -v 参数再次请求,发现终端发起的请求使用的是 http2。再使用 --http1.1 参数强制使用 http1.1,接口正常返回。佐证了问题出在 http2,遵循优先快速恢复原则,我们先关闭了 http2,请求恢复正常。但还是有 3 个疑问困扰?

  1. 为什么有问题的curl请求在 apifox 始终能成功?
  2. 测试同学发现,iOS 14 和 iOS 17 的请求都能的成功,Cookie 大小也和 iOS 13的一致,但是却始终返回正常。
  3. 通过手动修改 Cookie 长度,找到了成功和失败的临界点。但是这个值5000多,为什么大于 4KB?

根因排查

第1个问题:apifox 无法在界面指定 http 协议,于是在启动 Charles 抓包,apifox 默认就是使用的 http1.1,所以没有问题。第2个问题:👍 测试同学又抓包发现,iOS 13 和 iOS 14 的请求对 cookie 的处理方式不同:

  • iOS 13 所有的 cookie 合并到一起
  • iOS 14 则逐个分开

我们现在知道了拆分 Cookie 可以跳过 nginx 的 http2_max_field_size,但这就是 iOS 14 为什么要拆分 Cookie 的原因吗?

我们在rfc文档里找到了答案https://datatracker.ietf.org/doc/html/rfc7540#section-8.1.2.5http2 拆分 Cookie 可以获得更高的压缩率,新版系统应该对此有优化。因为 http2 的设计对 header 的优化其中一点就是:减少冗余数据。通过索引和动态表,减少了重复传输相同头部字段和值所需的字节数。

💡
The Cookie header field [COOKIE] uses a semi-colon (";") to delimit
cookie-pairs (or "crumbs"). This header field doesn't follow the
list construction rules in HTTP (see [RFC7230], Section 3.2.2), which
prevents cookie-pairs from being separated into different name-value
pairs. This can significantly reduce compression efficiency as
individual cookie-pairs are updated.
To allow for better compression efficiency, the Cookie header field
MAY be split into separate header fields, each with one or more
cookie-pairs. If there are multiple Cookie header fields after
decompression, these MUST be concatenated into a single octet string
using the two-octet delimiter of 0x3B, 0x20 (the ASCII string "; ")
before being passed into a non-HTTP/2 context, such as an HTTP/1.1
connection, or a generic HTTP server application.
Therefore, the following two lists of Cookie header fields are
semantically equivalent.
cookie: a=b; c=d; e=f
cookie: a=b
cookie: c=d
cookie: e=f

HTTP/2 支持 HEADER 压缩,开发了 HPACK 算法,在客户端和服务器两端建立“字典”,用索引号表示重复的字符串,还釆用哈夫曼编码来压缩整数和字符串,可以达到 50%~90% 的高压缩率。

回到第2个问题,“iOS 14 和 iOS 17 的请求都能的成功,Cookie 大小也和 iOS 13的一致,但是却始终返回正常”。因为 iOS 14 对 Cookie 的处理方式进行了优化。原因不是为了绕过 http2_max_field_size 的限制,而是为了获取更高的压缩率。

第3个问题:http2_max_field_size 针对单个 HeaderField 的限制,如果进行 Huffman(哈夫曼编码)压缩,那么此限制针对的是对压缩后的值进行比较;

注:此指令自版本 1.19.7 起已过时。应改用 large_client_header_buffers

总结

要让 HPACK 的“字典”持续使用,必须确保Key-Value不会发生变更。所以尽量拆分 HEADER 内容,不常变更的和常变更的要分开。少了 HEADER 的传输,带宽和请求时延都有提升,这是不是一个降本的好思路呢,快来检查你们接口的请求和响应 HEADER,看看有没有优化的可能性。

  • HTTP/2 支持将 Cookie 字段可以拆分为单独的 HEADER 字段,每个标头字段包含一个或多个 cookie 对。
  • 是否将 Cookie 拆分是请求方逻辑,部分旧客户端虽然支持 HTTP/2,但是不一定会进行此优化。

最后也发现 Charles 的小 Bug 🪳:测试反馈接口问题,服务端可能让提供 Charles 请求复制成 Curl Request。但把拆分 Cookie 请求,复制成 curl, Cookie 会合并到一起🌝。

参考:

Read more

golang proto api 校验国际化 protovalidate

golang proto api 校验国际化 protovalidate

众所周知,protobuf 原型文件扩展很多功能,比如生成 http 接口层代码,顺势就有了生成接口参数校验代码的需求。 早期可以使用https://github.com/bufbuild/protoc-gen-validate 来实现,通过生成特定的 go 代码的方式来实现校验。 github 中也提到目前趋于稳定,不会有更多新特性的支持,推荐大家使用新的版本 protovalidate,https://github.com/bufbuild/protovalidate 。该版本是protoc-gen-validate 的“精神继承者”。它不需要任何代码生成并支持自定义约束。 现在我们尝试新版本,并且增加国际化支持。 go get github.com/bufbuild/protovalidate-go import "github.com/bufbuild/protovalidate-go" syntax = "proto3"; package

By brian
git clone 复制一个整个仓库并推送到新地址

git clone 复制一个整个仓库并推送到新地址

要使用 git clone --bare 复制一个新的仓库并推送到远程仓库,可以按照以下步骤操作: 1. 克隆一个裸仓库 首先,使用 git clone --bare 命令克隆源仓库。假设源仓库的 URL 是 https://github.com/user/source-repo.git,你可以执行以下命令: bash复制 git clone --bare https://github.com/user/source-repo.git 这将创建一个新的裸仓库(没有工作区),通常会创建一个名为 source-repo.git 的目录。 2. 进入裸仓库目录 进入刚刚克隆的裸仓库目录: bash复制 cd source-repo.git 3. 添加新的远程仓库 接下来,

By brian
搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

搜索引擎技巧不用多,学会 3 个加速 100% 找到目标

在搜索时使用英文关键词,提高结果质量。尽量使用 google.com 以下搜索引擎技巧在 google.com 进行测试,效果都很好,前三个非常常用且强烈推荐。 使用精确搜索 * 建议: 使用双引号 "" 搜索完全匹配的短语,避免无关结果。 * 示例: * "Java NullPointerException" fix * 场景: 找到错误信息的精确解决方案。 利用站内搜索 * 建议: 使用 site: 限制搜索范围到特定网站。 * 示例: * site:stackoverflow.com "TypeError: undefined is not a function" * 场景: 搜索 Stack Overflow、官方文档或技术博客的特定内容。 一些关键词 * 建议:

By brian
沪ICP备2022013452号-1