ruby_skynet 0.8.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
|