zk-server 1.0.1 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/zk-server +7 -0
- data/lib/zk-server/base.rb +13 -18
- data/lib/zk-server/cluster.rb +96 -0
- data/lib/zk-server/command.rb +69 -0
- data/lib/zk-server/config.rb +174 -7
- data/lib/zk-server/sub_process.rb +36 -9
- data/lib/zk-server/version.rb +1 -1
- data/lib/zk-server.rb +4 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/wait_watchers.rb +55 -0
- data/spec/zk-server/cluster_spec.rb +34 -0
- data/zk-server.gemspec +1 -0
- metadata +30 -7
data/bin/zk-server
ADDED
data/lib/zk-server/base.rb
CHANGED
@@ -7,10 +7,10 @@ module ZK
|
|
7
7
|
include Logging
|
8
8
|
|
9
9
|
def_delegators :config,
|
10
|
-
:base_dir, :data_dir, :log4j_props_path, :
|
11
|
-
:
|
12
|
-
:
|
13
|
-
|
10
|
+
:base_dir, :data_dir, :log4j_props_path, :command_args,
|
11
|
+
:client_port, :stdio_redirect_path, :zoo_cfg_path,
|
12
|
+
:zoo_myid_path, :myid
|
13
|
+
|
14
14
|
# the {Config} object that will be used to configure this Process
|
15
15
|
attr_accessor :config
|
16
16
|
|
@@ -18,7 +18,7 @@ module ZK
|
|
18
18
|
@child_startup_timeout = opts.delete(:child_startup_timeout) || 6
|
19
19
|
|
20
20
|
@run_called = false
|
21
|
-
@config = Config.new(opts)
|
21
|
+
@config = opts[:config] || Config.new(opts)
|
22
22
|
|
23
23
|
@mutex = Monitor.new
|
24
24
|
@exit_cond = @mutex.new_cond
|
@@ -69,6 +69,7 @@ module ZK
|
|
69
69
|
def create_files!
|
70
70
|
mkdir_p base_dir
|
71
71
|
mkdir_p data_dir
|
72
|
+
write_myid!
|
72
73
|
write_zoo_cfg!
|
73
74
|
write_log4j_properties!
|
74
75
|
mkdir_p(File.dirname(stdio_redirect_path))
|
@@ -80,21 +81,15 @@ module ZK
|
|
80
81
|
end
|
81
82
|
end
|
82
83
|
|
84
|
+
def write_myid!
|
85
|
+
File.open(zoo_myid_path, 'w') do |io|
|
86
|
+
io.puts myid
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
83
90
|
def write_zoo_cfg!
|
84
91
|
File.open(zoo_cfg_path, 'w') do |fp|
|
85
|
-
fp.puts
|
86
|
-
tickTime=#{tick_time}
|
87
|
-
dataDir=#{data_dir}
|
88
|
-
clientPort=#{client_port}
|
89
|
-
maxClientCnxns=#{max_client_cnxns}
|
90
|
-
EOS
|
91
|
-
|
92
|
-
fp.puts("forceSync=#{force_sync}") if force_sync
|
93
|
-
fp.puts("snapCount=#{snap_count}") if snap_count
|
94
|
-
zoo_cfg_hash.each do |k,v|
|
95
|
-
fp.puts("#{k}=#{v}")
|
96
|
-
end
|
97
|
-
|
92
|
+
fp.puts(config.to_config_file_str)
|
98
93
|
fp.fsync
|
99
94
|
end
|
100
95
|
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module ZK
|
2
|
+
module Server
|
3
|
+
# Cluster simplifies the case when you want to test a 3+ node cluster
|
4
|
+
# You give the base_dir, and number of nodes, and it will construct a
|
5
|
+
# cluster for you. It's less configurable than the SubProcess class, but
|
6
|
+
# that's the price of convenience!
|
7
|
+
class Cluster
|
8
|
+
FOLLOWER_PORT_OFFSET = 100
|
9
|
+
LEADER_PORT_OFFSET = 200
|
10
|
+
DEFAULT_BASE_PORT = 21811
|
11
|
+
|
12
|
+
attr_accessor :base_dir
|
13
|
+
|
14
|
+
# defaults to 21811, used as the lowest port number for the cluster,
|
15
|
+
# all others will be offsets of this number
|
16
|
+
attr_accessor :base_port
|
17
|
+
|
18
|
+
# access to the SubProcess instances that make up this cluster
|
19
|
+
attr_reader :processes
|
20
|
+
|
21
|
+
# how many nodes in the cluster
|
22
|
+
attr_reader :num_members
|
23
|
+
|
24
|
+
def initialize(num_members, opts={})
|
25
|
+
@num_members = num_members
|
26
|
+
@base_dir = Config.default_base_dir
|
27
|
+
@base_port = DEFAULT_BASE_PORT
|
28
|
+
@processes = nil
|
29
|
+
@running = false
|
30
|
+
opts.each { |k,v| __send__(:"#{k}=", v) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def running?
|
34
|
+
!!@running
|
35
|
+
end
|
36
|
+
|
37
|
+
def run
|
38
|
+
return if running?
|
39
|
+
@running = true
|
40
|
+
|
41
|
+
processes.each { |p| p.run }
|
42
|
+
rescue Exception
|
43
|
+
processes.each { |p| p.shutdown }
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
|
47
|
+
def shutdown
|
48
|
+
return unless running?
|
49
|
+
@running = false
|
50
|
+
|
51
|
+
pary, @processes = @processes, nil
|
52
|
+
|
53
|
+
pary.each(&:shutdown)
|
54
|
+
pary
|
55
|
+
end
|
56
|
+
|
57
|
+
def clobber!
|
58
|
+
processes.each(&:clobber!)
|
59
|
+
end
|
60
|
+
|
61
|
+
def all_running?
|
62
|
+
processes.all?(&:running?)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ping_all?
|
66
|
+
processes.all?(&:ping?)
|
67
|
+
end
|
68
|
+
|
69
|
+
def processes
|
70
|
+
@processes ||= [].tap do |ary|
|
71
|
+
num_members.times do |idx|
|
72
|
+
ary << SubProcess.new.tap do |sp|
|
73
|
+
c = sp.config
|
74
|
+
|
75
|
+
c.myid = idx
|
76
|
+
c.base_dir = File.join(@base_dir, "server-#{idx}")
|
77
|
+
c.client_port = base_port + idx
|
78
|
+
c.zoo_cfg_hash = server_hash
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# terrible name, the list of 'servers' lines in the config
|
85
|
+
#
|
86
|
+
def server_hash
|
87
|
+
@server_hash ||= {}.tap do |h|
|
88
|
+
num_members.times do |idx|
|
89
|
+
h["server.#{idx}"] = "127.0.0.1:#{base_port + FOLLOWER_PORT_OFFSET + idx}:#{base_port + LEADER_PORT_OFFSET + idx}"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end # Cluster
|
95
|
+
end # Server
|
96
|
+
end # ZK
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module ZK
|
4
|
+
module Server
|
5
|
+
class Command
|
6
|
+
include Logging
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@cond = ConditionVariable.new
|
11
|
+
@shutdown_requested = false
|
12
|
+
end
|
13
|
+
|
14
|
+
def spawn_shutdown_handler
|
15
|
+
@shutdown_thread ||= Thread.new do
|
16
|
+
@mutex.synchronize do
|
17
|
+
@cond.wait until @shutdown_requested
|
18
|
+
logger.debug { "shutdown thread awakened! shutting down server!" }
|
19
|
+
@server.shutdown if @server
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def run
|
25
|
+
opts = Slop.parse(:help => true, :strict => true) do
|
26
|
+
banner "zk-server [opts] runs a ZooKeeper server in the foreground"
|
27
|
+
on :d, :base_dir=, "defaults to #{ZK::Server::Config.default_base_dir}"
|
28
|
+
on :force_sync, 'force fsync on every snapshot'
|
29
|
+
on :skip_acl, 'skip acl checks'
|
30
|
+
on :p, :port=, 'port to listen on', :as => :integer
|
31
|
+
on :jvm_flags=, 'additional JVM flags to pass'
|
32
|
+
on :snap_count=, 'how often to take a snapshot, default 100_000', :as => :integer
|
33
|
+
end
|
34
|
+
|
35
|
+
return if opts.help?
|
36
|
+
|
37
|
+
hash = opts.to_hash
|
38
|
+
|
39
|
+
hash.delete(:help)
|
40
|
+
hash.reject! { |k,v| v.nil? }
|
41
|
+
|
42
|
+
config = ZK::Server::Config.new(hash)
|
43
|
+
|
44
|
+
spawn_shutdown_handler
|
45
|
+
|
46
|
+
|
47
|
+
%w[HUP INT].each do |sig|
|
48
|
+
trap(sig) do
|
49
|
+
@mutex.synchronize do
|
50
|
+
$stderr.puts "trapped #{sig}, shutting down"
|
51
|
+
@shutdown_requested = true
|
52
|
+
@cond.broadcast
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
@server = ZK::Server.new(:config => config)
|
58
|
+
@server.run
|
59
|
+
|
60
|
+
unless @server.join
|
61
|
+
$stderr.puts "server exited with status #{@server.status.inspect}"
|
62
|
+
st = @server.status
|
63
|
+
exit st.exited? ? st.exitstatus : 42
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
data/lib/zk-server/config.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
module ZK
|
2
2
|
module Server
|
3
|
+
# Note that this supports all of the 3.3.5 options, but __will not__ do any
|
4
|
+
# sanity checking for you. All options specifyable here (especially those that
|
5
|
+
# require directories to be created outside of {#base_dir}) may not be handled
|
6
|
+
# properly by {Base} and subclasses.
|
7
|
+
#
|
3
8
|
class Config
|
4
9
|
DEFAULT_JVM_FLAGS = %w[
|
5
|
-
-server
|
10
|
+
-server
|
6
11
|
-Xmx256m
|
7
|
-
-Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
|
12
|
+
-Dzookeeper.serverCnxnFactory=org.apache.zookeeper.server.NettyServerCnxnFactory
|
8
13
|
].freeze
|
9
14
|
|
10
15
|
# the com.sun.managemnt.jmxremote.port arg will be filled in dynamically
|
@@ -23,12 +28,32 @@ module ZK
|
|
23
28
|
# basis for all other path generation. Defaults to `File.join(Dir.getwd, 'zookeeper')`
|
24
29
|
attr_accessor :base_dir
|
25
30
|
|
31
|
+
# defaults to `#{base_dir}/data`. use this to override the default
|
32
|
+
#
|
33
|
+
# > the location where ZooKeeper will store the in-memory database
|
34
|
+
# > snapshots and, unless specified otherwise, the transaction log of
|
35
|
+
# > updates to the database.
|
36
|
+
#
|
37
|
+
# be aware that the {ZK::Server::Base} class will not create any
|
38
|
+
# directories but {#base_dir}.
|
39
|
+
#
|
40
|
+
attr_writer :data_dir
|
41
|
+
|
42
|
+
# defaults to nil, and zookeeper will just use the default value (being the
|
43
|
+
# same as {#data_dir}
|
44
|
+
#
|
45
|
+
# > This option will direct the machine to write the transaction log to the
|
46
|
+
# > dataLogDir rather than the dataDir. This allows a dedicated log device
|
47
|
+
# > to be used, and helps avoid competition between logging and snaphots.
|
48
|
+
#
|
49
|
+
attr_accessor :data_log_dir
|
50
|
+
|
26
51
|
# a hash that will be used to provide extra values for the zoo.cfg file.
|
27
|
-
# keys are written as-is to the file, so they should be camel-cased.
|
52
|
+
# keys are written as-is to the file, so they should be camel-cased.
|
28
53
|
#
|
29
54
|
# dataDir will be set relative to {#base_dir} and clientPort will either use
|
30
55
|
# the default of 2181, or can be adjusted by {#client_port=}
|
31
|
-
#
|
56
|
+
#
|
32
57
|
attr_accessor :zoo_cfg_hash
|
33
58
|
|
34
59
|
# what port should the server listen on for connections? (default 2181)
|
@@ -42,6 +67,36 @@ module ZK
|
|
42
67
|
# defaults to 2000
|
43
68
|
attr_accessor :tick_time
|
44
69
|
|
70
|
+
# what cluster id should this zookeeper use for itself? (default 1)
|
71
|
+
attr_accessor :myid
|
72
|
+
|
73
|
+
# default is nil, and will not be specified in the config
|
74
|
+
#
|
75
|
+
# > Clients can submit requests faster than ZooKeeper can process them,
|
76
|
+
# > especially if there are a lot of clients. To prevent ZooKeeper from
|
77
|
+
# > running out of memory due to queued requests, ZooKeeper will throttle
|
78
|
+
# > clients so that there is no more than globalOutstandingLimit
|
79
|
+
# > outstanding requests in the system. The default limit is 1,000.
|
80
|
+
#
|
81
|
+
attr_accessor :global_outstanding_limit
|
82
|
+
|
83
|
+
# necessary for cluster nodes (default 5)
|
84
|
+
#
|
85
|
+
# from the zookeeper admin guide:
|
86
|
+
# > Amount of time, in ticks (see tickTime), to allow followers to connect
|
87
|
+
# > and sync to a leader. Increased this value as needed, if the amount of data
|
88
|
+
# > managed by ZooKeeper is large.
|
89
|
+
#
|
90
|
+
attr_accessor :init_limit
|
91
|
+
|
92
|
+
# necessary for cluster nodes (default 2)
|
93
|
+
#
|
94
|
+
# > Amount of time, in ticks (see tickTime), to allow followers to sync
|
95
|
+
# > with ZooKeeper. If followers fall too far behind a leader, they will be
|
96
|
+
# > dropped.
|
97
|
+
#
|
98
|
+
attr_accessor :sync_limit
|
99
|
+
|
45
100
|
# from the [admin guide](http://zookeeper.apache.org/doc/r3.3.5/zookeeperAdmin.html)
|
46
101
|
#
|
47
102
|
# > ZooKeeper logs transactions to a transaction log. After snapCount
|
@@ -50,7 +105,7 @@ module ZK
|
|
50
105
|
#
|
51
106
|
# For testing, to speed up disk IO, I generally set this to 1_000_000 and
|
52
107
|
# force_sync to false. YMMV, understand what this does before messing with it
|
53
|
-
# if you care about your data.
|
108
|
+
# if you care about your data.
|
54
109
|
#
|
55
110
|
# default: unset
|
56
111
|
attr_accessor :snap_count
|
@@ -59,9 +114,74 @@ module ZK
|
|
59
114
|
# an fsync() call for each snapshot write. It is however DANGEROUS if you
|
60
115
|
# care about the data. (I set it to false for running tests)
|
61
116
|
#
|
117
|
+
# if true: 'yes', false: 'no', nil not specified in the config
|
118
|
+
#
|
62
119
|
# default: no value set
|
63
120
|
attr_accessor :force_sync
|
64
121
|
|
122
|
+
# default: nil
|
123
|
+
#
|
124
|
+
# > To avoid seeks ZooKeeper allocates space in the transaction log file in
|
125
|
+
# > blocks of preAllocSize kilobytes. The default block size is 64M. One
|
126
|
+
# > reason for changing the size of the blocks is to reduce the block size
|
127
|
+
# > if snapshots are taken more often. (Also, see snapCount).
|
128
|
+
attr_accessor :pre_alloc_size
|
129
|
+
|
130
|
+
# default: nil
|
131
|
+
#
|
132
|
+
# > the address (ipv4, ipv6 or hostname) to listen for client
|
133
|
+
# > connections; that is, the address that clients attempt to connect to.
|
134
|
+
# > This is optional, by default we bind in such a way that any connection
|
135
|
+
# > to the clientPort for any address/interface/nic on the server will be
|
136
|
+
# > accepted.
|
137
|
+
#
|
138
|
+
attr_accessor :client_port_address
|
139
|
+
|
140
|
+
# default: nil
|
141
|
+
#
|
142
|
+
# > the minimum session timeout in milliseconds that the server will allow
|
143
|
+
# > the client to negotiate. Defaults to 2 times the tickTime
|
144
|
+
#
|
145
|
+
attr_accessor :min_session_timeout
|
146
|
+
|
147
|
+
# default: nil
|
148
|
+
#
|
149
|
+
# > the maximum session timeout in milliseconds that the server will allow
|
150
|
+
# > the client to negotiate. Defaults to 20 times the tickTime.
|
151
|
+
attr_accessor :max_session_timeout
|
152
|
+
|
153
|
+
# default: nil
|
154
|
+
#
|
155
|
+
# If true, the value 'yes' will be used for this value, false will write 'no'
|
156
|
+
# to the config file. The default (nil) is not to specify a value in the config.
|
157
|
+
#
|
158
|
+
# > Leader accepts client connections. Default value is "yes". The leader
|
159
|
+
# > machine coordinates updates. For higher update throughput at thes
|
160
|
+
# > slight expense of read throughput the leader can be configured to not
|
161
|
+
# > accept clients and focus on coordination. The default to this option is
|
162
|
+
# > yes, which means that a leader will accept client connections
|
163
|
+
#
|
164
|
+
attr_accessor :leader_serves
|
165
|
+
|
166
|
+
# default: nil
|
167
|
+
#
|
168
|
+
# by default this is not specified
|
169
|
+
#
|
170
|
+
# > Sets the timeout value for opening connections for leader election
|
171
|
+
# > notifications.
|
172
|
+
#
|
173
|
+
attr_accessor :cnx_timeout
|
174
|
+
|
175
|
+
# default: nil
|
176
|
+
#
|
177
|
+
# if true: 'yes', false: 'no', nil not specified in the config
|
178
|
+
#
|
179
|
+
# this is listed as a DANGEROUS setting
|
180
|
+
#
|
181
|
+
# > Skips ACL checks. This results in a boost in throughput, but opens up
|
182
|
+
# > full access to the data tree to everyone.
|
183
|
+
attr_accessor :skip_acl
|
184
|
+
|
65
185
|
# if truthy, will enable jmx (defaults to false)
|
66
186
|
# note that our defualt jmx config has all security and auth turned off
|
67
187
|
# if you want to customize this, then use jvm_flags and set this to false
|
@@ -75,8 +195,13 @@ module ZK
|
|
75
195
|
# default is {DEEFAULT_JVM_FLAGS}
|
76
196
|
attr_accessor :jvm_flags
|
77
197
|
|
198
|
+
def self.default_base_dir
|
199
|
+
File.join(Dir.getwd, 'zookeeper')
|
200
|
+
end
|
201
|
+
|
78
202
|
def initialize(opts={})
|
79
|
-
|
203
|
+
$stderr.puts "#{self.class}#initialize #{opts.inspect}"
|
204
|
+
@base_dir = self.class.default_base_dir
|
80
205
|
@zoo_cfg_hash = {}
|
81
206
|
@tick_time = 2000
|
82
207
|
@client_port = 2181
|
@@ -85,6 +210,9 @@ module ZK
|
|
85
210
|
@jmx_port = 22222
|
86
211
|
@enable_jmx = false
|
87
212
|
@jvm_flags = DEFAULT_JVM_FLAGS.dup
|
213
|
+
@myid = 1
|
214
|
+
@init_limit = 5
|
215
|
+
@sync_limit = 2
|
88
216
|
|
89
217
|
@max_client_cnxns = 100
|
90
218
|
|
@@ -96,6 +224,11 @@ module ZK
|
|
96
224
|
File.join(base_dir, 'zoo.cfg')
|
97
225
|
end
|
98
226
|
|
227
|
+
# @private
|
228
|
+
def zoo_myid_path
|
229
|
+
File.join(base_dir, 'data', 'myid')
|
230
|
+
end
|
231
|
+
|
99
232
|
# @private
|
100
233
|
def log4j_props_path
|
101
234
|
File.join(base_dir, 'log4j.properties')
|
@@ -113,7 +246,7 @@ module ZK
|
|
113
246
|
|
114
247
|
# @private
|
115
248
|
def data_dir
|
116
|
-
File.join(base_dir, 'data')
|
249
|
+
@data_dir ||= File.join(base_dir, 'data')
|
117
250
|
end
|
118
251
|
|
119
252
|
# @private
|
@@ -132,6 +265,40 @@ module ZK
|
|
132
265
|
cmd += jvm_flags
|
133
266
|
cmd += %W[-cp #{classpath.join(':')} #{ZOO_MAIN} #{zoo_cfg_path}]
|
134
267
|
end
|
268
|
+
|
269
|
+
# renders this config as a string that can be written to zoo.cfg
|
270
|
+
def to_config_file_str
|
271
|
+
config = {
|
272
|
+
'dataDir' => data_dir,
|
273
|
+
'skipACL' => skip_acl,
|
274
|
+
'tickTime' => tick_time,
|
275
|
+
'initLimit' => init_limit,
|
276
|
+
'syncLimit' => sync_limit,
|
277
|
+
'forceSync' => force_sync,
|
278
|
+
'snapCount' => snap_count,
|
279
|
+
'clientPort' => client_port,
|
280
|
+
'dataLogDir' => data_log_dir,
|
281
|
+
'preAllocSize' => pre_alloc_size,
|
282
|
+
'leaderServes' => leader_serves,
|
283
|
+
'maxClientCnxns' => max_client_cnxns,
|
284
|
+
'clientPortAddress' => client_port_address,
|
285
|
+
'minSessionTimeout' => min_session_timeout,
|
286
|
+
'maxSessionTimeout' => max_session_timeout,
|
287
|
+
'globalOutstandingLimit' => global_outstanding_limit,
|
288
|
+
}
|
289
|
+
|
290
|
+
config = config.merge(zoo_cfg_hash)
|
291
|
+
|
292
|
+
config.delete_if { |k,v| v.nil? }
|
293
|
+
|
294
|
+
%w[leaderServes skipACL forceSync].each do |yorn_key|
|
295
|
+
if config.has_key?(yorn_key)
|
296
|
+
config[yorn_key] = config[yorn_key] ? 'yes' : 'no'
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
config.sort.map {|kv| kv.join("=") }.join("\n")
|
301
|
+
end
|
135
302
|
end
|
136
303
|
end
|
137
304
|
end
|
@@ -8,7 +8,7 @@ module ZK
|
|
8
8
|
# to store our data under (configurable).
|
9
9
|
#
|
10
10
|
class SubProcess < Base
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :status
|
12
12
|
|
13
13
|
# how long should we wait for the child to start responding to 'ruok'?
|
14
14
|
attr_accessor :child_startup_timeout
|
@@ -17,14 +17,28 @@ module ZK
|
|
17
17
|
@exit_watching_thread = nil
|
18
18
|
|
19
19
|
@pid = nil
|
20
|
-
@
|
20
|
+
@status = nil
|
21
21
|
|
22
22
|
super
|
23
23
|
end
|
24
24
|
|
25
|
+
# wait for sub-process to exit
|
26
|
+
def join
|
27
|
+
@mutex.synchronize do
|
28
|
+
logger.debug { "spawned process #{@pid}" }
|
29
|
+
@exit_cond.wait_until { @status }
|
30
|
+
end
|
31
|
+
@status.success?
|
32
|
+
end
|
33
|
+
|
25
34
|
# true if the process was started and is still running
|
26
35
|
def running?
|
27
|
-
spawned? and !@
|
36
|
+
spawned? and !@status and alive?
|
37
|
+
end
|
38
|
+
|
39
|
+
def alive?
|
40
|
+
return false unless spawned?
|
41
|
+
false|kill(0)
|
28
42
|
rescue Errno::ESRCH
|
29
43
|
false
|
30
44
|
end
|
@@ -37,16 +51,16 @@ module ZK
|
|
37
51
|
# shutdown the child, wait for it to exit, ensure it is dead
|
38
52
|
def shutdown
|
39
53
|
if @pid
|
40
|
-
return if @
|
54
|
+
return if @status
|
41
55
|
|
42
56
|
@mutex.synchronize do
|
43
57
|
%w[HUP TERM KILL].each do |signal|
|
44
58
|
logger.debug { "sending #{signal} to #{@pid}" }
|
45
59
|
|
46
|
-
return unless running? # jruby doesn't seem to get @
|
60
|
+
return unless running? # jruby doesn't seem to get @status ?
|
47
61
|
|
48
62
|
begin
|
49
|
-
|
63
|
+
kill(signal)
|
50
64
|
rescue Errno::ESRCH
|
51
65
|
return true
|
52
66
|
end
|
@@ -55,7 +69,13 @@ module ZK
|
|
55
69
|
end
|
56
70
|
end
|
57
71
|
|
58
|
-
|
72
|
+
unless @exit_watching_thread.join(3) == @exit_watching_thread
|
73
|
+
logger.warn { "exit watching thread didn't exit after 3 sec" }
|
74
|
+
end
|
75
|
+
|
76
|
+
@pid = @exit_watching_thread = nil
|
77
|
+
|
78
|
+
logger.debug { "@status: #{@status}" }
|
59
79
|
end
|
60
80
|
true
|
61
81
|
end
|
@@ -65,6 +85,11 @@ module ZK
|
|
65
85
|
@pid
|
66
86
|
end
|
67
87
|
|
88
|
+
def kill(signal)
|
89
|
+
raise "No pid yet!" unless @pid
|
90
|
+
Process.kill(signal, @pid)
|
91
|
+
end
|
92
|
+
|
68
93
|
# start the child, using the {#config}. we create the files necessary,
|
69
94
|
# fork the child, and wait 5s for the child to start responding to pings
|
70
95
|
#
|
@@ -99,7 +124,7 @@ module ZK
|
|
99
124
|
protected
|
100
125
|
def spawn_exit_watching_thread
|
101
126
|
@exit_watching_thread ||= Thread.new do
|
102
|
-
_, @
|
127
|
+
_, @status = Process.wait2(@pid)
|
103
128
|
@mutex.synchronize do
|
104
129
|
@exit_cond.broadcast
|
105
130
|
end
|
@@ -124,7 +149,9 @@ module ZK
|
|
124
149
|
@pid ||= (
|
125
150
|
args = command_args()
|
126
151
|
args << { :err => [:child, :out], :out => [stdio_redirect_path, 'w'] }
|
127
|
-
::Kernel.spawn({}, *command_args)
|
152
|
+
::Kernel.spawn({}, *command_args).tap do |pid|
|
153
|
+
logger.debug { "Spawned process #{pid}" }
|
154
|
+
end
|
128
155
|
)
|
129
156
|
end
|
130
157
|
|
data/lib/zk-server/version.rb
CHANGED
data/lib/zk-server.rb
CHANGED
@@ -9,6 +9,8 @@ require 'socket'
|
|
9
9
|
|
10
10
|
Bundler.require
|
11
11
|
|
12
|
+
require 'slop'
|
13
|
+
|
12
14
|
# require 'zk'
|
13
15
|
|
14
16
|
#ZK.logger = Logger.new($stderr).tap { |l| l.level = Logger::DEBUG }
|
@@ -130,6 +132,8 @@ require 'zk-server/logging'
|
|
130
132
|
require 'zk-server/config'
|
131
133
|
require 'zk-server/base'
|
132
134
|
require 'zk-server/sub_process'
|
135
|
+
require 'zk-server/cluster'
|
136
|
+
require 'zk-server/command'
|
133
137
|
|
134
138
|
if defined?(::JRUBY_VERSION)
|
135
139
|
require 'zk-server/java_embedded'
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,55 @@
|
|
1
|
+
module WaitWatchers
|
2
|
+
class TimeoutError < StandardError; end
|
3
|
+
|
4
|
+
# method to wait until block passed returns truthy (false will not work) or
|
5
|
+
# timeout (default is 2 seconds) is reached raises TiemoutError on timeout
|
6
|
+
#
|
7
|
+
# @returns the truthy value
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
#
|
11
|
+
# @a = nil
|
12
|
+
#
|
13
|
+
# th = Thread.new do
|
14
|
+
# sleep(1)
|
15
|
+
# @a = :fudge
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# wait_until(2) { @a }.should == :fudge
|
19
|
+
#
|
20
|
+
def wait_until(timeout=2)
|
21
|
+
if ENV['TRAVIS'] and timeout and timeout < 5
|
22
|
+
timeout = 5
|
23
|
+
end
|
24
|
+
|
25
|
+
time_to_stop = Time.now + timeout
|
26
|
+
while true
|
27
|
+
rval = yield
|
28
|
+
return rval if rval
|
29
|
+
raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
|
30
|
+
Thread.pass
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# inverse of wait_until
|
35
|
+
def wait_while(timeout=2)
|
36
|
+
if ENV['travis'] and timeout and timeout < 5
|
37
|
+
timeout = 5
|
38
|
+
end
|
39
|
+
|
40
|
+
time_to_stop = Time.now + timeout
|
41
|
+
while true
|
42
|
+
rval = yield
|
43
|
+
return rval unless rval
|
44
|
+
raise TimeoutError, "timeout of #{timeout}s exceeded" if Time.now > time_to_stop
|
45
|
+
Thread.pass
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def report_realtime(what)
|
50
|
+
return yield
|
51
|
+
t = Benchmark.realtime { yield }
|
52
|
+
$stderr.puts "#{what}: %0.3f" % [t.to_f]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ZK::Server::Cluster do
|
4
|
+
let(:tmpdir) { '/tmp/zookeeper' }
|
5
|
+
|
6
|
+
let(:num_members) { 5 }
|
7
|
+
|
8
|
+
subject do
|
9
|
+
described_class.new(num_members, :base_dir => tmpdir)
|
10
|
+
end
|
11
|
+
|
12
|
+
after do
|
13
|
+
subject.shutdown
|
14
|
+
# subject.clobber!
|
15
|
+
# FileUtils.rm_rf(tmpdir)
|
16
|
+
end
|
17
|
+
|
18
|
+
it %[should spawn a ZK server, ping, and then shutdown properly] do
|
19
|
+
pending "cannot run this under JRuby" if defined?(::JRUBY_VERSION)
|
20
|
+
|
21
|
+
subject.run
|
22
|
+
|
23
|
+
wait_until { subject.all_running? }
|
24
|
+
|
25
|
+
subject.should be_ping_all
|
26
|
+
subject.should be_running
|
27
|
+
|
28
|
+
subject.shutdown
|
29
|
+
|
30
|
+
subject.should_not be_ping_all
|
31
|
+
subject.should_not be_running
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
data/zk-server.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
|
|
11
11
|
s.add_runtime_dependency 'bundler', '~> 1.1.3'
|
12
12
|
s.add_runtime_dependency 'slyphon-log4j', '= 1.2.15'
|
13
13
|
s.add_runtime_dependency 'slyphon-zookeeper_jar', '~> 3.3.5'
|
14
|
+
s.add_runtime_dependency 'slop', '~> 3.2.0'
|
14
15
|
|
15
16
|
s.files = `git ls-files`.split($\)
|
16
17
|
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: zk-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
|
-
- 0
|
9
8
|
- 1
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 1.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Jonathan D. Simms
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-05-
|
18
|
+
date: 2012-05-16 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: bundler
|
@@ -65,11 +65,27 @@ dependencies:
|
|
65
65
|
version: 3.3.5
|
66
66
|
type: :runtime
|
67
67
|
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: slop
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ~>
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 15
|
77
|
+
segments:
|
78
|
+
- 3
|
79
|
+
- 2
|
80
|
+
- 0
|
81
|
+
version: 3.2.0
|
82
|
+
type: :runtime
|
83
|
+
version_requirements: *id004
|
68
84
|
description: runs a standalone zookeeper server
|
69
85
|
email:
|
70
86
|
- slyphon@gmail.com
|
71
|
-
executables:
|
72
|
-
|
87
|
+
executables:
|
88
|
+
- zk-server
|
73
89
|
extensions: []
|
74
90
|
|
75
91
|
extra_rdoc_files: []
|
@@ -83,8 +99,11 @@ files:
|
|
83
99
|
- MIT_LICENSE
|
84
100
|
- README.md
|
85
101
|
- Rakefile
|
102
|
+
- bin/zk-server
|
86
103
|
- lib/zk-server.rb
|
87
104
|
- lib/zk-server/base.rb
|
105
|
+
- lib/zk-server/cluster.rb
|
106
|
+
- lib/zk-server/command.rb
|
88
107
|
- lib/zk-server/config.rb
|
89
108
|
- lib/zk-server/java_embedded.rb
|
90
109
|
- lib/zk-server/log4j.properties
|
@@ -92,6 +111,8 @@ files:
|
|
92
111
|
- lib/zk-server/sub_process.rb
|
93
112
|
- lib/zk-server/version.rb
|
94
113
|
- spec/spec_helper.rb
|
114
|
+
- spec/support/wait_watchers.rb
|
115
|
+
- spec/zk-server/cluster_spec.rb
|
95
116
|
- spec/zk-server/java_embedded_spec.rb
|
96
117
|
- spec/zk-server/process_spec.rb
|
97
118
|
- zk-server.gemspec
|
@@ -124,11 +145,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
124
145
|
requirements: []
|
125
146
|
|
126
147
|
rubyforge_project:
|
127
|
-
rubygems_version: 1.8.
|
148
|
+
rubygems_version: 1.8.24
|
128
149
|
signing_key:
|
129
150
|
specification_version: 3
|
130
151
|
summary: runs a standalone zookeeper server
|
131
152
|
test_files:
|
132
153
|
- spec/spec_helper.rb
|
154
|
+
- spec/support/wait_watchers.rb
|
155
|
+
- spec/zk-server/cluster_spec.rb
|
133
156
|
- spec/zk-server/java_embedded_spec.rb
|
134
157
|
- spec/zk-server/process_spec.rb
|