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 }