github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/store/tikv/client.go (about)

     1  // Copyright 2016 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  // Package tikv provides tcp connection to kvserver.
    15  package tikv
    16  
    17  import (
    18  	"net"
    19  	"sync/atomic"
    20  	"time"
    21  
    22  	"github.com/insionng/yougam/libraries/juju/errors"
    23  	"github.com/insionng/yougam/libraries/ngaut/log"
    24  	"github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/coprocessor"
    25  	"github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/kvrpcpb"
    26  	"github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/msgpb"
    27  	"github.com/insionng/yougam/libraries/pingcap/kvproto/pkg/util"
    28  	"gopkg.in/fatih/pool.v2"
    29  )
    30  
    31  // Client is a client that sends RPC.
    32  // It should not be used after calling Close().
    33  type Client interface {
    34  	// Close should release all data.
    35  	Close() error
    36  	// SendKVReq sends kv request.
    37  	SendKVReq(req *kvrpcpb.Request) (*kvrpcpb.Response, error)
    38  	// SendCopReq sends coprocessor request.
    39  	SendCopReq(req *coprocessor.Request) (*coprocessor.Response, error)
    40  }
    41  
    42  // ClientFactory is a function that creates a Client with server address.
    43  type ClientFactory func(string) (Client, error)
    44  
    45  // rpcClient connects kvserver to send Request by TCP.
    46  type rpcClient struct {
    47  	dst   string
    48  	msgID uint64
    49  	pool  pool.Pool
    50  }
    51  
    52  const (
    53  	initConnection int           = 0
    54  	maxConnecion   int           = 20
    55  	readTimeout    time.Duration = 5 * time.Second // seconds
    56  	writeTimeout   time.Duration = 5 * time.Second // seconds
    57  	connectTimeout time.Duration = 5 * time.Second // seconds
    58  )
    59  
    60  // rpcBackoff is for RPC (with TiKV) retry.
    61  // It is expected to sleep for about 10s(+/-3s) in total before abort.
    62  func rpcBackoff() func() error {
    63  	const (
    64  		maxRetry  = 10
    65  		sleepBase = 100
    66  		sleepCap  = 2000
    67  	)
    68  	return NewBackoff(maxRetry, sleepBase, sleepCap, EqualJitter)
    69  }
    70  
    71  // NewRPCClient new client that sends protobuf to do RPC.
    72  func NewRPCClient(srvHost string) (Client, error) {
    73  	factory := func() (net.Conn, error) {
    74  		conn, err := net.DialTimeout("tcp", srvHost, connectTimeout)
    75  		if err != nil {
    76  			return nil, errors.Trace(err)
    77  		}
    78  		return conn, nil
    79  	}
    80  	p, err := pool.NewChannelPool(initConnection, maxConnecion, factory)
    81  	if err != nil {
    82  		return nil, errors.Errorf("new channel pool failed err[%s]", err)
    83  	}
    84  	return &rpcClient{
    85  		dst:   srvHost,
    86  		msgID: 0,
    87  		pool:  p,
    88  	}, nil
    89  }
    90  
    91  // SendCopReq sends a Request to co-processor and receives Response.
    92  func (c *rpcClient) SendCopReq(req *coprocessor.Request) (*coprocessor.Response, error) {
    93  	conn, err := c.pool.Get()
    94  	if err != nil {
    95  		return nil, errors.Trace(err)
    96  	}
    97  	defer conn.Close()
    98  	msg := msgpb.Message{
    99  		MsgType: msgpb.MessageType_CopReq.Enum(),
   100  		CopReq:  req,
   101  	}
   102  	err = c.doSend(conn, &msg)
   103  	if err == nil {
   104  		if msg.GetMsgType() != msgpb.MessageType_CopResp || msg.GetCopResp() == nil {
   105  			err = errors.Trace(errInvalidResponse)
   106  		}
   107  	}
   108  	if err != nil {
   109  		// This connection is not valid any more, so close its underlying conn.
   110  		if poolConn, ok := conn.(*pool.PoolConn); ok {
   111  			poolConn.MarkUnusable()
   112  		}
   113  		return nil, errors.Trace(err)
   114  	}
   115  	return msg.GetCopResp(), nil
   116  }
   117  
   118  // SendKVReq sends a Request to kv server and receives Response.
   119  func (c *rpcClient) SendKVReq(req *kvrpcpb.Request) (*kvrpcpb.Response, error) {
   120  	conn, err := c.pool.Get()
   121  	if err != nil {
   122  		return nil, errors.Trace(err)
   123  	}
   124  	defer conn.Close()
   125  	msg := msgpb.Message{
   126  		MsgType: msgpb.MessageType_KvReq.Enum(),
   127  		KvReq:   req,
   128  	}
   129  	err = c.doSend(conn, &msg)
   130  	if err == nil {
   131  		if msg.GetMsgType() != msgpb.MessageType_KvResp || msg.GetKvResp() == nil {
   132  			err = errors.Trace(errInvalidResponse)
   133  		}
   134  	}
   135  	if err != nil {
   136  		// This connection is not valid any more, so close its underlying conn.
   137  		if poolConn, ok := conn.(*pool.PoolConn); ok {
   138  			poolConn.MarkUnusable()
   139  		}
   140  		return nil, errors.Trace(err)
   141  	}
   142  	return msg.GetKvResp(), nil
   143  }
   144  
   145  // Close close client.
   146  func (c *rpcClient) Close() error {
   147  	c.pool.Close()
   148  	return nil
   149  }
   150  
   151  func (c *rpcClient) doSend(conn net.Conn, msg *msgpb.Message) error {
   152  	curMsgID := atomic.AddUint64(&c.msgID, 1)
   153  	log.Debugf("Send request msgID[%d] type[%v]", curMsgID, msg.GetMsgType())
   154  	if err := conn.SetWriteDeadline(time.Now().Add(writeTimeout)); err != nil {
   155  		log.Warn("Set write deadline failed, it may be blocked.")
   156  	}
   157  	if err := util.WriteMessage(conn, curMsgID, msg); err != nil {
   158  		return errors.Trace(err)
   159  	}
   160  	if err := conn.SetReadDeadline(time.Now().Add(readTimeout)); err != nil {
   161  		log.Warn("Set read deadline failed, it may be blocked.")
   162  	}
   163  	msgID, err := util.ReadMessage(conn, msg)
   164  	if err != nil {
   165  		return errors.Trace(err)
   166  	}
   167  	if curMsgID != msgID {
   168  		log.Errorf("Sent msgID[%d] mismatches recv msgID[%d]", curMsgID, msgID)
   169  	}
   170  	log.Debugf("Receive response msgID[%d] type[%v]", msgID, msg.GetMsgType())
   171  	return nil
   172  }