Does RPC have a timeout mechanism?

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?

5 Answers

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.

5

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) 

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 , which supports timeout RPC calls out of the box.

1
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error 

Call method may block goroutine forever

Change use Go method:

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call 

Client example:

call := rpcClient.Go(method, args, reply, make(chan *rpc.Call, 1)) select { case <-time.After(timeout): log.Printf("[WARN] rpc call timeout(%v) %v => %v", timeout, rpcClient, s.RpcServer) rpcClient.Close() return errors.New("timeout") case resp := <-call.Done: if resp != nil && resp.Error != nil { rpcClient.Close() return resp.Error } 

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) } } 
1

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like