You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

企业环境下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:32:11