英文:
Does RPC have a timeout mechanism?
问题
如果RPC没有超时机制,那么如果它试图调用一个已关闭的服务器的RPC方法,我该如何“终止”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?
答案1
得分: 10
你可以使用channels来实现超时模式:
import "time"
c := make(chan error, 1)
go func() { c <- client.Call("Service", args, &result) }()
select {
case err := <-c:
// 使用 err 和 result
case <-time.After(timeoutNanoseconds):
// 调用超时
}
select
语句将会阻塞,直到client.Call
返回或者超过了timeoutNanoseconds
指定的时间。
英文:
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.
答案2
得分: 4
如果你想实现超时功能(以防止调用时间过长),那么你需要将rpc.Dial更改为net.DialTimeout(注意它们是不同的包:rpc vs net)。还要注意返回的类型不再是客户端(如前面的示例中所示),而是一个“连接”。
conn, err := net.DialTimeout("tcp", "localhost:8080", time.Minute)
if err != nil {
log.Fatal("dialing:", err)
}
client := rpc.NewClient(conn)
英文:
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)
答案3
得分: 1
似乎 net/rpc 的唯一解决方案是在发现请求卡住时关闭底层连接。然后客户端应该使用“连接中断”错误完成挂起的请求。
另一种方法是使用 https://github.com/valyala/gorpc,它支持开箱即用的超时 RPC 调用。
英文:
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.
答案4
得分: 1
func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error
调用方法可能会永远阻塞goroutine
改用Go方法:
func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call
客户端示例:
call := rpcClient.Go(method, args, reply, make(chan *rpc.Call, 1))
select {
case <-time.After(timeout):
log.Printf("[WARN] rpc调用超时(%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
}
英文:
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
}
答案5
得分: 0
或者,现在有人可能更喜欢使用上下文。这也可以处理在超时时返回适当的错误(context.DeadlineExceeded
)。
import (
"context"
"log"
"net/rpc"
)
type Client struct {
*rpc.Client
}
// CallEx是对rpc的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()
}
}
使用带有截止时间的上下文调用它:
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)
}
}
英文:
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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论