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 }