电话
400 9058 355
RPC调用失败时盲目重试会引发雪崩,必须结合超时、退避、熔断三要素;net/rpc需手动封装context超时,推荐迁移到原生支持context的gRPC,并通过拦截器统一实现带错误过滤与指数退避的重试逻辑。
Go 标准库 net/rpc 本身不提供重试机制,强行在客户端套一层 for 循环重试 Call 或 Go,往往导致雪崩:服务端超时未返回,客户端反复发请求,堆积更多连接和 goroutine。重试必须配合超时、退避、熔断三要素,否则不是容错,是攻击。
rpc.Client 的底层连接是长连接,但单次 Call 调用没有内置超时 —— 必须手动包装 context.WithTimeout 或改用支持 contex
io.EOF、net.OpError(连接拒绝/超时)可重试;rpc.ErrShutdown 或明确的业务错误码(如 "user_not_found")不应重试标准 net/rpc 的 Client.Call 签名不接受 context.Context,所以无法直接中断挂起的调用。可行方案只有两个:换框架,或自己封装带超时的调用逻辑。
gRPC:原生支持 context.Context,且 grpc.WithBlock() + context.WithTimeout() 可精确控制每次请求生命周期net/rpc,需在 dial 阶段设置连接超时,并用 time.AfterFunc + sync.Once 手动 cancel goroutine(不完美,但比无超时强)client.Call,同时 select 等待 ctx.Done() 或结果 channel;一旦超时,关闭 channel 并标记本次失败重试逻辑不该散落在每个 Call 前后,应抽成可复用函数。关键点是区分“可重试错误”和“不可重试错误”,并控制退避间隔。
github.com/cenkalti/backoff/v4 库,配置 MaxRetries: 3、InitialInterval: 100 * time.Millisecond
func isRetryable(err error) bool { return errors.Is(err, io.EOF) || strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "i/o timeout") }
rpc 不暴露 status,但自定义错误字符串里含 "400" 或 "500" 就该跳过)ctx.Err() != nil,防止重试过程本身被取消后还继续如果你已在用 gRPC,别自己写重试循环 —— 利用 UnaryClientInterceptor 在拦截器里做重试,对业务代码零侵入。
backoff.Retry 包裹原始 invoker 调用,传入自定义重试策略和错误判定函数grpc_retry.WithPerRetryTimeout 和 grpc_retry.WithCodes(codes.Unavailable, codes.DeadlineExceeded)
重试不是加个 for 循环就完事;真正难的是判断“此刻该不该重”,以及“重了之后系统整体会不会更糟”。很多 RPC 失败本质是下游已不可用,这时候最稳妥的操作,往往是立刻失败、上报告警,而不是默默 retry 到超时。
邮箱:8955556@qq.com
Q Q:8955556
本文详解如何将Go官方present工具(用于生成HTML5...
PySNMP在不同版本中对SNMP错误状态(errorSta...
time.Sleep仅阻塞当前goroutine,其他gor...
PHPfopen()创建含特殊符号的文件名失败主因是操作系统...
WooCommerce中通过代码为分组产品动态聚合子商品的属...
io.ReadFull返回io.ErrUnexpectedE...
本文详解Yii2中控制器向视图传递ActiveRecord数...
本文详解为何通过wp_set_object_terms()为...
Pytest中使用@mock.patch类装饰器会导致补丁泄...
带缓冲的channel是并发安全的FIFO队列;make(c...