github.com/micro/go-micro/v2@v2.9.1/tunnel/link.go (about)

     1  package tunnel
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/google/uuid"
    11  	"github.com/micro/go-micro/v2/logger"
    12  	"github.com/micro/go-micro/v2/transport"
    13  )
    14  
    15  type link struct {
    16  	transport.Socket
    17  
    18  	// transport to use for connections
    19  	transport transport.Transport
    20  
    21  	sync.RWMutex
    22  
    23  	// stops the link
    24  	closed chan bool
    25  	// metric used to track metrics
    26  	metric chan *metric
    27  	// link state channel for testing link
    28  	state chan *packet
    29  	// send queue for sending packets
    30  	sendQueue chan *packet
    31  	// receive queue for receiving packets
    32  	recvQueue chan *packet
    33  	// unique id of this link e.g uuid
    34  	// which we define for ourselves
    35  	id string
    36  	// whether its a loopback connection
    37  	// this flag is used by the transport listener
    38  	// which accepts inbound quic connections
    39  	loopback bool
    40  	// whether its actually connected
    41  	// dialled side sets it to connected
    42  	// after sending the message. the
    43  	// listener waits for the connect
    44  	connected bool
    45  	// the last time we received a keepalive
    46  	// on this link from the remote side
    47  	lastKeepAlive time.Time
    48  	// channels keeps a mapping of channels and last seen
    49  	channels map[string]time.Time
    50  	// the weighted moving average roundtrip
    51  	length int64
    52  	// weighted moving average of bits flowing
    53  	rate float64
    54  	// keep an error count on the link
    55  	errCount int
    56  }
    57  
    58  // packet send over link
    59  type packet struct {
    60  	// message to send or received
    61  	message *transport.Message
    62  
    63  	// status returned when sent
    64  	status chan error
    65  
    66  	// receive related error
    67  	err error
    68  }
    69  
    70  // metric is used to record link rate
    71  type metric struct {
    72  	// amount of data sent
    73  	data int
    74  	// time taken to send
    75  	duration time.Duration
    76  	// if an error occurred
    77  	status error
    78  }
    79  
    80  var (
    81  	// the 4 byte 0 packet sent to determine the link state
    82  	linkRequest = []byte{0, 0, 0, 0}
    83  	// the 4 byte 1 filled packet sent to determine link state
    84  	linkResponse = []byte{1, 1, 1, 1}
    85  
    86  	ErrLinkConnectTimeout = errors.New("link connect timeout")
    87  )
    88  
    89  func newLink(s transport.Socket) *link {
    90  	l := &link{
    91  		Socket:        s,
    92  		id:            uuid.New().String(),
    93  		lastKeepAlive: time.Now(),
    94  		closed:        make(chan bool),
    95  		channels:      make(map[string]time.Time),
    96  		state:         make(chan *packet, 64),
    97  		sendQueue:     make(chan *packet, 128),
    98  		recvQueue:     make(chan *packet, 128),
    99  		metric:        make(chan *metric, 128),
   100  	}
   101  
   102  	// process inbound/outbound packets
   103  	go l.process()
   104  	// manage the link state
   105  	go l.manage()
   106  
   107  	return l
   108  }
   109  
   110  // setRate sets the bits per second rate as a float64
   111  func (l *link) setRate(bits int64, delta time.Duration) {
   112  	// rate of send in bits per nanosecond
   113  	rate := float64(bits) / float64(delta.Nanoseconds())
   114  
   115  	// default the rate if its zero
   116  	if l.rate == 0 {
   117  		// rate per second
   118  		l.rate = rate * 1e9
   119  	} else {
   120  		// set new rate per second
   121  		l.rate = 0.8*l.rate + 0.2*(rate*1e9)
   122  	}
   123  }
   124  
   125  // setRTT sets a nanosecond based moving average roundtrip time for the link
   126  func (l *link) setRTT(d time.Duration) {
   127  	l.Lock()
   128  
   129  	if l.length <= 0 {
   130  		l.length = d.Nanoseconds()
   131  		l.Unlock()
   132  		return
   133  	}
   134  
   135  	// https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/
   136  	length := 0.8*float64(l.length) + 0.2*float64(d.Nanoseconds())
   137  	// set new length
   138  	l.length = int64(length)
   139  
   140  	l.Unlock()
   141  }
   142  
   143  func (l *link) delChannel(ch string) {
   144  	l.Lock()
   145  	delete(l.channels, ch)
   146  	l.Unlock()
   147  }
   148  
   149  func (l *link) getChannel(ch string) time.Time {
   150  	l.RLock()
   151  	t := l.channels[ch]
   152  	l.RUnlock()
   153  	return t
   154  }
   155  
   156  func (l *link) setChannel(channels ...string) {
   157  	l.Lock()
   158  	for _, ch := range channels {
   159  		l.channels[ch] = time.Now()
   160  	}
   161  	l.Unlock()
   162  }
   163  
   164  // set the keepalive time
   165  func (l *link) keepalive() {
   166  	l.Lock()
   167  	l.lastKeepAlive = time.Now()
   168  	l.Unlock()
   169  }
   170  
   171  // process deals with the send queue
   172  func (l *link) process() {
   173  	// receive messages
   174  	go func() {
   175  		for {
   176  			m := new(transport.Message)
   177  			err := l.recv(m)
   178  			if err != nil {
   179  				// record the metric
   180  				select {
   181  				case l.metric <- &metric{status: err}:
   182  				default:
   183  				}
   184  			}
   185  
   186  			// process new received message
   187  
   188  			pk := &packet{message: m, err: err}
   189  
   190  			// this is our link state packet
   191  			if m.Header["Micro-Method"] == "link" {
   192  				// process link state message
   193  				select {
   194  				case l.state <- pk:
   195  				case <-l.closed:
   196  					return
   197  				default:
   198  				}
   199  				continue
   200  			}
   201  
   202  			// process all messages as is
   203  
   204  			select {
   205  			case l.recvQueue <- pk:
   206  			case <-l.closed:
   207  				return
   208  			}
   209  		}
   210  	}()
   211  
   212  	// send messages
   213  
   214  	for {
   215  		select {
   216  		case pk := <-l.sendQueue:
   217  			// send the message
   218  			select {
   219  			case pk.status <- l.send(pk.message):
   220  			case <-l.closed:
   221  				return
   222  			}
   223  		case <-l.closed:
   224  			return
   225  		}
   226  	}
   227  }
   228  
   229  // manage manages the link state including rtt packets and channel mapping expiry
   230  func (l *link) manage() {
   231  	// tick over every minute to expire and fire rtt packets
   232  	t1 := time.NewTicker(time.Minute)
   233  	defer t1.Stop()
   234  
   235  	// used to batch update link metrics
   236  	t2 := time.NewTicker(time.Second * 5)
   237  	defer t2.Stop()
   238  
   239  	// get link id
   240  	linkId := l.Id()
   241  
   242  	// used to send link state packets
   243  	send := func(b []byte) error {
   244  		return l.Send(&transport.Message{
   245  			Header: map[string]string{
   246  				"Micro-Method":  "link",
   247  				"Micro-Link-Id": linkId,
   248  			}, Body: b,
   249  		})
   250  	}
   251  
   252  	// set time now
   253  	now := time.Now()
   254  
   255  	// send the initial rtt request packet
   256  	send(linkRequest)
   257  
   258  	for {
   259  		select {
   260  		// exit if closed
   261  		case <-l.closed:
   262  			return
   263  		// process link state rtt packets
   264  		case p := <-l.state:
   265  			if p.err != nil {
   266  				continue
   267  			}
   268  			// check the type of message
   269  			switch {
   270  			case bytes.Equal(p.message.Body, linkRequest):
   271  				if logger.V(logger.TraceLevel, log) {
   272  					log.Tracef("Link %s received link request", linkId)
   273  				}
   274  				// send response
   275  				if err := send(linkResponse); err != nil {
   276  					l.Lock()
   277  					l.errCount++
   278  					l.Unlock()
   279  				}
   280  			case bytes.Equal(p.message.Body, linkResponse):
   281  				// set round trip time
   282  				d := time.Since(now)
   283  				if logger.V(logger.TraceLevel, log) {
   284  					log.Tracef("Link %s received link response in %v", linkId, d)
   285  				}
   286  				// set the RTT
   287  				l.setRTT(d)
   288  			}
   289  		case <-t1.C:
   290  			// drop any channel mappings older than 2 minutes
   291  			var kill []string
   292  			killTime := time.Minute * 2
   293  
   294  			l.RLock()
   295  			for ch, t := range l.channels {
   296  				if d := time.Since(t); d > killTime {
   297  					kill = append(kill, ch)
   298  				}
   299  			}
   300  			l.RUnlock()
   301  
   302  			// if nothing to kill don't bother with a wasted lock
   303  			if len(kill) == 0 {
   304  				continue
   305  			}
   306  
   307  			// kill the channels!
   308  			l.Lock()
   309  			for _, ch := range kill {
   310  				delete(l.channels, ch)
   311  			}
   312  			l.Unlock()
   313  
   314  			// fire off a link state rtt packet
   315  			now = time.Now()
   316  			send(linkRequest)
   317  		case <-t2.C:
   318  			// get a batch of metrics
   319  			batch := l.batch()
   320  
   321  			// skip if there's no metrics
   322  			if len(batch) == 0 {
   323  				continue
   324  			}
   325  
   326  			// lock once to record a batch
   327  			l.Lock()
   328  			for _, metric := range batch {
   329  				l.record(metric)
   330  			}
   331  			l.Unlock()
   332  		}
   333  	}
   334  }
   335  
   336  func (l *link) batch() []*metric {
   337  	var metrics []*metric
   338  
   339  	// pull all the metrics
   340  	for {
   341  		select {
   342  		case m := <-l.metric:
   343  			metrics = append(metrics, m)
   344  		// non blocking return
   345  		default:
   346  			return metrics
   347  		}
   348  	}
   349  }
   350  
   351  func (l *link) record(m *metric) {
   352  	// there's an error increment the counter and bail
   353  	if m.status != nil {
   354  		l.errCount++
   355  		return
   356  	}
   357  
   358  	// reset the counter
   359  	l.errCount = 0
   360  
   361  	// calculate based on data
   362  	if m.data > 0 {
   363  		// bit sent
   364  		bits := m.data * 1024
   365  
   366  		// set the rate
   367  		l.setRate(int64(bits), m.duration)
   368  	}
   369  }
   370  
   371  func (l *link) send(m *transport.Message) error {
   372  	if m.Header == nil {
   373  		m.Header = make(map[string]string)
   374  	}
   375  	// send the message
   376  	return l.Socket.Send(m)
   377  }
   378  
   379  // recv a message on the link
   380  func (l *link) recv(m *transport.Message) error {
   381  	if m.Header == nil {
   382  		m.Header = make(map[string]string)
   383  	}
   384  	// receive the transport message
   385  	return l.Socket.Recv(m)
   386  }
   387  
   388  // Delay is the current load on the link
   389  func (l *link) Delay() int64 {
   390  	return int64(len(l.sendQueue) + len(l.recvQueue))
   391  }
   392  
   393  // Current transfer rate as bits per second (lower is better)
   394  func (l *link) Rate() float64 {
   395  	l.RLock()
   396  	r := l.rate
   397  	l.RUnlock()
   398  	return r
   399  }
   400  
   401  func (l *link) Loopback() bool {
   402  	l.RLock()
   403  	lo := l.loopback
   404  	l.RUnlock()
   405  	return lo
   406  }
   407  
   408  // Length returns the roundtrip time as nanoseconds (lower is better).
   409  // Returns 0 where no measurement has been taken.
   410  func (l *link) Length() int64 {
   411  	l.RLock()
   412  	length := l.length
   413  	l.RUnlock()
   414  	return length
   415  }
   416  
   417  func (l *link) Id() string {
   418  	l.RLock()
   419  	id := l.id
   420  	l.RUnlock()
   421  	return id
   422  }
   423  
   424  func (l *link) Close() error {
   425  	l.Lock()
   426  	defer l.Unlock()
   427  
   428  	select {
   429  	case <-l.closed:
   430  		return nil
   431  	default:
   432  		l.Socket.Close()
   433  		close(l.closed)
   434  	}
   435  
   436  	return nil
   437  }
   438  
   439  // Send sencs a message on the link
   440  func (l *link) Send(m *transport.Message) error {
   441  	// create a new packet to send over the link
   442  	p := &packet{
   443  		message: m,
   444  		status:  make(chan error, 1),
   445  	}
   446  
   447  	// calculate the data sent
   448  	dataSent := len(m.Body)
   449  
   450  	// set header length
   451  	for k, v := range m.Header {
   452  		dataSent += (len(k) + len(v))
   453  	}
   454  
   455  	// get time now
   456  	now := time.Now()
   457  
   458  	// queue the message
   459  	select {
   460  	case l.sendQueue <- p:
   461  		// in the send queue
   462  	case <-l.closed:
   463  		return io.EOF
   464  	}
   465  
   466  	// error to use
   467  	var err error
   468  
   469  	// wait for response
   470  	select {
   471  	case <-l.closed:
   472  		return io.EOF
   473  	case err = <-p.status:
   474  	}
   475  
   476  	// create a metric with
   477  	// time taken, size of package, error status
   478  	mt := &metric{
   479  		data:     dataSent,
   480  		duration: time.Since(now),
   481  		status:   err,
   482  	}
   483  
   484  	// pass back a metric
   485  	// do not block
   486  	select {
   487  	case l.metric <- mt:
   488  	default:
   489  	}
   490  
   491  	return nil
   492  }
   493  
   494  // Accept accepts a message on the socket
   495  func (l *link) Recv(m *transport.Message) error {
   496  	select {
   497  	case <-l.closed:
   498  		// check if there's any messages left
   499  		select {
   500  		case pk := <-l.recvQueue:
   501  			// check the packet receive error
   502  			if pk.err != nil {
   503  				return pk.err
   504  			}
   505  			*m = *pk.message
   506  		default:
   507  			return io.EOF
   508  		}
   509  	case pk := <-l.recvQueue:
   510  		// check the packet receive error
   511  		if pk.err != nil {
   512  			return pk.err
   513  		}
   514  		*m = *pk.message
   515  	}
   516  	return nil
   517  }
   518  
   519  // State can return connected, closed, error
   520  func (l *link) State() string {
   521  	select {
   522  	case <-l.closed:
   523  		return "closed"
   524  	default:
   525  		l.RLock()
   526  		errCount := l.errCount
   527  		l.RUnlock()
   528  
   529  		if errCount > 3 {
   530  			return "error"
   531  		}
   532  
   533  		return "connected"
   534  	}
   535  }