ruby_skynet 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +4 -2
- data/Gemfile.lock +22 -16
- data/README.md +13 -9
- data/Rakefile +1 -2
- data/lib/rails/generators/ruby_skynet/config/templates/ruby_skynet.yml +25 -21
- data/lib/ruby_skynet/client.rb +1 -1
- data/lib/ruby_skynet/connection.rb +1 -1
- data/lib/ruby_skynet/railties/ruby_skynet.rake +6 -1
- data/lib/ruby_skynet/registry.rb +17 -0
- data/lib/ruby_skynet/ruby_skynet.rb +47 -48
- data/lib/ruby_skynet/server.rb +13 -13
- data/lib/ruby_skynet/service.rb +1 -1
- data/lib/ruby_skynet/service_registry.rb +48 -55
- data/lib/ruby_skynet/version.rb +1 -1
- data/lib/ruby_skynet/zookeeper/cached_registry.rb +75 -0
- data/lib/ruby_skynet/zookeeper/extensions/java_base.rb +27 -0
- data/lib/ruby_skynet/zookeeper/json/deserializer.rb +64 -0
- data/lib/ruby_skynet/zookeeper/json/serializer.rb +57 -0
- data/lib/ruby_skynet/zookeeper/registry.rb +510 -0
- data/lib/ruby_skynet/zookeeper.rb +11 -0
- data/lib/ruby_skynet.rb +2 -1
- data/test/client_test.rb +1 -1
- data/test/service_registry_test.rb +21 -35
- data/test/zookeeper_registry_test.rb +200 -0
- metadata +16 -23
- data/test.sh +0 -7
@@ -0,0 +1,510 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
require 'semantic_logger'
|
3
|
+
require 'zookeeper'
|
4
|
+
|
5
|
+
# Replace the Zookeeper logger for consistency
|
6
|
+
::Zookeeper.logger = SemanticLogger[::Zookeeper]
|
7
|
+
# Map Zookeeper debug logging to trace level to reduce verbosity in logs
|
8
|
+
::Zookeeper.logger.instance_eval "def debug(*args,&block)\n trace(*args,&block)\nend"
|
9
|
+
|
10
|
+
module RubySkynet
|
11
|
+
module Zookeeper
|
12
|
+
#
|
13
|
+
# Registry
|
14
|
+
#
|
15
|
+
# Store information in Zookeepr and subscribe to future changes
|
16
|
+
#
|
17
|
+
# Notifies registered subscribers when information has changed
|
18
|
+
#
|
19
|
+
# All paths specified are relative to the root. As such the root key
|
20
|
+
# is never returned, nor is it required when a key is supplied as input.
|
21
|
+
# For example, with a root of /foo/bar, any paths passed in will leave
|
22
|
+
# out the root: host/name
|
23
|
+
class Registry
|
24
|
+
# Logging instance for this class
|
25
|
+
include SemanticLogger::Loggable
|
26
|
+
|
27
|
+
attr_reader :root
|
28
|
+
|
29
|
+
# Create a Registry instance to manage a information within Zookeeper
|
30
|
+
#
|
31
|
+
# :root [String]
|
32
|
+
# Root key to load and then monitor for changes
|
33
|
+
# It is not recommended to set the root to "/" as it will generate
|
34
|
+
# significant traffic since it will also monitor ZooKeeper Admin changes
|
35
|
+
# Mandatory
|
36
|
+
#
|
37
|
+
# :zookeeper [Hash|ZooKeeper]
|
38
|
+
# ZooKeeper configuration information, or an existing
|
39
|
+
# ZooKeeper ( ZooKeeper client) instance
|
40
|
+
#
|
41
|
+
# :servers [Array of String]
|
42
|
+
# Array of URL's of ZooKeeper servers to connect to with port numbers
|
43
|
+
# ['server1:2181', 'server2:2181']
|
44
|
+
#
|
45
|
+
# :connect_timeout [Float]
|
46
|
+
# Time in seconds to timeout when trying to connect to the server
|
47
|
+
#
|
48
|
+
# Optional Block
|
49
|
+
# The block will be called for every key found in the registry on startup
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
#
|
53
|
+
# require 'ruby_skynet/zookeeper'
|
54
|
+
# registry = RubySkynet::Zookeeper::Registry.new(root: '/registry') do |key, value, version|
|
55
|
+
# puts "Found #{key} => '#{value}' V#{version}"
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
def initialize(params, &block)
|
59
|
+
params = params.dup
|
60
|
+
@root = params.delete(:root)
|
61
|
+
raise "Missing mandatory parameter :root" unless @root
|
62
|
+
|
63
|
+
# Add leading '/' to root if missing
|
64
|
+
@root = "/#{@root}" unless @root.start_with?('/')
|
65
|
+
|
66
|
+
# Strip trailing '/' if supplied
|
67
|
+
@root = @root[0..-2] if @root.end_with?("/")
|
68
|
+
@root_with_trail = "#{@root}/"
|
69
|
+
@root = '/' if @root == ''
|
70
|
+
|
71
|
+
registry_config = params.delete(:registry) || {}
|
72
|
+
if registry_config.is_a?(::Zookeeper::Client)
|
73
|
+
@zookeeper = registry_config
|
74
|
+
else
|
75
|
+
servers = registry_config.delete(:servers) || ['127.0.0.1:2181']
|
76
|
+
connect_timeout = (registry_config.delete(:connect_timeout) || 10).to_f
|
77
|
+
|
78
|
+
# Generate warning log entries for any unknown configuration options
|
79
|
+
registry_config.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: zookeeper.#{k}"}
|
80
|
+
|
81
|
+
# Create Zookeeper connection
|
82
|
+
# server1:2181,server2:2181,server3:2181
|
83
|
+
@zookeeper = ::Zookeeper.new(servers.join(','), connect_timeout, watcher)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Allow the serializer and deserializer implementations to be replaced
|
87
|
+
@serializer = params.delete(:serializer) || RubySkynet::Zookeeper::Json::Serializer
|
88
|
+
@deserializer = params.delete(:deserializer) || RubySkynet::Zookeeper::Json::Deserializer
|
89
|
+
|
90
|
+
# Generate warning log entries for any unknown configuration options
|
91
|
+
params.each_pair {|k,v| logger.warn "Ignoring unknown configuration option: #{k}"}
|
92
|
+
|
93
|
+
# Hash with Array values containing the list of children for each node, if any
|
94
|
+
@children = ThreadSafe::Hash.new
|
95
|
+
|
96
|
+
# Start watching registry for any changes
|
97
|
+
get_recursive(@root, watch=true, create_path=true, &block)
|
98
|
+
|
99
|
+
at_exit do
|
100
|
+
close
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Retrieve the latest value from a specific path from the registry
|
105
|
+
# Returns nil when the key is not present in the registry
|
106
|
+
def [](key)
|
107
|
+
result = @zookeeper.get(:path => full_key(key))
|
108
|
+
case result[:rc]
|
109
|
+
when ::Zookeeper::ZOK
|
110
|
+
@deserializer.deserialize(result[:data])
|
111
|
+
when ::Zookeeper::ZNONODE
|
112
|
+
# Return nil if node not present
|
113
|
+
else
|
114
|
+
check_rc(result)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Replace the latest value at a specific key
|
119
|
+
# Supplying a nil value will result in the key being deleted in ZooKeeper
|
120
|
+
def []=(key,value)
|
121
|
+
if value.nil?
|
122
|
+
delete(key)
|
123
|
+
return value
|
124
|
+
end
|
125
|
+
v = @serializer.serialize(value)
|
126
|
+
k = full_key(key)
|
127
|
+
result = @zookeeper.set(:path => k, :data => v)
|
128
|
+
if result[:rc] == ::Zookeeper::ZNONODE
|
129
|
+
create_path(k, v)
|
130
|
+
else
|
131
|
+
check_rc(result)
|
132
|
+
end
|
133
|
+
value
|
134
|
+
end
|
135
|
+
|
136
|
+
# Delete the value at a specific key and any parent nodes if they
|
137
|
+
# don't have any children or values
|
138
|
+
#
|
139
|
+
# Params
|
140
|
+
# remove_empty_parents
|
141
|
+
# If set to true it will also delete any parent nodes that have no
|
142
|
+
# children or value
|
143
|
+
#
|
144
|
+
# Returns nil
|
145
|
+
def delete(key, remove_empty_parents=true)
|
146
|
+
result = @zookeeper.delete(:path => full_key(key))
|
147
|
+
return if result[:rc] == ::Zookeeper::ZNONODE
|
148
|
+
check_rc(result)
|
149
|
+
|
150
|
+
if remove_empty_parents
|
151
|
+
paths = key.split('/')
|
152
|
+
paths.pop
|
153
|
+
while paths.size > 0
|
154
|
+
parent_path = full_key(paths.join('/'))
|
155
|
+
result = @zookeeper.get(:path => parent_path)
|
156
|
+
break if (result[:rc] == ::Zookeeper::ZNONODE) || (result[:data] != nil)
|
157
|
+
|
158
|
+
delete(parent_path)
|
159
|
+
paths.pop
|
160
|
+
end
|
161
|
+
end
|
162
|
+
nil
|
163
|
+
end
|
164
|
+
|
165
|
+
# Iterate over every key, value pair in the registry
|
166
|
+
# Optional relative path can be supplied
|
167
|
+
# Returns the number of nodes iterated over
|
168
|
+
#
|
169
|
+
# Example:
|
170
|
+
# registry.each_pair {|k,v| puts "#{k} => #{v}"}
|
171
|
+
def each_pair(relative_path = '', &block)
|
172
|
+
get_recursive(full_key(relative_path), watch=false, &block)
|
173
|
+
end
|
174
|
+
|
175
|
+
# Returns [Array<String>] all keys in the registry
|
176
|
+
def keys
|
177
|
+
keys = []
|
178
|
+
each_pair {|k,v| keys << k}
|
179
|
+
keys
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns a copy of the registry as a Hash
|
183
|
+
def to_h
|
184
|
+
h = {}
|
185
|
+
each_pair {|k,v| h[k] = v}
|
186
|
+
h
|
187
|
+
end
|
188
|
+
|
189
|
+
# Cleanup on process termination
|
190
|
+
def close
|
191
|
+
@zookeeper.close if @zookeeper
|
192
|
+
@zookeeper = nil
|
193
|
+
end
|
194
|
+
|
195
|
+
# When an entry is created the block will be called
|
196
|
+
# Parameters
|
197
|
+
# key
|
198
|
+
# The relative key to watch for changes
|
199
|
+
# block
|
200
|
+
# The block to be called
|
201
|
+
#
|
202
|
+
# Parameters passed to the block:
|
203
|
+
# key
|
204
|
+
# The key that was created
|
205
|
+
# Supplying a key of '*' means all paths
|
206
|
+
# Default: '*'
|
207
|
+
#
|
208
|
+
# value
|
209
|
+
# New value from doozer
|
210
|
+
#
|
211
|
+
# version
|
212
|
+
# The version number of this node
|
213
|
+
#
|
214
|
+
# Example:
|
215
|
+
# registry.on_update do |key, value, revision|
|
216
|
+
# puts "#{key} was created with #{value}"
|
217
|
+
# end
|
218
|
+
#
|
219
|
+
# Note: They key must either be the exact path or '*' for all keys
|
220
|
+
def on_create(key='*', &block)
|
221
|
+
((@create_subscribers ||= ThreadSafe::Hash.new)[key] ||= ThreadSafe::Array.new) << block
|
222
|
+
end
|
223
|
+
|
224
|
+
# When an entry is updated the block will be called
|
225
|
+
# Parameters
|
226
|
+
# key
|
227
|
+
# The relative key to watch for changes
|
228
|
+
# block
|
229
|
+
# The block to be called
|
230
|
+
#
|
231
|
+
# Parameters passed to the block:
|
232
|
+
# key
|
233
|
+
# The key that was updated in doozer
|
234
|
+
# Supplying a key of '*' means all paths
|
235
|
+
# Default: '*'
|
236
|
+
#
|
237
|
+
# value
|
238
|
+
# New value from doozer
|
239
|
+
#
|
240
|
+
# version
|
241
|
+
# The version number of this node
|
242
|
+
#
|
243
|
+
# Example:
|
244
|
+
# registry.on_update do |key, value, version|
|
245
|
+
# puts "#{key} was updated to #{value}"
|
246
|
+
# end
|
247
|
+
#
|
248
|
+
# Note: They key must either be the exact path or '*' for all keys
|
249
|
+
def on_update(key='*', &block)
|
250
|
+
((@update_subscribers ||= ThreadSafe::Hash.new)[key] ||= ThreadSafe::Array.new) << block
|
251
|
+
end
|
252
|
+
|
253
|
+
# When an entry is deleted the block will be called
|
254
|
+
# Parameters
|
255
|
+
# key
|
256
|
+
# The relative key to watch for changes
|
257
|
+
# block
|
258
|
+
# The block to be called
|
259
|
+
#
|
260
|
+
# Parameters passed to the block:
|
261
|
+
# key
|
262
|
+
# The key that was deleted from doozer
|
263
|
+
# Supplying a key of '*' means all paths
|
264
|
+
# Default: '*'
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
# registry.on_delete do |key, revision|
|
268
|
+
# puts "#{key} was deleted"
|
269
|
+
# end
|
270
|
+
#
|
271
|
+
# Note: They key must either be the exact path or '*' for all keys
|
272
|
+
def on_delete(key='*', &block)
|
273
|
+
((@delete_subscribers ||= ThreadSafe::Hash.new)[key] ||= ThreadSafe::Array.new) << block
|
274
|
+
end
|
275
|
+
|
276
|
+
##########################################
|
277
|
+
protected
|
278
|
+
|
279
|
+
# Returns the full key given a relative key
|
280
|
+
def full_key(relative_key)
|
281
|
+
relative_key = strip_slash(relative_key)
|
282
|
+
relative_key == '' ? @root : File.join(@root,relative_key)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns the full key given a relative key
|
286
|
+
def relative_key(full_key)
|
287
|
+
key = full_key.sub(@root_with_trail, '')
|
288
|
+
key == '' ? '/' : key
|
289
|
+
end
|
290
|
+
|
291
|
+
##########################################
|
292
|
+
protected
|
293
|
+
|
294
|
+
# Strip leading and trailing '/'
|
295
|
+
def strip_slash(path)
|
296
|
+
path = path[1..-1] if path.start_with?('/')
|
297
|
+
path = path[0..-2] if path.end_with?('/')
|
298
|
+
path
|
299
|
+
end
|
300
|
+
|
301
|
+
# Create the supplied path and set the supplied value
|
302
|
+
# Navigates through tree and creates all required parents with no values
|
303
|
+
# as needed to create child node with its value
|
304
|
+
# Note: Value must already be serialized
|
305
|
+
def create_path(full_path, value=nil)
|
306
|
+
paths = full_path.split('/')
|
307
|
+
# Don't create the child node yet
|
308
|
+
paths.pop
|
309
|
+
paths.shift
|
310
|
+
path = ''
|
311
|
+
paths.each do |p|
|
312
|
+
path << "/#{p}"
|
313
|
+
# Ignore errors since it may already exist
|
314
|
+
@zookeeper.create(:path => path)
|
315
|
+
end
|
316
|
+
if value
|
317
|
+
@zookeeper.create(:path => full_path, :data => value)
|
318
|
+
else
|
319
|
+
@zookeeper.create(:path => full_path)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
# returns the watcher proc for this registry instances
|
324
|
+
def watcher
|
325
|
+
# Subscription block to call for watch events
|
326
|
+
@watch_proc ||= Proc.new do |event_hash|
|
327
|
+
path = event_hash[:path]
|
328
|
+
case event_hash[:type]
|
329
|
+
when ::Zookeeper::ZOO_CHANGED_EVENT
|
330
|
+
logger.debug "Node '#{path}' Changed", event_hash
|
331
|
+
|
332
|
+
# Fetch current value and re-subscribe
|
333
|
+
result = @zookeeper.get(:path => path, :watcher => @watch_proc)
|
334
|
+
check_rc(result)
|
335
|
+
value = @deserializer.deserialize(result[:data])
|
336
|
+
stat = result[:stat]
|
337
|
+
|
338
|
+
# Invoke on_update callbacks
|
339
|
+
node_updated(relative_key(path), value, stat.version)
|
340
|
+
|
341
|
+
when ::Zookeeper::ZOO_DELETED_EVENT
|
342
|
+
# A node has been deleted
|
343
|
+
# TODO How to ignore child deleted when it is a directory, not a leaf
|
344
|
+
logger.debug "Node '#{path}' Deleted", event_hash
|
345
|
+
@children.delete(path)
|
346
|
+
node_deleted(relative_key(path))
|
347
|
+
|
348
|
+
when ::Zookeeper::ZOO_CHILD_EVENT
|
349
|
+
# The list of nodes has changed - Does not say if it was added or removed
|
350
|
+
logger.debug "Node '#{path}' Child changed", event_hash
|
351
|
+
result = @zookeeper.get_children(:path => path, :watcher => @watch_proc)
|
352
|
+
|
353
|
+
# This node could have been deleted already
|
354
|
+
if result[:rc] == ::Zookeeper::ZOK
|
355
|
+
current_children = result[:children]
|
356
|
+
previous_children = @children[path]
|
357
|
+
|
358
|
+
# Save children so that we can later identify new children
|
359
|
+
@children[path] = current_children
|
360
|
+
|
361
|
+
# New Child Nodes
|
362
|
+
new_nodes = previous_children ? (current_children - previous_children) : current_children
|
363
|
+
new_nodes.each do |child|
|
364
|
+
get_recursive(File.join(path,child), true) do |key, value, version|
|
365
|
+
node_created(key, value, version)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
# Ignore Deleted Child Nodes since they will be handled by the Deleted Node event
|
369
|
+
end
|
370
|
+
|
371
|
+
when ::Zookeeper::ZOO_CREATED_EVENT
|
372
|
+
# Node created events are only created for paths that were deleted
|
373
|
+
# and then created again
|
374
|
+
# No op - This is covered by node_child created event
|
375
|
+
logger.debug "Node '#{path}' Created - No op", event_hash
|
376
|
+
|
377
|
+
when ::Zookeeper::ZOO_SESSION_EVENT
|
378
|
+
logger.debug "Session Event: #{@zookeeper.state_by_value(event_hash[:state])}", event_hash
|
379
|
+
|
380
|
+
when ::Zookeeper::ZOO_NOTWATCHING_EVENT
|
381
|
+
logger.debug "Ignoring ZOO_NOTWATCHING_EVENT", event_hash
|
382
|
+
|
383
|
+
else
|
384
|
+
# TODO Need to re-load registry when re-connected
|
385
|
+
logger.warn "Ignoring unknown event", event_hash
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
# Recursively fetches all the values in the registry and optionally
|
391
|
+
# registers and starts watching all nodes in ZooKeeper
|
392
|
+
# Returns the number of nodes iterated over
|
393
|
+
#
|
394
|
+
# Optionally supply a block of code to be called when each node
|
395
|
+
# is being subscribed to. It calls the block supplying the value of that node
|
396
|
+
# along with its relative path
|
397
|
+
#
|
398
|
+
# Example:
|
399
|
+
# get_recursive(full_key(relative_path), true)
|
400
|
+
def get_recursive(full_path, watch=false, create_path=true, &block)
|
401
|
+
watch_proc = watcher if watch
|
402
|
+
|
403
|
+
# Get value for this node
|
404
|
+
result = @zookeeper.get(:path => full_path, :watcher => watch_proc)
|
405
|
+
|
406
|
+
# Create the path if it does not exist
|
407
|
+
if create_path && (result[:rc] == ::Zookeeper::ZNONODE)
|
408
|
+
create_path(full_path)
|
409
|
+
result = @zookeeper.get(:path => full_path, :watcher => watch_proc)
|
410
|
+
end
|
411
|
+
|
412
|
+
# Cannot find this node
|
413
|
+
return 0 if result[:rc] == ::Zookeeper::ZNONODE
|
414
|
+
|
415
|
+
check_rc(result)
|
416
|
+
value = @deserializer.deserialize(result[:data])
|
417
|
+
stat = result[:stat]
|
418
|
+
|
419
|
+
# ZooKeeper assigns a nil value to all parent nodes when no value is supplied
|
420
|
+
# Call block if this is a leaf node, or if it is a parent node with a value
|
421
|
+
if block && ((stat.num_children == 0) || value != nil)
|
422
|
+
block.call(relative_key(full_path), value, stat.version, stat.num_children)
|
423
|
+
end
|
424
|
+
|
425
|
+
# Iterate over children if any
|
426
|
+
node_count = 1
|
427
|
+
# Ephemeral nodes cannot have children
|
428
|
+
if !(stat.ephemeral_owner && (stat.ephemeral_owner != 0)) && (watch || (stat.num_children > 0))
|
429
|
+
# Also watch this node for child changes
|
430
|
+
result = @zookeeper.get_children(:path => full_path, :watcher => watch_proc)
|
431
|
+
|
432
|
+
# This node could have been deleted already
|
433
|
+
if result[:rc] == ::Zookeeper::ZOK
|
434
|
+
children = result[:children]
|
435
|
+
|
436
|
+
# Save the current list of children so that we can figure out what
|
437
|
+
# a child changed event actually means
|
438
|
+
@children[full_path] = children if watch
|
439
|
+
|
440
|
+
# Also watch children nodes
|
441
|
+
children.each do |child|
|
442
|
+
node_count += get_recursive(File.join(full_path,child), watch, &block)
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
node_count
|
447
|
+
end
|
448
|
+
|
449
|
+
# Checks the return code from ZooKeeper and raises an exception if it is non-zero
|
450
|
+
def check_rc(result)
|
451
|
+
if result[:rc] != ::Zookeeper::ZOK
|
452
|
+
logger.error "Zookeeper failure", result
|
453
|
+
::Zookeeper::Exceptions.raise_on_error(result[:rc])
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
# The key was created in the registry
|
458
|
+
def node_created(key, value, version)
|
459
|
+
logger.debug "Created: #{key}", value
|
460
|
+
|
461
|
+
return unless @create_subscribers
|
462
|
+
|
463
|
+
# Subscribers to specific paths
|
464
|
+
if subscribers = @create_subscribers[key]
|
465
|
+
subscribers.each{|subscriber| subscriber.call(key, value, version)}
|
466
|
+
end
|
467
|
+
|
468
|
+
# Any subscribers for all events?
|
469
|
+
if all_subscribers = @create_subscribers['*']
|
470
|
+
all_subscribers.each{|subscriber| subscriber.call(key, value, version)}
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
# The key was updated in the registry
|
475
|
+
def node_updated(key, value, version)
|
476
|
+
logger.debug "Updated: #{key}", value
|
477
|
+
|
478
|
+
return unless @update_subscribers
|
479
|
+
|
480
|
+
# Subscribers to specific paths
|
481
|
+
if subscribers = @update_subscribers[key]
|
482
|
+
subscribers.each{|subscriber| subscriber.call(key, value, version)}
|
483
|
+
end
|
484
|
+
|
485
|
+
# Any subscribers for all events?
|
486
|
+
if all_subscribers = @update_subscribers['*']
|
487
|
+
all_subscribers.each{|subscriber| subscriber.call(key, value, version)}
|
488
|
+
end
|
489
|
+
end
|
490
|
+
|
491
|
+
# An existing key was removed from the registry
|
492
|
+
def node_deleted(key)
|
493
|
+
logger.debug { "Deleted: #{key}" }
|
494
|
+
|
495
|
+
return unless @delete_subscribers
|
496
|
+
|
497
|
+
# Subscribers to specific paths
|
498
|
+
if subscribers = @delete_subscribers[key]
|
499
|
+
subscribers.each{|subscriber| subscriber.call(key)}
|
500
|
+
end
|
501
|
+
|
502
|
+
# Any subscribers for all events?
|
503
|
+
if all_subscribers = @delete_subscribers['*']
|
504
|
+
all_subscribers.each{|subscriber| subscriber.call(key)}
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
end
|
509
|
+
end
|
510
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'semantic_logger'
|
2
|
+
module RubySkynet
|
3
|
+
module Zookeeper
|
4
|
+
autoload :Registry, 'ruby_skynet/zookeeper/registry'
|
5
|
+
autoload :CachedRegistry, 'ruby_skynet/zookeeper/cached_registry'
|
6
|
+
module Json
|
7
|
+
autoload :Deserializer, 'ruby_skynet/zookeeper/json/deserializer'
|
8
|
+
autoload :Serializer, 'ruby_skynet/zookeeper/json/serializer'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
data/lib/ruby_skynet.rb
CHANGED
@@ -2,7 +2,7 @@ require 'semantic_logger'
|
|
2
2
|
require 'ruby_skynet/exceptions'
|
3
3
|
require 'ruby_skynet/version'
|
4
4
|
require 'ruby_skynet/ruby_skynet'
|
5
|
-
require '
|
5
|
+
require 'ruby_skynet/zookeeper'
|
6
6
|
|
7
7
|
module RubySkynet
|
8
8
|
autoload :Base, 'ruby_skynet/base'
|
@@ -11,6 +11,7 @@ module RubySkynet
|
|
11
11
|
autoload :Client, 'ruby_skynet/client'
|
12
12
|
autoload :Service, 'ruby_skynet/service'
|
13
13
|
autoload :Server, 'ruby_skynet/server'
|
14
|
+
autoload :Registry, 'ruby_skynet/registry'
|
14
15
|
autoload :ServiceRegistry, 'ruby_skynet/service_registry'
|
15
16
|
end
|
16
17
|
|
data/test/client_test.rb
CHANGED
@@ -60,7 +60,7 @@ class ClientTest < Test::Unit::TestCase
|
|
60
60
|
@region = 'ClientTest'
|
61
61
|
RubySkynet.region = @region
|
62
62
|
RubySkynet::Server.start
|
63
|
-
# Give
|
63
|
+
# Give Service Registry time to push out the presence of the service above
|
64
64
|
sleep 0.1
|
65
65
|
|
66
66
|
@service_name = 'ClientTestService'
|
@@ -28,39 +28,31 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
28
28
|
RubySkynet.local_ip_address = nil
|
29
29
|
end
|
30
30
|
|
31
|
-
context "without a registered service" do
|
32
|
-
should "not be in doozer" do
|
33
|
-
RubySkynet.services.send(:doozer_pool).with_connection do |doozer|
|
34
|
-
assert_equal nil, doozer[@service_key]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
31
|
context "with a registered service" do
|
40
32
|
setup do
|
41
|
-
RubySkynet.
|
42
|
-
RubySkynet.
|
43
|
-
# Allow time for
|
33
|
+
RubySkynet.service_registry.register_service(@service_name, @version, @region, @hostname, @port)
|
34
|
+
RubySkynet.service_registry.register_service(@service_name, @version, @region+'BLAH', @hostname, @port)
|
35
|
+
# Allow time for registry callback that service was registered
|
44
36
|
sleep 0.1
|
45
37
|
end
|
46
38
|
|
47
39
|
teardown do
|
48
|
-
RubySkynet.
|
49
|
-
RubySkynet.
|
50
|
-
# Allow time for
|
40
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version, @region, @hostname, @port)
|
41
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version, @region+'BLAH', @hostname, @port)
|
42
|
+
# Allow time for registry callback that service was deregistered
|
51
43
|
sleep 0.1
|
52
44
|
# No servers should be in the local registry
|
53
|
-
assert_equal nil, RubySkynet.
|
45
|
+
# assert_equal nil, RubySkynet.service_registry.servers_for(@service_name, @version, @region)
|
54
46
|
end
|
55
47
|
|
56
48
|
should "find server using exact match" do
|
57
|
-
assert servers = RubySkynet.
|
49
|
+
assert (servers = RubySkynet.service_registry.servers_for(@service_name, @version, @region)), RubySkynet.service_registry.to_h.inspect
|
58
50
|
assert_equal 1, servers.size
|
59
51
|
assert_equal "#{@hostname}:#{@port}", servers.first
|
60
52
|
end
|
61
53
|
|
62
54
|
should "find server using * version match" do
|
63
|
-
assert servers = RubySkynet.
|
55
|
+
assert (servers = RubySkynet.service_registry.servers_for(@service_name, '*', @region)), RubySkynet.service_registry.to_h.inspect
|
64
56
|
assert_equal 1, servers.size
|
65
57
|
assert_equal "#{@hostname}:#{@port}", servers.first
|
66
58
|
end
|
@@ -68,21 +60,21 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
68
60
|
context "with multiple servers" do
|
69
61
|
setup do
|
70
62
|
@second_hostname = '127.0.10.1'
|
71
|
-
RubySkynet.
|
72
|
-
RubySkynet.
|
73
|
-
RubySkynet.
|
74
|
-
RubySkynet.
|
63
|
+
RubySkynet.service_registry.register_service(@service_name, @version, @region, @hostname, @port+1)
|
64
|
+
RubySkynet.service_registry.register_service(@service_name, @version, @region, @hostname, @port+3)
|
65
|
+
RubySkynet.service_registry.register_service(@service_name, @version-1, @region, @hostname, @port+2)
|
66
|
+
RubySkynet.service_registry.register_service(@service_name, @version, @region, @second_hostname, @port)
|
75
67
|
end
|
76
68
|
|
77
69
|
teardown do
|
78
|
-
RubySkynet.
|
79
|
-
RubySkynet.
|
80
|
-
RubySkynet.
|
81
|
-
RubySkynet.
|
70
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version, @region, @hostname, @port+1)
|
71
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version, @region, @hostname, @port+3)
|
72
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version-1, @region, @hostname, @port+2)
|
73
|
+
RubySkynet.service_registry.deregister_service(@service_name, @version, @region, @second_hostname, @port)
|
82
74
|
end
|
83
75
|
|
84
76
|
should "using * version match" do
|
85
|
-
assert servers = RubySkynet.
|
77
|
+
assert (servers = RubySkynet.service_registry.servers_for(@service_name, '*', @region)), RubySkynet.service_registry.to_h.inspect
|
86
78
|
assert_equal 3, servers.size, servers
|
87
79
|
assert_equal true, servers.include?("#{@hostname}:#{@port}"), servers
|
88
80
|
assert_equal true, servers.include?("#{@hostname}:#{@port+1}"), servers
|
@@ -91,21 +83,15 @@ class ServiceRegistryTest < Test::Unit::TestCase
|
|
91
83
|
end
|
92
84
|
|
93
85
|
should "return nil when service not found" do
|
94
|
-
assert_equal nil, RubySkynet.
|
86
|
+
assert_equal nil, RubySkynet.service_registry.servers_for('MissingService', @version, @region)
|
95
87
|
end
|
96
88
|
|
97
89
|
should "return nil when version not found" do
|
98
|
-
assert_equal nil, RubySkynet.
|
90
|
+
assert_equal nil, RubySkynet.service_registry.servers_for(@service_name, @version+1, @region)
|
99
91
|
end
|
100
92
|
|
101
93
|
should "return nil when region not found" do
|
102
|
-
assert_equal nil, RubySkynet.
|
103
|
-
end
|
104
|
-
|
105
|
-
should "be in doozer" do
|
106
|
-
RubySkynet.services.send(:doozer_pool).with_connection do |doozer|
|
107
|
-
assert_equal true, doozer[@service_key].length > 20
|
108
|
-
end
|
94
|
+
assert_equal nil, RubySkynet.service_registry.servers_for(@service_name, @version, 'OtherRegion')
|
109
95
|
end
|
110
96
|
end
|
111
97
|
|