func (c *RawCaller) Close() {
	c.Lock()
	defer c.Unlock()
	if c.client != nil {
		_ = c.client.Close()
		c.client = nil
	}
}
Close release underlying connection resources.
package mux
import (
	"io"
	"net"
	nrpc "net/rpc"
	"strings"
	"sync"
	"github.com/pkg/errors"
	"github.com/SQLess/SQLess/conf"
	"github.com/SQLess/SQLess/rpc"
)
type RawCaller struct {
	targetAddr string
	client     *nrpc.Client
	sync.RWMutex
}
func NewRawCaller(targetAddr string) *RawCaller {
	return &RawCaller{
		targetAddr: targetAddr,
	}
}
func (c *RawCaller) isClientValid() bool {
	c.RLock()
	defer c.RUnlock()
	return c.client != nil
}
func (c *RawCaller) resetClient() (err error) {
	c.Lock()
	defer c.Unlock()
	if c.client != nil {
		_ = c.client.Close()
		c.client = nil
	}
	var conn net.Conn
	if conn, err = net.DialTimeout("tcp", c.targetAddr, conf.TCPDialTimeout); err != nil {
		err = errors.Wrapf(err, "dial to target %s failed", c.targetAddr)
		return
	}
	stream, err := NewOneOffMuxConn(conn)
	if err != nil {
		return
	}
	c.client = rpc.NewClient(stream)
	return
}
func (c *RawCaller) Call(method string, args interface{}, reply interface{}) (err error) {
	if !c.isClientValid() {
		if err = c.resetClient(); err != nil {
			return
		}
	}
	c.RLock()
	err = c.client.Call(method, args, reply)
	c.RUnlock()
	if err != nil {
		if err == io.EOF ||
			err == io.ErrUnexpectedEOF ||
			err == io.ErrClosedPipe ||
			err == nrpc.ErrShutdown ||
			strings.Contains(strings.ToLower(err.Error()), "shut down") ||
			strings.Contains(strings.ToLower(err.Error()), "broken pipe") {
			reconnectErr := c.resetClient()
			if reconnectErr != nil {
				err = errors.Wrap(reconnectErr, "reconnect failed")
			}
		}
		err = errors.Wrapf(err, "call %s failed", method)
	}
	return
}