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