【问题标题】:Does RPC have a timeout mechanism?RPC 有超时机制吗?
【发布时间】:2014-06-13 08:18:50
【问题描述】:

如果 RPC 没有超时机制,如果 RPC 调用试图调用已关闭服务器的 RPC 方法,我该如何“杀死”它?

【问题讨论】:

    标签: go rpc


    【解决方案1】:

    您可以使用channels 来实现超时模式:

    import "time"
    
    c := make(chan error, 1)
    go func() { c <- client.Call("Service", args, &result) } ()
    select {
      case err := <-c:
        // use err and result
      case <-time.After(timeoutNanoseconds):
        // call timed out
    }
    

    select 将阻塞直到client.Call 返回或timeoutNanoseconds 过去。

    【讨论】:

    • 这是一个很好的解决方案。但请注意,当超时发生时,goroutine 将继续在后台运行,可能会占用操作系统资源。
    • 如果它是一个无缓冲的通道,这将是正确的。由于我使用的是大小为 1 的缓冲通道,因此 go 例程不会阻塞。
    • Sebastian,如果 RPC 服务器不响应,goroutine 可能会在 client.Call() 中永远阻塞。这将导致 goroutine 泄漏。
    • 至少在 Go 1.5 中,有一个 client.Go 方法更可取
    • 请注意,在大规模服务时间。之后会导致内存问题,因为它不会被 GC 收集,直到它被触发。而不是time.Timer 可能更需要,因为您可以手动停止它。
    【解决方案2】:

    似乎 net/rpc 的唯一解决方案是在您发现卡住的请求时关闭底层连接。然后客户端应该完成挂起的请求,出现“连接中断”错误。

    另一种方法是使用 https://github.com/valyala/gorpc ,它支持开箱即用的超时 RPC 调用。

    【讨论】:

      【解决方案3】:

      如果您想实现超时(以防止调用时间过长),那么您需要将 rpc.Dial 更改为 net.DialTimeout(注意它们是单独的包:rpc 与 net)。另请注意,返回的类型不再是客户端(就像在前面的示例中一样);相反,它是一个“连接”。

        conn, err := net.DialTimeout("tcp", "localhost:8080", time.Minute)
        if err != nil {
          log.Fatal("dialing:", err)
        }
      
        client := rpc.NewClient(conn)
      

      【讨论】:

        【解决方案4】:

        或者,现在,有人可能更喜欢使用上下文。这还负责在超时时返回正确的错误。 (context.DeadlineExceeded)

        import (
            "context"
            "log"
            "net/rpc"
        )
        
        type Client struct {
            *rpc.Client
        }
        
        // CallEx is a context aware wrapper around rpc's Client.Call()
        func (c *client) CallEx(ctx context.Context, serviceMethod string, args interface{}, reply interface{}) error {
            ec := make(chan error, 1)
            go func() {
                ec <- c.Call(serviceMethod, args, reply)
            }()
            select {
            case err := <-ec:
                return err
            case <-ctx.Done():
                return ctx.Err()
            }
        }
        

        使用 Deadlined 上下文调用它:

        type Args struct {
           A, B int
        }
        
        func main(){
            rpc, err := rpc.DialHTTP("tcp", "host")
            if err != nil {
                t.Fatal(err)
            }
            c := client{rpc}
        
            ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
            defer cancel()
        
            var i int
            if err := c.CallEx(ctx, "Calc.Multiply", Args{2, 2}, &i); err != nil {
                log.Fatal(err)
            }
        }
        

        【讨论】:

        • 如楼上@valyala 的评论提醒,如果服务器没有响应,client.call 将永不返回,goroutine 将泄漏。跨度>
        猜你喜欢
        • 1970-01-01
        • 2013-11-26
        • 1970-01-01
        • 2021-01-09
        • 2018-12-28
        • 2014-09-13
        • 2013-02-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多