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,75 @@
|
|
1
|
+
require 'thread_safe'
|
2
|
+
require 'semantic_logger'
|
3
|
+
require 'ruby_skynet/zookeeper/registry'
|
4
|
+
|
5
|
+
#
|
6
|
+
# CachedRegistry
|
7
|
+
#
|
8
|
+
# Store information in ZooKeeper and subscribe to future changes
|
9
|
+
# and keep a local copy of the information in ZooKeeper
|
10
|
+
#
|
11
|
+
# Notifies registered subscribers when information has changed
|
12
|
+
#
|
13
|
+
# All paths specified are relative to the root_path. As such the root key
|
14
|
+
# is never returned, nor is it required when a key is supplied as input.
|
15
|
+
# For example, with a root_path of /foo/bar, any paths passed in will leave
|
16
|
+
# out the root_path: host/name
|
17
|
+
#
|
18
|
+
# Keeps a local copy in memory of all descendant values of the supplied root_path
|
19
|
+
# Supports high-frequency calls to retrieve registry data
|
20
|
+
# The in-memory cache will be kept in synch with any changes on the server
|
21
|
+
module RubySkynet
|
22
|
+
module Zookeeper
|
23
|
+
class CachedRegistry < Registry
|
24
|
+
# Logging instance for this class
|
25
|
+
include SemanticLogger::Loggable
|
26
|
+
|
27
|
+
# Create a CachedRegistry instance to manage information within the Registry
|
28
|
+
# and keep a local cached copy of the data in the Registry to support
|
29
|
+
# high-speed or frequent reads.
|
30
|
+
#
|
31
|
+
# Writes are sent to ZooKeeper and then replicated back to the local cache
|
32
|
+
# only once ZooKeeper has updated its store
|
33
|
+
#
|
34
|
+
# See RubySkynet::Zookeeper::Registry for the complete list of options
|
35
|
+
#
|
36
|
+
def initialize(params)
|
37
|
+
@cache = ThreadSafe::Hash.new
|
38
|
+
# Supplied block to load the current keys from the Registry
|
39
|
+
super(params) do |key, value, version|
|
40
|
+
@cache[key] = value
|
41
|
+
end
|
42
|
+
|
43
|
+
on_create {|key, value| @cache[key] = value}
|
44
|
+
on_update {|key, value, version| @cache[key] = value}
|
45
|
+
on_delete {|key| @cache.delete(key)}
|
46
|
+
end
|
47
|
+
|
48
|
+
# Retrieve the latest value from a specific key from the registry
|
49
|
+
def [](key)
|
50
|
+
@cache[key]
|
51
|
+
end
|
52
|
+
|
53
|
+
# Iterate over every key, value pair in the registry at the root_path
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
# registry.each_pair {|k,v| puts "#{k} => #{v}"}
|
57
|
+
def each_pair(&block)
|
58
|
+
# Have to duplicate the cache otherwise concurrent changes to the
|
59
|
+
# registry will interfere with the iterator
|
60
|
+
@cache.dup.each_pair(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns [Array<String>] all keys in the registry
|
64
|
+
def keys
|
65
|
+
@cache.keys
|
66
|
+
end
|
67
|
+
|
68
|
+
# Returns a copy of the registry as a Hash
|
69
|
+
def to_h
|
70
|
+
@cache.dup
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# This monkey-patch must be removed when ticket #44 has been included in
|
2
|
+
# an updated ZooKeeper Gem
|
3
|
+
# Ticket: https://github.com/slyphon/zookeeper/issues/44
|
4
|
+
# Pull Request: https://github.com/slyphon/zookeeper/pull/45
|
5
|
+
|
6
|
+
module Zookeeper
|
7
|
+
class JavaBase
|
8
|
+
|
9
|
+
def get(req_id, path, callback, watcher)
|
10
|
+
handle_keeper_exception do
|
11
|
+
watch_cb = watcher ? create_watcher(req_id, path) : false
|
12
|
+
|
13
|
+
if callback
|
14
|
+
jzk.getData(path, watch_cb, JavaCB::DataCallback.new(req_id), event_queue)
|
15
|
+
[Code::Ok, nil, nil] # the 'nil, nil' isn't strictly necessary here
|
16
|
+
else # sync
|
17
|
+
stat = JZKD::Stat.new
|
18
|
+
value = jzk.getData(path, watch_cb, stat)
|
19
|
+
data = String.from_java_bytes(value) unless value.nil?
|
20
|
+
|
21
|
+
[Code::Ok, data, stat.to_hash]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'multi_json'
|
3
|
+
module RubySkynet
|
4
|
+
module Zookeeper
|
5
|
+
module Json
|
6
|
+
|
7
|
+
# Deserialize from JSON entries in Zookeeper
|
8
|
+
module Deserializer
|
9
|
+
def self.deserialize(value)
|
10
|
+
return value if value.nil? || (value == '')
|
11
|
+
|
12
|
+
if value.strip.start_with?('{') || value.strip.start_with?('[{')
|
13
|
+
symbolize(MultiJson.load(value))
|
14
|
+
else
|
15
|
+
symbolize_string(value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Returns the supplied value symbolized
|
20
|
+
def self.symbolize(v)
|
21
|
+
if v.is_a?(Hash)
|
22
|
+
symbolize_hash(v)
|
23
|
+
elsif v.is_a?(Array)
|
24
|
+
symbolize_array(v)
|
25
|
+
elsif v.is_a?(String)
|
26
|
+
symbolize_string(v)
|
27
|
+
else
|
28
|
+
v
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a new hash updated with keys and values that are strings
|
33
|
+
# starting with ':' are turned into symbols
|
34
|
+
def self.symbolize_hash(hash)
|
35
|
+
h = hash.dup
|
36
|
+
hash.each_pair do |k, v|
|
37
|
+
# Convert values in the hash
|
38
|
+
h[k] = symbolize(v)
|
39
|
+
|
40
|
+
# Convert key to a symbol if it is a symbol string
|
41
|
+
h[k[1..-1].to_sym] = h.delete(k) if k.is_a?(String) && k.start_with?(':')
|
42
|
+
end
|
43
|
+
h
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns a new Array with any symbols strings returned as symbols
|
47
|
+
def self.symbolize_array(a)
|
48
|
+
a.collect {|v| symbolize(v)}
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a new string with the string parsed and symbol string converted to a symbol
|
52
|
+
def self.symbolize_string(s)
|
53
|
+
# JSON Parser cannot parse non-hash/array values
|
54
|
+
value = YAML.load(s)
|
55
|
+
# Now check for symbols which are strings starting with ':'
|
56
|
+
value.is_a?(String) && value.start_with?(':') ? value[1..-1].to_sym : value
|
57
|
+
rescue Exception
|
58
|
+
s
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
module RubySkynet
|
3
|
+
module Zookeeper
|
4
|
+
module Json
|
5
|
+
|
6
|
+
# Serialize to JSON for storing in Doozer
|
7
|
+
module Serializer
|
8
|
+
def self.serialize(value)
|
9
|
+
if value.is_a?(Hash) || value.is_a?(Array)
|
10
|
+
MultiJson.encode(desymbolize(value))
|
11
|
+
elsif value.is_a?(Symbol)
|
12
|
+
desymbolize_symbol(value)
|
13
|
+
else
|
14
|
+
value.to_s
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Returns the supplied value with symbols converted to a string prefixed
|
19
|
+
# with ':'
|
20
|
+
def self.desymbolize(v)
|
21
|
+
if v.is_a?(Hash)
|
22
|
+
desymbolize_hash(v)
|
23
|
+
elsif v.is_a?(Array)
|
24
|
+
desymbolize_array(v)
|
25
|
+
elsif v.is_a?(Symbol)
|
26
|
+
desymbolize_symbol(v)
|
27
|
+
else
|
28
|
+
v.to_s
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns a new hash with all symbol keys and values as strings starting with ':'
|
33
|
+
def self.desymbolize_hash(hash)
|
34
|
+
h = hash.dup
|
35
|
+
hash.each_pair do |k, v|
|
36
|
+
# Convert values in the hash
|
37
|
+
h[k] = desymbolize(v)
|
38
|
+
|
39
|
+
# Convert key to a string if it is a symbol
|
40
|
+
h[desymbolize_symbol(k)] = h.delete(k) if k.is_a?(Symbol)
|
41
|
+
end
|
42
|
+
h
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a new Array with any symbols returned as symbol strings
|
46
|
+
def self.desymbolize_array(a)
|
47
|
+
a.collect {|v| desymbolize(v)}
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.desymbolize_symbol(s)
|
51
|
+
":#{s}"
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|