@@ -4,6 +4,7 @@ package fargo
4
4
5
5
import (
6
6
"math/rand"
7
+ "sync"
7
8
"time"
8
9
)
9
10
@@ -80,7 +81,7 @@ func NewConn(address ...string) (e EurekaConnection) {
80
81
}
81
82
82
83
// UpdateApp creates a goroutine that continues to keep an application updated
83
- // with its status in Eureka
84
+ // with its status in Eureka.
84
85
func (e * EurekaConnection ) UpdateApp (app * Application ) {
85
86
go func () {
86
87
for {
@@ -93,3 +94,134 @@ func (e *EurekaConnection) UpdateApp(app *Application) {
93
94
}
94
95
}()
95
96
}
97
+
98
+ // AppUpdate is the outcome of an attempt to get a fresh snapshot of a Eureka
99
+ // application's state, together with an error that may have occurred in that
100
+ // attempt. If the Err field is nil, the App field will be non-nil.
101
+ type AppUpdate struct {
102
+ App * Application
103
+ Err error
104
+ }
105
+
106
+ func sendAppUpdatesEvery (d time.Duration , produce func () AppUpdate , c chan <- AppUpdate , done <- chan struct {}) {
107
+ t := time .NewTicker (d )
108
+ defer t .Stop ()
109
+ for {
110
+ select {
111
+ case <- done :
112
+ close (c )
113
+ return
114
+ case <- t .C :
115
+ // Drop attempted sends when the consumer hasn't received the last buffered update.
116
+ select {
117
+ case c <- produce ():
118
+ default :
119
+ }
120
+ }
121
+ }
122
+ }
123
+
124
+ // ScheduleAppUpdates starts polling for updates to the Eureka application with
125
+ // the given name, using the connection's configured polling interval as its
126
+ // period. It sends the outcome of each update attempt to the returned channel,
127
+ // and continues until the supplied done channel is either closed or has a value
128
+ // available. Once done sending updates to the returned channel, it closes it.
129
+ //
130
+ // If await is true, it sends at least one application update outcome to the
131
+ // returned channel before returning.
132
+ func (e * EurekaConnection ) ScheduleAppUpdates (name string , await bool , done <- chan struct {}) <- chan AppUpdate {
133
+ produce := func () AppUpdate {
134
+ app , err := e .GetApp (name )
135
+ return AppUpdate {app , err }
136
+ }
137
+ c := make (chan AppUpdate , 1 )
138
+ if await {
139
+ c <- produce ()
140
+ }
141
+ go sendAppUpdatesEvery (time .Duration (e .PollInterval )* time .Second , produce , c , done )
142
+ return c
143
+ }
144
+
145
+ // An AppSource holds a periodically updated copy of a Eureka application.
146
+ type AppSource struct {
147
+ m * sync.RWMutex
148
+ app * Application
149
+ done chan <- struct {}
150
+ }
151
+
152
+ // NewAppSource returns a new AppSource that offers a periodically updated copy
153
+ // of the Eureka application with the given name, using the connection's
154
+ // configured polling interval as its period.
155
+ //
156
+ // If await is true, it waits for the first application update to complete
157
+ // before returning, though it's possible that that first update attempt could
158
+ // fail, so that a subsequent call to CopyLatestAppTo would return false.
159
+ func (e * EurekaConnection ) NewAppSource (name string , await bool ) * AppSource {
160
+ done := make (chan struct {})
161
+ updates := e .ScheduleAppUpdates (name , await , done )
162
+ s := & AppSource {
163
+ done : done ,
164
+ }
165
+ if await {
166
+ if u := <- updates ; u .Err != nil {
167
+ s .app = u .App
168
+ }
169
+ }
170
+ go func () {
171
+ for u := range updates {
172
+ if u .Err != nil {
173
+ s .m .Lock ()
174
+ s .app = u .App
175
+ s .m .Unlock ()
176
+ }
177
+ }
178
+ }()
179
+ return s
180
+ }
181
+
182
+ // Latest returns the most recently acquired Eureke application, if any. If the
183
+ // most recent update attempt failed, or if no update attempt has yet to
184
+ // complete, it returns nil.
185
+ func (s * AppSource ) Latest () * Application {
186
+ if s == nil {
187
+ return nil
188
+ }
189
+ s .m .RLock ()
190
+ defer s .m .RUnlock ()
191
+ return s .app
192
+ }
193
+
194
+ // CopyLatestTo copies the most recently acquired Eureka application to dst, if
195
+ // any, and returns true if such an application was available. If no preceding
196
+ // update attempt had succeeded, such that no application is available to be
197
+ // copied, it returns false.
198
+ func (s * AppSource ) CopyLatestTo (dst * Application ) bool {
199
+ if s == nil {
200
+ return false
201
+ }
202
+ s .m .RLock ()
203
+ defer s .m .RUnlock ()
204
+ if s .app == nil {
205
+ return false
206
+ }
207
+ * dst = * s .app
208
+ return true
209
+ }
210
+
211
+ // Stop turns off an AppSource, so that it will no longer attempt to update its
212
+ // latest application.
213
+ //
214
+ // It is safe to call Latest or CopyLatestTo on a stopped source.
215
+ func (s * AppSource ) Stop () {
216
+ if s == nil {
217
+ return
218
+ }
219
+ // Allow multiple calls to Stop by precluding repeated attempts to close an
220
+ // already closed channel.
221
+ s .m .Lock ()
222
+ defer s .m .Unlock ()
223
+ if s .done != nil {
224
+ close (s .done )
225
+ s .done = nil
226
+ }
227
+ }
0 commit comments