企业环境下gRPC文件上传报错sendmsg: Result too large (34)求助
gRPC单向流文件上传间歇性失败排查
问题背景
在企业环境中使用gRPC实现工作流自动化,采用单向流模式进行文件上传/下载。当前个人电脑向企业数据中心服务器上传文件时出现间歇性失败,失败概率远高于成功概率。
测试验证结果
- 客户端与服务器均在数据中心服务器运行:无异常
- 客户端与服务器均在个人电脑运行:无异常
- 客户端在数据中心服务器、服务器在个人电脑:无异常
- 仅客户端在个人电脑、服务器在数据中心服务器(PC→数据中心上传):出现失败
客户端失败异常信息
Traceback (most recent call last): File "<some-path>/client.py", line 39, in <module> upload_file(stub, file_loc, file_rem=file_rem) File "<some-path>/client.py", line 24, in upload_file resp = stub.upload_file(read_iterfile(file_loc, file_rem)) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<some-path>/.venv/lib/python3.12/site-packages/grpc/_channel.py", line 1536, in __call__ return _end_unary_response_blocking(state, call, False, None) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "<some-path>/.venv/lib/python3.12/site-packages/grpc/_channel.py", line 1006, in _end_unary_response_blocking raise _InactiveRpcError(state) # pytype: disable=not-instantiable ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ grpc._channel._InactiveRpcError: <_InactiveRpcError of RPC that terminated with: status = StatusCode.UNAVAILABLE details = "sendmsg: Result too large (34)" debug_error_string = "UNKNOWN:Error received from peer {grpc_message:"sendmsg: Result too large (34)", grpc_status:14, created_time:"2025-01-10T17:26:25.646875+01:00"}"
复现代码
客户端代码
import os import sys import grpc import test_pb2 import test_pb2_grpc def read_iterfile(file_loc, file_rem, chunk_size=16*1024): yield test_pb2.upstream(file=file_rem) with open(file_loc, mode="rb") as fh: i = 0 while chunk := fh.read(chunk_size): i += 1 yield test_pb2.upstream(data_chunk=chunk) print("chunk {}, size {}".format(i, len(chunk))) print("total chunks: {}.".format(i)) def upload_file(stub, file_loc, file_rem): resp = stub.upload_file(read_iterfile(file_loc, file_rem)) print("file '{}' upload {}".format(file_loc, resp.message)) if __name__ == '__main__': ipaddr = sys.argv[1] port = sys.argv[2] file_loc = sys.argv[3] file_rem = 'file_rem.test' s_addr = '{}:{}'.format(ipaddr, port) channel = grpc.insecure_channel(s_addr) stub = test_pb2_grpc.debugStub(channel) upload_file(stub, file_loc, file_rem=file_rem)
服务端代码
import sys from concurrent import futures import multiprocessing import grpc import test_pb2 import test_pb2_grpc class debug_server(test_pb2_grpc.debugServicer): def upload_file(self, req_iterator, context): fh = None file_loc = None i = 0 for req in req_iterator: if req.file: file_loc = req.file print("file_loc: {}".format(file_loc)) fh = open(file_loc, 'wb') else: i += 1 print("chunk {}, size {}".format(i, len(req.data_chunk))) fh.write(req.data_chunk) print("total chunks: {}".format(i)) if fh is None: status = 'failed' else: fh.close() status = 'success' print("file '{}' upload {}".format(file_loc, status)) return test_pb2.generic_msg(message=status) def start_server(port): server = grpc.server(futures.ThreadPoolExecutor(max_workers=3)) o_debug_server = debug_server() test_pb2_grpc.add_debugServicer_to_server(o_debug_server, server) bind_addr = "[::]:{}".format(port) server.add_insecure_port(bind_addr) server.start() print("gRPC server started: port {}".format(port)) server.wait_for_termination() print("gRPC server terminated: port {}".format(port)) if __name__ == '__main__': port = sys.argv[1] mp_server = multiprocessing.Process(target=start_server, args=(port,)) mp_server.start() print("gRPC server process started: port {}".format(port)) mp_server.join() print("gRPC server process terminated: port {}".format(port))
Protobuf定义(test.proto)
syntax = "proto3"; package debugging; service debug { rpc upload_file(stream upstream) returns (generic_msg) {} } message generic_msg { string message = 1; } message upstream { oneof request { string file = 1; bytes data_chunk = 2; } }
代码生成与运行命令
- 生成代码:
python -m grpc_tools.protoc --python_out=${PWD} --grpc_python_out=${PWD} -I${PWD} test.proto
- 启动服务端:
python server.py <server-port>
- 启动客户端:
python client.py <server-ipaddr> <server-port> <file-to-upload>
使用版本
Python 3.12.4,gRPC 1.65.4,MacOS 15.2
排查思路
1. 网络MTU限制分析
错误信息中的sendmsg: Result too large (34)核心指向数据包大小超过MTU(最大传输单元)限制:
- 检查客户端MTU设置:执行
ifconfig或networksetup -getMTU <interface>查看当前值,企业网络常见MTU为1500或更低(如1400) - 验证路径MTU:使用
traceroute -mtu 1500 <server-ip>测试链路是否存在MTU不匹配 - 调整分片大小:将客户端
chunk_size从16KB降低至4KB/8KB,同时在channel配置中添加grpc.max_send_message_length参数限制单消息大小
2. 企业网络中间设备限制
个人电脑到数据中心的链路可能经过防火墙、负载均衡器或VPN网关,需排查:
- 数据包分片限制:部分设备默认禁止UDP分片(gRPC默认用HTTP/2 over TCP,但特殊场景可能涉及UDP)
- 流量控制策略:设备对单连接带宽/数据包频率的限制导致间歇性丢包
- 超时设置:中间设备连接超时过短,长时上传流被强制中断
3. gRPC配置优化
- 启用HTTP/2活性保持:在客户端channel添加
grpc.http2.max_pings_without_data、grpc.http2.min_time_between_pings_ms参数,防止连接被断开 - 切换加密连接:企业网络可能对未加密连接做特殊处理,尝试用
grpc.secure_channel替代insecure_channel - 添加重试机制:针对
UNAVAILABLE状态配置gRPC自动重试策略
4. 系统层面参数调整(MacOS)
- 增大网络缓冲区:修改
sysctl参数,如sudo sysctl -w net.inet.tcp.sendspace=1048576 - 禁用UDP校验和:执行
sudo sysctl -w net.inet.udp.checksum=0测试是否因校验和错误导致丢包
5. 日志与抓包分析
- 开启gRPC详细日志:设置环境变量
GRPC_VERBOSITY=DEBUG和GRPC_TRACE=http2,获取HTTP/2层交互细节 - 抓包验证:用Wireshark过滤
tcp port <server-port>,查看是否存在数据包重传、RST或ICMP MTU过大错误
内容的提问来源于stack exchange,提问作者minice
相关产品推荐
相关产品推荐

