github.com/m-lab/tcp-info@v1.9.0/collector/collector_linux.go (about)

     1  // Package collector repeatedly queries the netlink socket to discover
     2  // measurement data about open TCP connections and sends that data down a
     3  // channel.
     4  package collector
     5  
     6  import (
     7  	"context"
     8  	"log"
     9  	"syscall"
    10  	"time"
    11  
    12  	"github.com/m-lab/tcp-info/metrics"
    13  
    14  	"github.com/m-lab/tcp-info/netlink"
    15  	"github.com/m-lab/tcp-info/saver"
    16  )
    17  
    18  var (
    19  	errCount   = 0
    20  	localCount = 0
    21  )
    22  
    23  // collectDefaultNamespace collects all AF_INET6 and AF_INET connection stats, and sends them
    24  // to svr.
    25  func collectDefaultNamespace(svr chan<- netlink.MessageBlock, skipLocal bool) (int, int) {
    26  	// Preallocate space for up to 500 connections.  We may want to adjust this upwards if profiling
    27  	// indicates a lot of reallocation.
    28  	buffer := netlink.MessageBlock{}
    29  
    30  	remoteCount := 0
    31  	res6, err := OneType(syscall.AF_INET6)
    32  	buffer.V6Time = time.Now()
    33  	if err != nil {
    34  		// Properly handle errors
    35  		// TODO add metric
    36  		log.Println(err)
    37  	} else {
    38  		buffer.V6Messages = res6
    39  	}
    40  	res4, err := OneType(syscall.AF_INET)
    41  	buffer.V4Time = time.Now()
    42  	if err != nil {
    43  		// Properly handle errors
    44  		// TODO add metric
    45  		log.Println(err)
    46  	} else {
    47  		buffer.V4Messages = res4
    48  	}
    49  
    50  	// Submit full set of message to the marshalling service.
    51  	svr <- buffer
    52  
    53  	return len(res4) + len(res6), remoteCount
    54  }
    55  
    56  // Run the collector, either for the specified number of loops, or, if the
    57  // number specified is infinite, run forever.
    58  func Run(ctx context.Context, reps int, svrChan chan<- netlink.MessageBlock, cl saver.CacheLogger, skipLocal bool) (localCount, errCount int) {
    59  	totalCount := 0
    60  	remoteCount := 0
    61  	loops := 0
    62  
    63  	// TODO - make this interval programmable.
    64  	ticker := time.NewTicker(10 * time.Millisecond)
    65  	defer ticker.Stop()
    66  
    67  	lastCollectionTime := time.Now().Add(-10 * time.Millisecond)
    68  
    69  	for loops = 0; (reps == 0 || loops < reps) && (ctx.Err() == nil); loops++ {
    70  		total, remote := collectDefaultNamespace(svrChan, skipLocal)
    71  		totalCount += total
    72  		remoteCount += remote
    73  		// print stats roughly once per minute.
    74  		if loops%6000 == 0 {
    75  			cl.LogCacheStats(localCount, errCount)
    76  		}
    77  
    78  		now := time.Now()
    79  		interval := now.Sub(lastCollectionTime)
    80  		lastCollectionTime = now
    81  		metrics.PollingHistogram.Observe(interval.Seconds())
    82  
    83  		// Wait for next tick.
    84  		<-ticker.C
    85  	}
    86  
    87  	if loops > 0 {
    88  		log.Println(totalCount, "sockets", remoteCount, "remotes", totalCount/loops, "per iteration")
    89  	}
    90  	return localCount, errCount
    91  }