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 }