這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Engine結構體有一個eventsMonitor成員:
type Engine struct { ...... eventsMonitor *EventsMonitor}
EventsMonitor結構體定義如下:
//EventsMonitor monitors eventstype EventsMonitor struct { stopChan chan struct{} cli client.APIClient handler func(msg events.Message) error}
stopChan用來通知停止接受訊息;cli是底層串連的client,而handler則是收到event的處理函數。
Engine.ConnectWithClient方法會給eventsMonitor成員賦值:
// ConnectWithClient is exportedfunc (e *Engine) ConnectWithClient(client dockerclient.Client, apiClient engineapi.APIClient) error { e.client = client e.apiClient = apiClient e.eventsMonitor = NewEventsMonitor(e.apiClient, e.handler) // Fetch the engine labels. if err := e.updateSpecs(); err != nil { return err } e.StartMonitorEvents() // Force a state update before returning. if err := e.RefreshContainers(true); err != nil { return err } if err := e.RefreshImages(); err != nil { return err } // Do not check error as older daemon does't support this call. e.RefreshVolumes() e.RefreshNetworks() e.emitEvent("engine_connect") return nil}
其中Engine.StartMonitorEvents代碼如下:
// StartMonitorEvents monitors events from the enginefunc (e *Engine) StartMonitorEvents() { log.WithFields(log.Fields{"name": e.Name, "id": e.ID}).Debug("Start monitoring events") ec := make(chan error) e.eventsMonitor.Start(ec) go func() { if err := <-ec; err != nil { if !strings.Contains(err.Error(), "EOF") { // failing node reconnect should use back-off strategy <-e.refreshDelayer.Wait(e.getFailureCount()) } e.StartMonitorEvents() } close(ec) }()}
Engine.StartMonitorEvents就是如果從ec channel收取訊息,如果是錯誤,就不斷地迴圈啟動Engine.StartMonitorEvents。
EventsMonitor.Start函數代碼如下:
// Start starts the EventsMonitorfunc (em *EventsMonitor) Start(ec chan error) { em.stopChan = make(chan struct{}) responseBody, err := em.cli.Events(context.Background(), types.EventsOptions{}) if err != nil { ec <- err return } resultChan := make(chan decodingResult) go func() { dec := json.NewDecoder(responseBody) for { var result decodingResult result.err = dec.Decode(&result.msg) resultChan <- result if result.err == io.EOF { break } } close(resultChan) }() go func() { defer responseBody.Close() for { select { case <-em.stopChan: ec <- nil return case result := <-resultChan: if result.err != nil { ec <- result.err return } if err := em.handler(result.msg); err != nil { ec <- err return } } } }()}
代碼邏輯實際就是發出“HTTP GET /events”請求,然後等待Docker Engine的響應。因為這個HTTP請求很可能會阻塞在這裡,因此隨後的HTTP訊息互動就會重建立立一個HTTP串連。原理在這裡:
type Response struct { ...... // Body represents the response body. // // The http Client and Transport guarantee that Body is always // non-nil, even on responses without a body or responses with // a zero-length body. It is the caller's responsibility to // close Body. The default HTTP client's Transport does not // attempt to reuse HTTP/1.0 or HTTP/1.1 TCP connections // ("keep-alive") unless the Body is read to completion and is // closed. // // The Body is automatically dechunked if the server replied // with a "chunked" Transfer-Encoding. Body io.ReadCloser ......}
如果想停止這個EventsMonitor,可以使用Engine.Stop方法:
// Stop stops the EventsMonitorfunc (em *EventsMonitor) Stop() { if em.stopChan == nil { return } close(em.stopChan)}