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  }