Does RPC have a timeout mechanism?

前端 未结 4 615
情书的邮戳
情书的邮戳 2021-02-09 14:30

If RPC does not have a timeout mechanism, how do I \"kill\" an RPC call if it is trying to call an RPC method of a server that is closed?

相关标签:
4条回答
  • 2021-02-09 15:01

    You can use channels to implement a timeout pattern:

    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
    }
    

    The select will block until either client.Call returns or timeoutNanoseconds elapsed.

    0 讨论(0)
  • 2021-02-09 15:09

    if you want to implement a timeout (to prevent a call from taking too long), then you'll want to change rpc.Dial for net.DialTimeout (notice they're separate packages: rpc vs net). Also be aware that the returned type isn't a client any more (as it is in the previous example); instead it is a 'connection'.

      conn, err := net.DialTimeout("tcp", "localhost:8080", time.Minute)
      if err != nil {
        log.Fatal("dialing:", err)
      }
    
      client := rpc.NewClient(conn)
    
    0 讨论(0)
  • 2021-02-09 15:15

    It seems the only solution for net/rpc is to close the underlying connection when you notice stuck requests. Then the client should finish pending requests with "connection broken" errors.

    An alternative way is to use https://github.com/valyala/gorpc , which supports timeout RPC calls out of the box.

    0 讨论(0)
  • 2021-02-09 15:21

    Or, anno now, someone might prefer to use context instead. This also takes care of returning a proper error when timed out. (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()
        }
    }
    

    Invoke this with a Deadlined context:

    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)
        }
    }
    
    0 讨论(0)
提交回复
热议问题