@@ -30,6 +30,31 @@ type ApiServices struct {
30
30
ClusterName string
31
31
}
32
32
33
+ // A ServicesState.Listener that we use for the /watch endpoint
34
+ type HttpListener struct {
35
+ eventChan chan catalog.ChangeEvent
36
+ name string
37
+ }
38
+
39
+ func NewHttpListener () * HttpListener {
40
+ return & HttpListener {
41
+ // This should be fine enough granularity for practical purposes
42
+ name : fmt .Sprintf ("httpListener-%d" , time .Now ().UTC ().UnixNano ()),
43
+ // Listeners must have buffered channels. We'll use a
44
+ // somewhat larger buffer here because of the slow link
45
+ // problem with http
46
+ eventChan : make (chan catalog.ChangeEvent , 50 ),
47
+ }
48
+ }
49
+
50
+ func (h * HttpListener ) Chan () chan catalog.ChangeEvent {
51
+ return h .eventChan
52
+ }
53
+
54
+ func (h * HttpListener ) Name () string {
55
+ return h .name
56
+ }
57
+
33
58
func makeHandler (fn func (http.ResponseWriter , * http.Request ,
34
59
* memberlist.Memberlist , * catalog.ServicesState ),
35
60
list * memberlist.Memberlist , state * catalog.ServicesState ) http.HandlerFunc {
@@ -44,29 +69,29 @@ func watchHandler(response http.ResponseWriter, req *http.Request, list *memberl
44
69
45
70
response .Header ().Set ("Content-Type" , "application/json" )
46
71
47
- lastChange := time .Unix (0 , 0 )
48
72
var jsonBytes []byte
49
73
var err error
50
74
51
- for {
52
- var changed bool
75
+ listener := NewHttpListener ()
53
76
54
- func () { // Wrap critical section
55
- state .RLock ()
56
- defer state .RUnlock ()
77
+ // Find out when the http connection closed so we can stop
78
+ notify := response .(http.CloseNotifier ).CloseNotify ()
57
79
58
- if state .LastChanged .After (lastChange ) {
59
- lastChange = state .LastChanged
60
- jsonBytes , err = json .Marshal (state .ByService ())
61
- if err != nil {
62
- log .Errorf ("Error marshaling state in watchHandler: %s" , err .Error ())
63
- return
64
- }
65
- changed = true // Trigger sending new encoding
66
- }
67
- }()
80
+ // Let's subscribe to state change events
81
+ state .AddListener (listener )
82
+ defer state .RemoveListener (listener .Name ())
68
83
69
- if changed {
84
+ for {
85
+ select {
86
+ case <- notify :
87
+ break
88
+
89
+ case <- listener .Chan ():
90
+ jsonBytes , err = json .Marshal (state .ByService ())
91
+ if err != nil {
92
+ log .Errorf ("Error marshaling state in watchHandler: %s" , err .Error ())
93
+ return
94
+ }
70
95
// In order to flush immediately, we have to cast to a Flusher.
71
96
// The normal HTTP library supports this but not all do, so we
72
97
// check just in case.
@@ -75,7 +100,6 @@ func watchHandler(response http.ResponseWriter, req *http.Request, list *memberl
75
100
f .Flush ()
76
101
}
77
102
}
78
- time .Sleep (250 * time .Millisecond )
79
103
}
80
104
}
81
105
0 commit comments