Some data pushes need to be in observer mode (also known as subscriber mode) to see how Docker implements this with Golang
Over the data structure type Events struct {mu sync. Mutex//Lock events []*jsonmessage. Jsonmessage//Data pub *pubsub. Publisher//Publisher}type publisher struct {m sync. Rwmutex buffer int//buffer timeout time. Duration//Timeout subscribers map[subscriber]struct{}//Subscriber map structure for easy traversal of map}
As a subscriber, subscribe once
Func (e *events) Subscribe () ([]*jsonmessage. Jsonmessage, Chan interface{}) {E.mu.lock () Current: = Make ([]*jsonmessage. Jsonmessage, Len (e.events))//e.events be notified when the Subscriber copy (current, e.events) L: = E.pub.subscribe ()//subscription, Log in to Publisher's subscribers to E.mu.unlock () return current, L}func (P *publisher) Subscribe () Chan interface{} {ch: = make ( Chan interface{}, P.buffer) P.m.lock () p.subscribers[ch] = struct{}{}//Record P.m.unlock () return CH//return to a pipeline}
Generate publisher once, of course, to broadcast to all subscribers.
Func (e *events) Log (action, ID, from string) {go func () {E.mu.lock () JM: = &jsonmessage. Jsonmessage{status:action, Id:id, From:from, Time:time. Now (). UTC (). Unix ()}if len (e.events) = = Cap (e.events) {//discards the oldest record because the size exceeds the allocated copy (E.events, e.events[1:]) E.events[len (e.events)- 1] = JM} else {e.events = append (e.events, JM)}e.mu.unlock () e.pub.publish (JM)//Publish Broadcast} ()}
And finally look at the broadcast code.
Func (P *publisher) Publish (v interface{}) {P.m.rlock () for sub: = Range p.subscribers {//Send under a select as to not BL Ock If the receiver is unavailable//if the setting times out, the default setting is 100*time. Millisecond, the data is sent, if it is not sent, it is blocked until you receive the timeout message if p.timeout > 0 {select {case sub <-v:case <-time. After (p.timeout):}continue}//does not set a time-out, the data is not sent to the pipeline successfully, then the default continues to execute Select {case Sub <-V:default:}}p.m.runlock ()}
See where we got this data:
//............ Omit for {select {case EV: = <-l://To the subscriber's outgoing data Jev to the pipeline, OK: = ev. ( *jsonmessage. Jsonmessage) If!ok {continue}if err: = Sendevent (JEV); Err! = Nil {return err}case <-timer. C://takes the timeout information to return nilcase <-closenotify://to the close notification message Logrus. Debug ("Client disconnected, stop sending events") return nil}}
Docker's observer pattern Subscribe/publisher implementation