踩坑记:gRPC 异常响应
2023-3-15 08:55:30 Author: Go语言中文网(查看原文) 阅读量:26 收藏

前些天接到一个 Oncall,来自 Lark 的胡同学反馈,用 gRPC 官方的 python 客户端请求 Kitex gRPC Server,有时收到的 response 为 None。

请求的代码大致如下:

stub = xx_grpc.XXXServiceStub(channel)
resp = stub.SomeMethod(req)
logger.info("resp = {}".format(resp))

如果请求失败,按 python 的尿性,这里应该 raise 一个 exception,返回个 None 算个啥?

胡同学补充说,从服务端的日志来看,请求是正常收到且处理了,并且通过增加两个环境变量再运行 client 端:

$ GRPC_TRACE=all GRPC_VERBOSITY=debug python3 test.py

可以看到 client 把 tcp 报文内容都 dump 出来了,虽然乱码很多,但是从一些文本中可以看到,确实收到了 server 的 response:

考虑到胡同学用的是 gRPC 官方的 client,我充分利用自己锻炼了多年的反思技能,先从 Kitex 查起。

既然这个问题能够稳定复现,那就说明可以稳定复现这个问题。

那么就先用 tcpdump 抓它个包:

$ tcpdump -i any tcp port 9954 -Ans 0 -w grpc.pcap

然后在 wireshark 里打开,结合 log 里的信息,通过 filter 找到有问题的这个请求: 

人肉 decode 这个报文确实有点为难,好在可以在 wireshark 里指定报文的协议为 HTTP2,然后它顺手就把 gRPC 也给识别出来了:

但也没有完全识别出来,只是把这个 gRPC payload 识别成了「Line-based text data」。将这个的「text data」保存为 packet_bytes.bin,然后执行:

$ protoc --decode_raw < packet_bytes.bin

可以正常解码,说明返回的数据是符合 protobuf 规范的。

而对比其他正常响应的报文,Wireshark 可以直接按照 protobuf 的编码协议当场 decode 出来:

这说明在一些情况下, Kitex gRPC Server 确实返回了不正确的报文。

对比这两个请求的 Wireshark 解析结果,可以看到,在 gRPC Message 下面有两个 flag 不同:

flag正常响应异常响应
Frame Type01
Compressed Flag01

看起来非常接近真相了。

那么问题是哪一个呢?

根据官方文档 gRPC over HTTP2 [1],其报文是由一系列「Length-Prefixed-Message」构成的;而每个「Length-Prefixed-Message」则由三个部分组成:

  • Compressed-Flag:0/1(1字节无符号整数)

  • Message-Length:消息长度(4字节无符号整数)

  • Message:二进制数据

异常响应的报文显然不符合规范 —— Compressed Flag 这个字节的值竟然是 0xAF ?

根据以前的经验,这种脏东西大概是这两种情况导致的:(1) 并发读写导致内存数据被写坏了,或 (2) 复用了未清理的buf。

侯捷大佬说过「源码面前,了无秘密」。但是他没说扒代码会这么辛苦。此处省略800字,我终于扒出来 Kitex 的 gRPC 编码逻辑,位于 pkg/remote/codec/protobuf/grpc.go:

在编码一个 Data Frame 的时候,会先从 mcache 获取一个 buf,在 [1, 5) 写入数据的长度,并在 [5, +∞) 写入 protobuf 编码后的数据。

至此破案。

既然问题定位到了,修复起来也就很简单了,只要将从 mcache 获得的 buf 第一个字节清零即可,相关 PR[2] 已经合入 develop 分支,将会随着下一个版本的 Kitex 一起发布。

此外,python client 在遇到错误的报文时没有任何报错,也不是太对劲,于是我给他们提了个 issue[3] 。

(完)

[1] gRPC Over HTTP2

https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md

[2] 给 Kitex 提的 PR

https://github.com/cloudwego/kitex/pull/795

[3] 给 gRPC 提的 issue

https://github.com/grpc/grpc/issues/32257

[4] CloudWeGo

https://www.cloudwego.io

[5] Wireshark Wiki - gRPC

https://wiki.wireshark.org/gRPC.md


推荐阅读

福利
我为大家整理了一份从入门到进阶的Go学习资料礼包,包含学习建议:入门看什么,进阶看什么。关注公众号 「polarisxu」,回复 ebook 获取;还可以回复「进群」,和数万 Gopher 交流学习。


文章来源: http://mp.weixin.qq.com/s?__biz=MzAxMTA4Njc0OQ==&mid=2651454177&idx=1&sn=ea1dec951ba573fa49994827caceb465&chksm=80bb2413b7ccad05dd88d4458b8a30754fc14b77557e229bff78ae7b1ff31f661d4b8000157d#rd
如有侵权请联系:admin#unsafe.sh