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 }