github.com/matm/etcd@v0.3.1-0.20140328024009-5b4a473f1453/server/transporter.go (about) 1 package server 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "fmt" 7 "io" 8 "net" 9 "net/http" 10 "time" 11 12 "github.com/coreos/etcd/log" 13 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 14 httpclient "github.com/coreos/etcd/third_party/github.com/mreiferson/go-httpclient" 15 ) 16 17 const ( 18 snapshotTimeout = time.Second * 120 19 ) 20 21 // Transporter layer for communication between raft nodes 22 type transporter struct { 23 requestTimeout time.Duration 24 followersStats *raftFollowersStats 25 serverStats *raftServerStats 26 registry *Registry 27 28 client *http.Client 29 transport *httpclient.Transport 30 snapshotClient *http.Client 31 snapshotTransport *httpclient.Transport 32 } 33 34 type dialer func(network, addr string) (net.Conn, error) 35 36 // Create transporter using by raft server 37 // Create http or https transporter based on 38 // whether the user give the server cert and key 39 func NewTransporter(followersStats *raftFollowersStats, serverStats *raftServerStats, registry *Registry, dialTimeout, requestTimeout, responseHeaderTimeout time.Duration) *transporter { 40 tr := &httpclient.Transport{ 41 ResponseHeaderTimeout: responseHeaderTimeout, 42 // This is a workaround for Transport.CancelRequest doesn't work on 43 // HTTPS connections blocked. The patch for it is in progress, 44 // and would be available in Go1.3 45 // More: https://codereview.appspot.com/69280043/ 46 ConnectTimeout: dialTimeout, 47 RequestTimeout: dialTimeout + responseHeaderTimeout, 48 ReadWriteTimeout: responseHeaderTimeout, 49 } 50 51 // Sending snapshot might take a long time so we use a different HTTP transporter 52 // Timeout is set to 120s (Around 100MB if the bandwidth is 10Mbits/s) 53 // This timeout is not calculated by heartbeat time. 54 // TODO(xiangl1) we can actually calculate the max bandwidth if we know 55 // average RTT. 56 // It should be equal to (TCP max window size/RTT). 57 sTr := &httpclient.Transport{ 58 ConnectTimeout: dialTimeout, 59 RequestTimeout: snapshotTimeout, 60 ReadWriteTimeout: snapshotTimeout, 61 } 62 63 t := transporter{ 64 client: &http.Client{Transport: tr}, 65 transport: tr, 66 snapshotClient: &http.Client{Transport: sTr}, 67 snapshotTransport: sTr, 68 requestTimeout: requestTimeout, 69 followersStats: followersStats, 70 serverStats: serverStats, 71 registry: registry, 72 } 73 74 return &t 75 } 76 77 func (t *transporter) SetTLSConfig(tlsConf tls.Config) { 78 t.transport.TLSClientConfig = &tlsConf 79 t.transport.DisableCompression = true 80 81 t.snapshotTransport.TLSClientConfig = &tlsConf 82 t.snapshotTransport.DisableCompression = true 83 } 84 85 // Sends AppendEntries RPCs to a peer when the server is the leader. 86 func (t *transporter) SendAppendEntriesRequest(server raft.Server, peer *raft.Peer, req *raft.AppendEntriesRequest) *raft.AppendEntriesResponse { 87 var b bytes.Buffer 88 89 if _, err := req.Encode(&b); err != nil { 90 log.Warn("transporter.ae.encoding.error:", err) 91 return nil 92 } 93 94 size := b.Len() 95 96 t.serverStats.SendAppendReq(size) 97 98 u, _ := t.registry.PeerURL(peer.Name) 99 100 log.Debugf("Send LogEntries to %s ", u) 101 102 thisFollowerStats, ok := t.followersStats.Followers[peer.Name] 103 104 if !ok { //this is the first time this follower has been seen 105 thisFollowerStats = &raftFollowerStats{} 106 thisFollowerStats.Latency.Minimum = 1 << 63 107 t.followersStats.Followers[peer.Name] = thisFollowerStats 108 } 109 110 start := time.Now() 111 112 resp, _, err := t.Post(fmt.Sprintf("%s/log/append", u), &b) 113 114 end := time.Now() 115 116 if err != nil { 117 log.Debugf("Cannot send AppendEntriesRequest to %s: %s", u, err) 118 if ok { 119 thisFollowerStats.Fail() 120 } 121 return nil 122 } else { 123 if ok { 124 thisFollowerStats.Succ(end.Sub(start)) 125 } 126 } 127 128 if resp != nil { 129 defer resp.Body.Close() 130 131 aeresp := &raft.AppendEntriesResponse{} 132 if _, err = aeresp.Decode(resp.Body); err != nil && err != io.EOF { 133 log.Warn("transporter.ae.decoding.error:", err) 134 return nil 135 } 136 return aeresp 137 } 138 139 return nil 140 } 141 142 // Sends RequestVote RPCs to a peer when the server is the candidate. 143 func (t *transporter) SendVoteRequest(server raft.Server, peer *raft.Peer, req *raft.RequestVoteRequest) *raft.RequestVoteResponse { 144 var b bytes.Buffer 145 146 if _, err := req.Encode(&b); err != nil { 147 log.Warn("transporter.vr.encoding.error:", err) 148 return nil 149 } 150 151 u, _ := t.registry.PeerURL(peer.Name) 152 log.Debugf("Send Vote from %s to %s", server.Name(), u) 153 154 resp, _, err := t.Post(fmt.Sprintf("%s/vote", u), &b) 155 156 if err != nil { 157 log.Debugf("Cannot send VoteRequest to %s : %s", u, err) 158 } 159 160 if resp != nil { 161 defer resp.Body.Close() 162 163 rvrsp := &raft.RequestVoteResponse{} 164 if _, err = rvrsp.Decode(resp.Body); err != nil && err != io.EOF { 165 log.Warn("transporter.vr.decoding.error:", err) 166 return nil 167 } 168 return rvrsp 169 } 170 return nil 171 } 172 173 // Sends SnapshotRequest RPCs to a peer when the server is the candidate. 174 func (t *transporter) SendSnapshotRequest(server raft.Server, peer *raft.Peer, req *raft.SnapshotRequest) *raft.SnapshotResponse { 175 var b bytes.Buffer 176 177 if _, err := req.Encode(&b); err != nil { 178 log.Warn("transporter.ss.encoding.error:", err) 179 return nil 180 } 181 182 u, _ := t.registry.PeerURL(peer.Name) 183 log.Debugf("Send Snapshot Request from %s to %s", server.Name(), u) 184 185 resp, _, err := t.Post(fmt.Sprintf("%s/snapshot", u), &b) 186 187 if err != nil { 188 log.Debugf("Cannot send Snapshot Request to %s : %s", u, err) 189 } 190 191 if resp != nil { 192 defer resp.Body.Close() 193 194 ssrsp := &raft.SnapshotResponse{} 195 if _, err = ssrsp.Decode(resp.Body); err != nil && err != io.EOF { 196 log.Warn("transporter.ss.decoding.error:", err) 197 return nil 198 } 199 return ssrsp 200 } 201 return nil 202 } 203 204 // Sends SnapshotRecoveryRequest RPCs to a peer when the server is the candidate. 205 func (t *transporter) SendSnapshotRecoveryRequest(server raft.Server, peer *raft.Peer, req *raft.SnapshotRecoveryRequest) *raft.SnapshotRecoveryResponse { 206 var b bytes.Buffer 207 208 if _, err := req.Encode(&b); err != nil { 209 log.Warn("transporter.ss.encoding.error:", err) 210 return nil 211 } 212 213 u, _ := t.registry.PeerURL(peer.Name) 214 log.Debugf("Send Snapshot Recovery from %s to %s", server.Name(), u) 215 216 resp, err := t.PostSnapshot(fmt.Sprintf("%s/snapshotRecovery", u), &b) 217 218 if err != nil { 219 log.Debugf("Cannot send Snapshot Recovery to %s : %s", u, err) 220 } 221 222 if resp != nil { 223 defer resp.Body.Close() 224 225 ssrrsp := &raft.SnapshotRecoveryResponse{} 226 if _, err = ssrrsp.Decode(resp.Body); err != nil && err != io.EOF { 227 log.Warn("transporter.ssr.decoding.error:", err) 228 return nil 229 } 230 return ssrrsp 231 } 232 return nil 233 234 } 235 236 // Send server side POST request 237 func (t *transporter) Post(urlStr string, body io.Reader) (*http.Response, *http.Request, error) { 238 req, _ := http.NewRequest("POST", urlStr, body) 239 resp, err := t.client.Do(req) 240 return resp, req, err 241 } 242 243 // Send server side GET request 244 func (t *transporter) Get(urlStr string) (*http.Response, *http.Request, error) { 245 req, _ := http.NewRequest("GET", urlStr, nil) 246 resp, err := t.client.Do(req) 247 return resp, req, err 248 } 249 250 // PostSnapshot posts a json format snapshot to the given url 251 // The underlying HTTP transport has a minute level timeout 252 func (t *transporter) PostSnapshot(url string, body io.Reader) (*http.Response, error) { 253 return t.snapshotClient.Post(url, "application/json", body) 254 }