github.com/kafkaliu/etcd@v0.1.2-0.20131007164923-44c16dd30d69/transporter.go (about)

     1  /*
     2  Copyright 2013 CoreOS Inc.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8       http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"bytes"
    21  	"crypto/tls"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io"
    25  	"net"
    26  	"net/http"
    27  	"time"
    28  
    29  	"github.com/coreos/go-raft"
    30  )
    31  
    32  // Timeout for setup internal raft http connection
    33  // This should not exceed 3 * RTT
    34  var dailTimeout = 3 * HeartbeatTimeout
    35  
    36  // Timeout for setup internal raft http connection + receive response header
    37  // This should not exceed 3 * RTT + RTT
    38  var responseHeaderTimeout = 4 * HeartbeatTimeout
    39  
    40  // Timeout for receiving the response body from the server
    41  // This should not exceed election timeout
    42  var tranTimeout = ElectionTimeout
    43  
    44  // Transporter layer for communication between raft nodes
    45  type transporter struct {
    46  	client    *http.Client
    47  	transport *http.Transport
    48  }
    49  
    50  // Create transporter using by raft server
    51  // Create http or https transporter based on
    52  // whether the user give the server cert and key
    53  func newTransporter(scheme string, tlsConf tls.Config) *transporter {
    54  	t := transporter{}
    55  
    56  	tr := &http.Transport{
    57  		Dial: dialWithTimeout,
    58  		ResponseHeaderTimeout: responseHeaderTimeout,
    59  	}
    60  
    61  	if scheme == "https" {
    62  		tr.TLSClientConfig = &tlsConf
    63  		tr.DisableCompression = true
    64  	}
    65  
    66  	t.client = &http.Client{Transport: tr}
    67  	t.transport = tr
    68  
    69  	return &t
    70  }
    71  
    72  // Dial with timeout
    73  func dialWithTimeout(network, addr string) (net.Conn, error) {
    74  	return net.DialTimeout(network, addr, dailTimeout)
    75  }
    76  
    77  // Sends AppendEntries RPCs to a peer when the server is the leader.
    78  func (t *transporter) SendAppendEntriesRequest(server *raft.Server, peer *raft.Peer, req *raft.AppendEntriesRequest) *raft.AppendEntriesResponse {
    79  	var aersp *raft.AppendEntriesResponse
    80  	var b bytes.Buffer
    81  
    82  	json.NewEncoder(&b).Encode(req)
    83  
    84  	size := b.Len()
    85  
    86  	r.serverStats.SendAppendReq(size)
    87  
    88  	u, _ := nameToRaftURL(peer.Name)
    89  
    90  	debugf("Send LogEntries to %s ", u)
    91  
    92  	thisFollowerStats, ok := r.followersStats.Followers[peer.Name]
    93  
    94  	if !ok { //this is the first time this follower has been seen
    95  		thisFollowerStats = &raftFollowerStats{}
    96  		thisFollowerStats.Latency.Minimum = 1 << 63
    97  		r.followersStats.Followers[peer.Name] = thisFollowerStats
    98  	}
    99  
   100  	start := time.Now()
   101  
   102  	resp, httpRequest, err := t.Post(fmt.Sprintf("%s/log/append", u), &b)
   103  
   104  	end := time.Now()
   105  
   106  	if err != nil {
   107  		debugf("Cannot send AppendEntriesRequest to %s: %s", u, err)
   108  		if ok {
   109  			thisFollowerStats.Fail()
   110  		}
   111  	} else {
   112  		if ok {
   113  			thisFollowerStats.Succ(end.Sub(start))
   114  		}
   115  	}
   116  
   117  	if resp != nil {
   118  		defer resp.Body.Close()
   119  
   120  		t.CancelWhenTimeout(httpRequest)
   121  
   122  		aersp = &raft.AppendEntriesResponse{}
   123  		if err := json.NewDecoder(resp.Body).Decode(&aersp); err == nil || err == io.EOF {
   124  			return aersp
   125  		}
   126  
   127  	}
   128  
   129  	return aersp
   130  }
   131  
   132  // Sends RequestVote RPCs to a peer when the server is the candidate.
   133  func (t *transporter) SendVoteRequest(server *raft.Server, peer *raft.Peer, req *raft.RequestVoteRequest) *raft.RequestVoteResponse {
   134  	var rvrsp *raft.RequestVoteResponse
   135  	var b bytes.Buffer
   136  	json.NewEncoder(&b).Encode(req)
   137  
   138  	u, _ := nameToRaftURL(peer.Name)
   139  	debugf("Send Vote to %s", u)
   140  
   141  	resp, httpRequest, err := t.Post(fmt.Sprintf("%s/vote", u), &b)
   142  
   143  	if err != nil {
   144  		debugf("Cannot send VoteRequest to %s : %s", u, err)
   145  	}
   146  
   147  	if resp != nil {
   148  		defer resp.Body.Close()
   149  
   150  		t.CancelWhenTimeout(httpRequest)
   151  
   152  		rvrsp := &raft.RequestVoteResponse{}
   153  		if err := json.NewDecoder(resp.Body).Decode(&rvrsp); err == nil || err == io.EOF {
   154  			return rvrsp
   155  		}
   156  
   157  	}
   158  	return rvrsp
   159  }
   160  
   161  // Sends SnapshotRequest RPCs to a peer when the server is the candidate.
   162  func (t *transporter) SendSnapshotRequest(server *raft.Server, peer *raft.Peer, req *raft.SnapshotRequest) *raft.SnapshotResponse {
   163  	var aersp *raft.SnapshotResponse
   164  	var b bytes.Buffer
   165  	json.NewEncoder(&b).Encode(req)
   166  
   167  	u, _ := nameToRaftURL(peer.Name)
   168  	debugf("Send Snapshot to %s [Last Term: %d, LastIndex %d]", u,
   169  		req.LastTerm, req.LastIndex)
   170  
   171  	resp, httpRequest, err := t.Post(fmt.Sprintf("%s/snapshot", u), &b)
   172  
   173  	if err != nil {
   174  		debugf("Cannot send SendSnapshotRequest to %s : %s", u, err)
   175  	}
   176  
   177  	if resp != nil {
   178  		defer resp.Body.Close()
   179  
   180  		t.CancelWhenTimeout(httpRequest)
   181  
   182  		aersp = &raft.SnapshotResponse{}
   183  		if err = json.NewDecoder(resp.Body).Decode(&aersp); err == nil || err == io.EOF {
   184  
   185  			return aersp
   186  		}
   187  	}
   188  
   189  	return aersp
   190  }
   191  
   192  // Sends SnapshotRecoveryRequest RPCs to a peer when the server is the candidate.
   193  func (t *transporter) SendSnapshotRecoveryRequest(server *raft.Server, peer *raft.Peer, req *raft.SnapshotRecoveryRequest) *raft.SnapshotRecoveryResponse {
   194  	var aersp *raft.SnapshotRecoveryResponse
   195  	var b bytes.Buffer
   196  	json.NewEncoder(&b).Encode(req)
   197  
   198  	u, _ := nameToRaftURL(peer.Name)
   199  	debugf("Send SnapshotRecovery to %s [Last Term: %d, LastIndex %d]", u,
   200  		req.LastTerm, req.LastIndex)
   201  
   202  	resp, _, err := t.Post(fmt.Sprintf("%s/snapshotRecovery", u), &b)
   203  
   204  	if err != nil {
   205  		debugf("Cannot send SendSnapshotRecoveryRequest to %s : %s", u, err)
   206  	}
   207  
   208  	if resp != nil {
   209  		defer resp.Body.Close()
   210  		aersp = &raft.SnapshotRecoveryResponse{}
   211  
   212  		if err = json.NewDecoder(resp.Body).Decode(&aersp); err == nil || err == io.EOF {
   213  			return aersp
   214  		}
   215  	}
   216  
   217  	return aersp
   218  }
   219  
   220  // Send server side POST request
   221  func (t *transporter) Post(urlStr string, body io.Reader) (*http.Response, *http.Request, error) {
   222  
   223  	req, _ := http.NewRequest("POST", urlStr, body)
   224  
   225  	resp, err := t.client.Do(req)
   226  
   227  	return resp, req, err
   228  
   229  }
   230  
   231  // Send server side GET request
   232  func (t *transporter) Get(urlStr string) (*http.Response, *http.Request, error) {
   233  
   234  	req, _ := http.NewRequest("GET", urlStr, nil)
   235  
   236  	resp, err := t.client.Do(req)
   237  
   238  	return resp, req, err
   239  }
   240  
   241  // Cancel the on fly HTTP transaction when timeout happens
   242  func (t *transporter) CancelWhenTimeout(req *http.Request) {
   243  	go func() {
   244  		time.Sleep(ElectionTimeout)
   245  		t.transport.CancelRequest(req)
   246  	}()
   247  }