Merge pull request #875 from micro/tun-measure
Measure roundtrip times on link
This commit is contained in:
commit
f07a6ac29b
@ -966,7 +966,7 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
|||||||
c.mode = options.Mode
|
c.mode = options.Mode
|
||||||
// set the dial timeout
|
// set the dial timeout
|
||||||
c.timeout = options.Timeout
|
c.timeout = options.Timeout
|
||||||
|
// get the current time
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
after := func() time.Duration {
|
after := func() time.Duration {
|
||||||
@ -980,6 +980,8 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var links []string
|
var links []string
|
||||||
|
// did we measure the rtt
|
||||||
|
var measured bool
|
||||||
|
|
||||||
// non multicast so we need to find the link
|
// non multicast so we need to find the link
|
||||||
if id := options.Link; id != "" {
|
if id := options.Link; id != "" {
|
||||||
@ -1021,6 +1023,9 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
|||||||
|
|
||||||
// shit fuck
|
// shit fuck
|
||||||
if !c.discovered {
|
if !c.discovered {
|
||||||
|
// piggy back roundtrip
|
||||||
|
nowRTT := time.Now()
|
||||||
|
|
||||||
// create a new discovery message for this channel
|
// create a new discovery message for this channel
|
||||||
msg := c.newMessage("discover")
|
msg := c.newMessage("discover")
|
||||||
msg.mode = Broadcast
|
msg.mode = Broadcast
|
||||||
@ -1075,12 +1080,30 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set roundtrip
|
||||||
|
d := time.Since(nowRTT)
|
||||||
|
|
||||||
|
// set the link time
|
||||||
|
t.RLock()
|
||||||
|
link, ok := t.links[c.link]
|
||||||
|
t.RUnlock()
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
// set the rountrip time
|
||||||
|
link.setRTT(d)
|
||||||
|
// set measured to true
|
||||||
|
measured = true
|
||||||
|
}
|
||||||
|
|
||||||
// set discovered to true
|
// set discovered to true
|
||||||
c.discovered = true
|
c.discovered = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// a unicast session so we call "open" and wait for an "accept"
|
// a unicast session so we call "open" and wait for an "accept"
|
||||||
|
|
||||||
|
// reset now in case we use it
|
||||||
|
now = time.Now()
|
||||||
|
|
||||||
// try to open the session
|
// try to open the session
|
||||||
err := c.Open()
|
err := c.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -1089,6 +1112,18 @@ func (t *tun) Dial(channel string, opts ...DialOption) (Session, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if we haven't measured the roundtrip do it now
|
||||||
|
if !measured && c.mode == Unicast {
|
||||||
|
// set the link time
|
||||||
|
t.RLock()
|
||||||
|
link, ok := t.links[c.link]
|
||||||
|
t.RUnlock()
|
||||||
|
if ok {
|
||||||
|
// set the rountrip time
|
||||||
|
link.setRTT(time.Since(now))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return c, nil
|
return c, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,12 @@ type link struct {
|
|||||||
// channels keeps a mapping of channels and last seen
|
// channels keeps a mapping of channels and last seen
|
||||||
channels map[string]time.Time
|
channels map[string]time.Time
|
||||||
|
|
||||||
|
// the weighted moving average roundtrip
|
||||||
|
rtt int64
|
||||||
|
|
||||||
|
// weighted moving average of bits flowing
|
||||||
|
rate float64
|
||||||
|
|
||||||
// keep an error count on the link
|
// keep an error count on the link
|
||||||
errCount int
|
errCount int
|
||||||
}
|
}
|
||||||
@ -48,6 +54,21 @@ func newLink(s transport.Socket) *link {
|
|||||||
return l
|
return l
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *link) setRTT(d time.Duration) {
|
||||||
|
l.Lock()
|
||||||
|
defer l.Unlock()
|
||||||
|
|
||||||
|
if l.rtt <= 0 {
|
||||||
|
l.rtt = d.Nanoseconds()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://fishi.devtail.io/weblog/2015/04/12/measuring-bandwidth-and-round-trip-time-tcp-connection-inside-application-layer/
|
||||||
|
rtt := 0.8*float64(l.rtt) + 0.2*float64(d.Nanoseconds())
|
||||||
|
// set new rtt
|
||||||
|
l.rtt = int64(rtt)
|
||||||
|
}
|
||||||
|
|
||||||
// watches the channel expiry
|
// watches the channel expiry
|
||||||
func (l *link) expiry() {
|
func (l *link) expiry() {
|
||||||
t := time.NewTicker(time.Minute)
|
t := time.NewTicker(time.Minute)
|
||||||
@ -92,12 +113,18 @@ func (l *link) Delay() int64 {
|
|||||||
|
|
||||||
// Current transfer rate as bits per second (lower is better)
|
// Current transfer rate as bits per second (lower is better)
|
||||||
func (l *link) Rate() float64 {
|
func (l *link) Rate() float64 {
|
||||||
return float64(10e8)
|
l.RLock()
|
||||||
|
defer l.RUnlock()
|
||||||
|
return l.rate
|
||||||
}
|
}
|
||||||
|
|
||||||
// Length returns the roundtrip time as nanoseconds (lower is better)
|
// Length returns the roundtrip time as nanoseconds (lower is better).
|
||||||
|
// Returns 0 where no measurement has been taken.
|
||||||
func (l *link) Length() int64 {
|
func (l *link) Length() int64 {
|
||||||
return time.Second.Nanoseconds()
|
l.RLock()
|
||||||
|
defer l.RUnlock()
|
||||||
|
|
||||||
|
return l.rtt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *link) Id() string {
|
func (l *link) Id() string {
|
||||||
@ -119,11 +146,43 @@ func (l *link) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (l *link) Send(m *transport.Message) error {
|
func (l *link) Send(m *transport.Message) error {
|
||||||
|
dataSent := len(m.Body)
|
||||||
|
|
||||||
|
// set header length
|
||||||
|
for k, v := range m.Header {
|
||||||
|
dataSent += (len(k) + len(v))
|
||||||
|
}
|
||||||
|
|
||||||
|
// get time now
|
||||||
|
now := time.Now()
|
||||||
|
|
||||||
|
// send the message
|
||||||
err := l.Socket.Send(m)
|
err := l.Socket.Send(m)
|
||||||
|
|
||||||
l.Lock()
|
l.Lock()
|
||||||
defer l.Unlock()
|
defer l.Unlock()
|
||||||
|
|
||||||
|
// calculate based on data
|
||||||
|
if dataSent > 0 {
|
||||||
|
// measure time taken
|
||||||
|
delta := time.Since(now)
|
||||||
|
|
||||||
|
// bit sent
|
||||||
|
bits := dataSent * 1024
|
||||||
|
|
||||||
|
// rate of send in bits per nanosecond
|
||||||
|
rate := float64(bits) / float64(delta.Nanoseconds())
|
||||||
|
|
||||||
|
//
|
||||||
|
if l.rate == 0 {
|
||||||
|
// rate per second
|
||||||
|
l.rate = rate * 1e9
|
||||||
|
} else {
|
||||||
|
// set new rate per second
|
||||||
|
l.rate = 0.8*l.rate + 0.2*(rate*1e9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if theres no error reset the counter
|
// if theres no error reset the counter
|
||||||
if err == nil {
|
if err == nil {
|
||||||
l.errCount = 0
|
l.errCount = 0
|
||||||
|
@ -292,3 +292,53 @@ func TestReconnectTunnel(t *testing.T) {
|
|||||||
// wait until done
|
// wait until done
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestTunnelRTTRate(t *testing.T) {
|
||||||
|
// create a new tunnel client
|
||||||
|
tunA := NewTunnel(
|
||||||
|
Address("127.0.0.1:9096"),
|
||||||
|
Nodes("127.0.0.1:9097"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// create a new tunnel server
|
||||||
|
tunB := NewTunnel(
|
||||||
|
Address("127.0.0.1:9097"),
|
||||||
|
)
|
||||||
|
|
||||||
|
// start tunB
|
||||||
|
err := tunB.Connect()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tunB.Close()
|
||||||
|
|
||||||
|
// start tunA
|
||||||
|
err = tunA.Connect()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer tunA.Close()
|
||||||
|
|
||||||
|
wait := make(chan bool)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
// start the listener
|
||||||
|
go testAccept(t, tunB, wait, &wg)
|
||||||
|
|
||||||
|
wg.Add(1)
|
||||||
|
// start the client
|
||||||
|
go testSend(t, tunA, wait, &wg)
|
||||||
|
|
||||||
|
// wait until done
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
for _, link := range tunA.Links() {
|
||||||
|
t.Logf("Link %s length %v rate %v", link.Id(), link.Length(), link.Rate())
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, link := range tunB.Links() {
|
||||||
|
t.Logf("Link %s length %v rate %v", link.Id(), link.Length(), link.Rate())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user