Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does RPC have a timeout mechanism?

Tags:

go

rpc

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?

like image 328
Aniruddh Chaturvedi Avatar asked Apr 27 '14 22:04

Aniruddh Chaturvedi


4 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.

like image 72
Sebastian Avatar answered Nov 19 '22 02:11

Sebastian


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)
like image 41
Alexander Milchinskiy Avatar answered Nov 19 '22 01:11

Alexander Milchinskiy


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.

like image 1
valyala Avatar answered Nov 19 '22 02:11

valyala


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
        }
like image 1
林期华 Avatar answered Nov 19 '22 00:11

林期华