github.com/moby/docker@v26.1.3+incompatible/daemon/stats/collector.go (about)

     1  package stats // import "github.com/docker/docker/daemon/stats"
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  
     7  	"github.com/docker/docker/api/types"
     8  	"github.com/docker/docker/container"
     9  	"github.com/moby/pubsub"
    10  )
    11  
    12  // Collector manages and provides container resource stats
    13  type Collector struct {
    14  	m          sync.Mutex
    15  	cond       *sync.Cond
    16  	supervisor supervisor
    17  	interval   time.Duration
    18  	publishers map[*container.Container]*pubsub.Publisher
    19  }
    20  
    21  // NewCollector creates a stats collector that will poll the supervisor with the specified interval
    22  func NewCollector(supervisor supervisor, interval time.Duration) *Collector {
    23  	s := &Collector{
    24  		interval:   interval,
    25  		supervisor: supervisor,
    26  		publishers: make(map[*container.Container]*pubsub.Publisher),
    27  	}
    28  	s.cond = sync.NewCond(&s.m)
    29  	return s
    30  }
    31  
    32  type supervisor interface {
    33  	// GetContainerStats collects all the stats related to a container
    34  	GetContainerStats(container *container.Container) (*types.StatsJSON, error)
    35  }
    36  
    37  // Collect registers the container with the collector and adds it to
    38  // the event loop for collection on the specified interval returning
    39  // a channel for the subscriber to receive on.
    40  func (s *Collector) Collect(c *container.Container) chan interface{} {
    41  	s.cond.L.Lock()
    42  	defer s.cond.L.Unlock()
    43  
    44  	publisher, exists := s.publishers[c]
    45  	if !exists {
    46  		publisher = pubsub.NewPublisher(100*time.Millisecond, 1024)
    47  		s.publishers[c] = publisher
    48  	}
    49  
    50  	s.cond.Broadcast()
    51  	return publisher.Subscribe()
    52  }
    53  
    54  // StopCollection closes the channels for all subscribers and removes
    55  // the container from metrics collection.
    56  func (s *Collector) StopCollection(c *container.Container) {
    57  	s.m.Lock()
    58  	if publisher, exists := s.publishers[c]; exists {
    59  		publisher.Close()
    60  		delete(s.publishers, c)
    61  	}
    62  	s.m.Unlock()
    63  }
    64  
    65  // Unsubscribe removes a specific subscriber from receiving updates for a container's stats.
    66  func (s *Collector) Unsubscribe(c *container.Container, ch chan interface{}) {
    67  	s.m.Lock()
    68  	publisher := s.publishers[c]
    69  	if publisher != nil {
    70  		publisher.Evict(ch)
    71  		if publisher.Len() == 0 {
    72  			delete(s.publishers, c)
    73  		}
    74  	}
    75  	s.m.Unlock()
    76  }
    77  
    78  // Run starts the collectors and will indefinitely collect stats from the supervisor
    79  func (s *Collector) Run() {
    80  	type publishersPair struct {
    81  		container *container.Container
    82  		publisher *pubsub.Publisher
    83  	}
    84  	// we cannot determine the capacity here.
    85  	// it will grow enough in first iteration
    86  	var pairs []publishersPair
    87  
    88  	for {
    89  		s.cond.L.Lock()
    90  		for len(s.publishers) == 0 {
    91  			s.cond.Wait()
    92  		}
    93  
    94  		// it does not make sense in the first iteration,
    95  		// but saves allocations in further iterations
    96  		pairs = pairs[:0]
    97  
    98  		for container, publisher := range s.publishers {
    99  			// copy pointers here to release the lock ASAP
   100  			pairs = append(pairs, publishersPair{container, publisher})
   101  		}
   102  
   103  		s.cond.L.Unlock()
   104  
   105  		for _, pair := range pairs {
   106  			stats, err := s.supervisor.GetContainerStats(pair.container)
   107  			if err != nil {
   108  				stats = &types.StatsJSON{
   109  					Name: pair.container.Name,
   110  					ID:   pair.container.ID,
   111  				}
   112  			}
   113  			pair.publisher.Publish(*stats)
   114  		}
   115  
   116  		time.Sleep(s.interval)
   117  	}
   118  }