Skip to content

Commit

Permalink
bugfix: events api is a long connection, the connection may be recycl…
Browse files Browse the repository at this point in the history
…ed if it keeps idle too long

Signed-off-by: Michael Wan <zirenwan@gmail.com>
  • Loading branch information
HusterWan committed Sep 25, 2018
1 parent 7addef1 commit b266303
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 6 deletions.
2 changes: 1 addition & 1 deletion apis/server/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func initRoute(s *Server) http.Handler {
s.addRoute(r, http.MethodGet, "/info", s.info)
s.addRoute(r, http.MethodGet, "/version", s.version)
s.addRoute(r, http.MethodPost, "/auth", s.auth)
s.addRoute(r, http.MethodGet, "/events", s.events)
s.addRoute(r, http.MethodGet, "/events", withCancelHandler(s.events))

// daemon, we still list this API into system manager.
s.addRoute(r, http.MethodPost, "/daemon/update", s.updateDaemon)
Expand Down
20 changes: 17 additions & 3 deletions apis/server/system_bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/docker/docker/pkg/ioutils"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)

func (s *Server) ping(context context.Context, rw http.ResponseWriter, req *http.Request) (err error) {
Expand Down Expand Up @@ -69,9 +70,6 @@ func (s *Server) auth(ctx context.Context, rw http.ResponseWriter, req *http.Req
}

func (s *Server) events(ctx context.Context, rw http.ResponseWriter, req *http.Request) (err error) {
ctx, cancel := context.WithCancel(ctx)
defer cancel()

rw.Header().Set("Content-Type", "application/json")
output := ioutils.NewWriteFlusher(rw)
defer output.Close()
Expand Down Expand Up @@ -123,11 +121,18 @@ func (s *Server) events(ctx context.Context, rw http.ResponseWriter, req *http.R
return nil
}

// Notes(ziren): The pouchd http server set some http timeout parameters (see pouch/apis/server/server.go),
// that may cause long idle connections been recycled. So, creating a tick here to keep the
// event api alive. set the ticker to 8s because of the IdleTimeout of http server is 10s.
ticker := time.NewTicker(time.Second * 8)
defer ticker.Stop()

// start subscribe new pouchd events
for {
select {
case ev := <-eventq:
if err := enc.Encode(ev); err != nil {
logrus.Errorf("encode events got an error: %v", err)
return err
}
case err := <-errq:
Expand All @@ -137,6 +142,15 @@ func (s *Server) events(ctx context.Context, rw http.ResponseWriter, req *http.R
return nil
case <-timeout:
return nil
case <-ticker.C:
emptyEvents := &types.EventsMessage{}
if err := enc.Encode(emptyEvents); err != nil {
logrus.Errorf("encode empty events got an error: %v", err)
return err
}
case <-ctx.Done():
logrus.Debug("client context is cancelled, stop sending events")
return nil
}
}
}
Expand Down
13 changes: 11 additions & 2 deletions cli/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,15 +94,24 @@ type eventProcessor func(event types.EventsMessage, err error) error
// Each output includes the event type, actor id, name and action.
// Actor attributes are printed at the end if the actor has any.
func printOutput(event types.EventsMessage, output io.Writer) {
// skip empty event message
if event == (types.EventsMessage{}) {
return
}

if event.TimeNano != 0 {
fmt.Fprintf(output, "%s ", time.Unix(0, event.TimeNano).Format(utils.RFC3339NanoFixed))
} else if event.Time != 0 {
fmt.Fprintf(output, "%s ", time.Unix(event.Time, 0).Format(utils.RFC3339NanoFixed))
}

fmt.Fprintf(output, "%s %s %s", event.Type, event.Action, event.Actor.ID)
id := ""
if event.Actor != nil {
id = event.Actor.ID
}
fmt.Fprintf(output, "%s %s %s", event.Type, event.Action, id)

if len(event.Actor.Attributes) > 0 {
if event.Actor != nil && len(event.Actor.Attributes) > 0 {
var attrs []string
var keys []string
for k := range event.Actor.Attributes {
Expand Down

0 comments on commit b266303

Please sign in to comment.