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  }