github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/network/tunnel/mucp/link.go (about)

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