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