mirror of
https://github.com/go-ping/ping.git
synced 2025-07-19 08:26:51 +00:00
Use exponential backoff for read deadline (#162)
Before this change, pinging any target once takes ~ 100 ms (the time it takes for Run() to return, not the RTT). After this change, it takes a time comparable to the RTT. The change comes from ReadFrom blocking for the specified delay if the done signal hasn't fired yet. It also improves a little the time it takes to ping a single target multiple times. Since this is exponential backoff in the way Ethernet does it (random delay, with an increasing maximum value, only changing in case of a timeout), the delay neither necessarily doubles nor increases each time the operation has to be retried, but it does eventually settle on the higher end of the possibilities if there are many timeouts. Signed-off-by: Marcelo E. Magallon <marcelo.magallon@grafana.com> Committed-by: Marcelo E. Magallon <marcelo.magallon@grafana.com> Committed-by: Charlie Jonas <charlie@charliejonas.co.uk>
This commit is contained in:
parent
1726e5ede5
commit
e4e642a957
26
ping.go
26
ping.go
@ -512,19 +512,42 @@ func (p *Pinger) Statistics() *Statistics {
|
||||
return &s
|
||||
}
|
||||
|
||||
type expBackoff struct {
|
||||
baseDelay time.Duration
|
||||
maxExp int64
|
||||
c int64
|
||||
}
|
||||
|
||||
func (b *expBackoff) Get() time.Duration {
|
||||
if b.c < b.maxExp {
|
||||
b.c++
|
||||
}
|
||||
|
||||
return b.baseDelay * time.Duration(rand.Int63n(1<<b.c))
|
||||
}
|
||||
|
||||
func newExpBackoff(baseDelay time.Duration, maxExp int64) expBackoff {
|
||||
return expBackoff{baseDelay: baseDelay, maxExp: maxExp}
|
||||
}
|
||||
|
||||
func (p *Pinger) recvICMP(
|
||||
conn *icmp.PacketConn,
|
||||
recv chan<- *packet,
|
||||
wg *sync.WaitGroup,
|
||||
) error {
|
||||
defer wg.Done()
|
||||
|
||||
// Start by waiting for 50 µs and increase to a possible maximum of ~ 100 ms.
|
||||
expBackoff := newExpBackoff(50*time.Microsecond, 11)
|
||||
delay := expBackoff.Get()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.done:
|
||||
return nil
|
||||
default:
|
||||
bytes := make([]byte, p.getMessageLength())
|
||||
if err := conn.SetReadDeadline(time.Now().Add(time.Millisecond * 100)); err != nil {
|
||||
if err := conn.SetReadDeadline(time.Now().Add(delay)); err != nil {
|
||||
return err
|
||||
}
|
||||
var n, ttl int
|
||||
@ -546,6 +569,7 @@ func (p *Pinger) recvICMP(
|
||||
if neterr, ok := err.(*net.OpError); ok {
|
||||
if neterr.Timeout() {
|
||||
// Read timeout
|
||||
delay = expBackoff.Get()
|
||||
continue
|
||||
} else {
|
||||
p.Stop()
|
||||
|
Loading…
Reference in New Issue
Block a user