func (c *Caller) CallNodeWithContext(
	ctx context.Context, node proto.NodeID, method string, args, reply interface{}) (err error,
) {
	startTime := time.Now()
	defer func() {
		recordRPCCost(startTime, method, err)
	}()
	client, err := DialToNodeWithPool(c.pool, node, method == route.DHTPing.String())
	if err != nil {
		err = errors.Wrapf(err, "dial to node %s failed", node)
		return
	}
	defer func() { _ = client.Close() }()
	ch := client.Go(method, args, reply, make(chan *rpc.Call, 1))
	select {
	case <-ctx.Done():
		err = ctx.Err()
	case call := <-ch.Done:
		err = call.Error
		if err != nil {  
			if setter, ok := client.(LastErrSetter); ok {
				setter.SetLastErr(err)
			}
		}
	}
	return
}
CallNodeWithContext calls node method with context.
package rpc
import (
	"context"
	"expvar"
	"net/rpc"
	"sync"
	"time"
	"github.com/pkg/errors"
	mw "github.com/zserge/metric"
	"github.com/SQLess/SQLess/proto"
	"github.com/SQLess/SQLess/route"
)
var (
	callRPCExpvarLock sync.Mutex
)
func recordRPCCost(startTime time.Time, method string, err error) {
	var (
		name, nameC string
		val, valC   expvar.Var
	)
	costTime := time.Since(startTime)
	if err == nil {
		name = "t_succ:" + method
		nameC = "c_succ:" + method
	} else {
		name = "t_fail:" + method
		nameC = "c_fail:" + method
	}
	val = expvar.Get(name)
	valC = expvar.Get(nameC)
	if val == nil || valC == nil {
		callRPCExpvarLock.Lock()
		val = expvar.Get(name)
		if val == nil {
			expvar.Publish(name, mw.NewHistogram("10s1s", "1m5s", "1h1m"))
			expvar.Publish(nameC, mw.NewCounter("10s1s", "1h1m"))
		}
		callRPCExpvarLock.Unlock()
		val = expvar.Get(name)
		valC = expvar.Get(nameC)
	}
	val.(mw.Metric).Add(costTime.Seconds())
	valC.(mw.Metric).Add(1)
	return
}
type Caller struct {
	pool NOClientPool
}
func NewCallerWithPool(pool NOClientPool) *Caller {
	return &Caller{
		pool: pool,
	}
}