beetle 0.1 → 0.2.1

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.
Files changed (48) hide show
  1. data/README.rdoc +18 -8
  2. data/beetle.gemspec +37 -121
  3. data/bin/beetle +9 -0
  4. data/examples/README.rdoc +0 -2
  5. data/examples/rpc.rb +3 -2
  6. data/ext/mkrf_conf.rb +19 -0
  7. data/lib/beetle.rb +2 -2
  8. data/lib/beetle/base.rb +1 -8
  9. data/lib/beetle/client.rb +16 -14
  10. data/lib/beetle/commands.rb +30 -0
  11. data/lib/beetle/commands/configuration_client.rb +73 -0
  12. data/lib/beetle/commands/configuration_server.rb +85 -0
  13. data/lib/beetle/configuration.rb +70 -7
  14. data/lib/beetle/deduplication_store.rb +50 -38
  15. data/lib/beetle/handler.rb +2 -5
  16. data/lib/beetle/logging.rb +7 -0
  17. data/lib/beetle/message.rb +11 -13
  18. data/lib/beetle/publisher.rb +12 -4
  19. data/lib/beetle/r_c.rb +2 -1
  20. data/lib/beetle/redis_configuration_client.rb +136 -0
  21. data/lib/beetle/redis_configuration_server.rb +301 -0
  22. data/lib/beetle/redis_ext.rb +79 -0
  23. data/lib/beetle/redis_master_file.rb +35 -0
  24. data/lib/beetle/redis_server_info.rb +65 -0
  25. data/lib/beetle/subscriber.rb +4 -1
  26. data/test/beetle/configuration_test.rb +14 -2
  27. data/test/beetle/deduplication_store_test.rb +61 -43
  28. data/test/beetle/message_test.rb +28 -4
  29. data/test/beetle/publisher_test.rb +17 -3
  30. data/test/beetle/redis_configuration_client_test.rb +97 -0
  31. data/test/beetle/redis_configuration_server_test.rb +278 -0
  32. data/test/beetle/redis_ext_test.rb +71 -0
  33. data/test/beetle/redis_master_file_test.rb +39 -0
  34. data/test/test_helper.rb +13 -1
  35. metadata +162 -69
  36. data/.gitignore +0 -5
  37. data/MIT-LICENSE +0 -20
  38. data/Rakefile +0 -114
  39. data/TODO +0 -7
  40. data/etc/redis-master.conf +0 -189
  41. data/etc/redis-slave.conf +0 -189
  42. data/examples/redis_failover.rb +0 -65
  43. data/script/start_rabbit +0 -29
  44. data/snafu.rb +0 -55
  45. data/test/beetle.yml +0 -81
  46. data/test/beetle/bla.rb +0 -0
  47. data/tmp/master/.gitignore +0 -2
  48. data/tmp/slave/.gitignore +0 -3
data/README.rdoc CHANGED
@@ -18,9 +18,9 @@ More information can be found on the {project website}[http://xing.github.com/be
18
18
  === Configuration
19
19
  # configure machines
20
20
 
21
- Beetle.config do |c|
21
+ Beetle.config do |config|
22
22
  config.servers = "broker1:5672, broker2:5672"
23
- config.redis_hosts = "redis1:6379, redis2:6379"
23
+ config.redis_server = "redis1:6379"
24
24
  end
25
25
 
26
26
  # instantiate a beetle client
@@ -41,7 +41,19 @@ More information can be found on the {project website}[http://xing.github.com/be
41
41
  === Subscribing
42
42
  b.listen
43
43
 
44
- :include: examples/README.rdoc
44
+ === Examples
45
+
46
+ Beetle ships with a number of {example scripts}[http://github.com/xing/beetle/tree/master/examples/].
47
+
48
+ The top level Rakefile comes with targets to start several RabbitMQ and redis instances
49
+ locally. Make sure the corresponding binaries are in your search path. Open four new shell
50
+ windows and execute the following commands:
51
+
52
+ rake rabbit:start1
53
+ rake rabbit:start2
54
+ rake redis:start1
55
+ rake redis:start2
56
+
45
57
 
46
58
  == Prerequisites
47
59
 
@@ -68,8 +80,9 @@ For development, you'll need
68
80
  == Authors
69
81
 
70
82
  {Stefan Kaes}[http://github.com/skaes],
71
- {Pascal Friederich}[http://github.com/paukul] and
72
- {Ali Jelveh}[http://github.com/dudemeister].
83
+ {Pascal Friederich}[http://github.com/paukul],
84
+ {Ali Jelveh}[http://github.com/dudemeister] and
85
+ {Sebastian Roebke}[http://github.com/boosty].
73
86
 
74
87
  You cand find out more about our work on our {dev blog}[http://devblog.xing.com].
75
88
 
@@ -77,6 +90,3 @@ Copyright (c) 2010 {XING AG}[http://www.xing.com/]
77
90
 
78
91
  Released under the MIT license. For full details see MIT-LICENSE included in this
79
92
  distribution.
80
-
81
-
82
-
data/beetle.gemspec CHANGED
@@ -1,127 +1,43 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
- # -*- encoding: utf-8 -*-
5
-
6
1
  Gem::Specification.new do |s|
7
- s.name = %q{beetle}
8
- s.version = "0.1"
2
+ s.name = "beetle"
3
+ s.version = "0.2.1"
4
+
5
+ s.required_rubygems_version = ">= 1.3.1"
6
+ s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh", "Sebastian Roebke"]
7
+ s.date = Time.now.strftime('%Y-%m-%d')
8
+ s.default_executable = "beetle"
9
+ s.description = "A highly available, reliable messaging infrastructure"
10
+ s.summary = "High Availability AMQP Messaging with Redundant Queues"
11
+ s.email = "developers@xing.com"
12
+ s.executables = ["beetle"]
13
+ s.extra_rdoc_files = ["README.rdoc"]
14
+ s.files = Dir['{examples,ext,lib}/**/*.rb'] + %w(beetle.gemspec examples/README.rdoc)
15
+ s.extensions = 'ext/mkrf_conf.rb'
16
+ s.homepage = "http://xing.github.com/beetle/"
17
+ s.rdoc_options = ["--charset=UTF-8"]
18
+ s.require_paths = ["lib"]
19
+ s.rubygems_version = "1.3.7"
20
+ s.test_files = Dir['test/**/*.rb']
21
+
22
+ s.post_install_message = <<-INFO
23
+ *********************************************************************************************
9
24
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Stefan Kaes", "Pascal Friederich", "Ali Jelveh"]
12
- s.date = %q{2010-04-14}
13
- s.description = %q{A highly available, reliable messaging infrastructure}
14
- s.email = %q{developers@xing.com}
15
- s.extra_rdoc_files = [
16
- "README.rdoc",
17
- "TODO"
18
- ]
19
- s.files = [
20
- ".gitignore",
21
- "MIT-LICENSE",
22
- "README.rdoc",
23
- "Rakefile",
24
- "TODO",
25
- "beetle.gemspec",
26
- "doc/redundant_queues.graffle",
27
- "etc/redis-master.conf",
28
- "etc/redis-slave.conf",
29
- "examples/README.rdoc",
30
- "examples/attempts.rb",
31
- "examples/handler_class.rb",
32
- "examples/handling_exceptions.rb",
33
- "examples/multiple_exchanges.rb",
34
- "examples/multiple_queues.rb",
35
- "examples/redis_failover.rb",
36
- "examples/redundant.rb",
37
- "examples/rpc.rb",
38
- "examples/simple.rb",
39
- "lib/beetle.rb",
40
- "lib/beetle/base.rb",
41
- "lib/beetle/client.rb",
42
- "lib/beetle/configuration.rb",
43
- "lib/beetle/deduplication_store.rb",
44
- "lib/beetle/handler.rb",
45
- "lib/beetle/message.rb",
46
- "lib/beetle/publisher.rb",
47
- "lib/beetle/r_c.rb",
48
- "lib/beetle/subscriber.rb",
49
- "script/start_rabbit",
50
- "snafu.rb",
51
- "test/beetle.yml",
52
- "test/beetle/base_test.rb",
53
- "test/beetle/bla.rb",
54
- "test/beetle/client_test.rb",
55
- "test/beetle/configuration_test.rb",
56
- "test/beetle/deduplication_store_test.rb",
57
- "test/beetle/handler_test.rb",
58
- "test/beetle/message_test.rb",
59
- "test/beetle/publisher_test.rb",
60
- "test/beetle/r_c_test.rb",
61
- "test/beetle/subscriber_test.rb",
62
- "test/beetle_test.rb",
63
- "test/test_helper.rb",
64
- "tmp/master/.gitignore",
65
- "tmp/slave/.gitignore"
66
- ]
67
- s.homepage = %q{http://xing.github.com/beetle/}
68
- s.rdoc_options = ["--charset=UTF-8"]
69
- s.require_paths = ["lib"]
70
- s.rubygems_version = %q{1.3.5}
71
- s.summary = %q{High Availability AMQP Messaging with Redundant Queues}
72
- s.test_files = [
73
- "test/beetle/base_test.rb",
74
- "test/beetle/bla.rb",
75
- "test/beetle/client_test.rb",
76
- "test/beetle/configuration_test.rb",
77
- "test/beetle/deduplication_store_test.rb",
78
- "test/beetle/handler_test.rb",
79
- "test/beetle/message_test.rb",
80
- "test/beetle/publisher_test.rb",
81
- "test/beetle/r_c_test.rb",
82
- "test/beetle/subscriber_test.rb",
83
- "test/beetle_test.rb",
84
- "test/test_helper.rb",
85
- "examples/attempts.rb",
86
- "examples/handler_class.rb",
87
- "examples/handling_exceptions.rb",
88
- "examples/multiple_exchanges.rb",
89
- "examples/multiple_queues.rb",
90
- "examples/redis_failover.rb",
91
- "examples/redundant.rb",
92
- "examples/rpc.rb",
93
- "examples/simple.rb"
94
- ]
25
+ If you're running a ruby version < 1.9 we silently installed the SystemTimer gem for you.
26
+ See: http://ph7spot.com/musings/system-timer
95
27
 
96
- if s.respond_to? :specification_version then
97
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
98
- s.specification_version = 3
28
+ *********************************************************************************************
29
+ INFO
99
30
 
100
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
101
- s.add_runtime_dependency(%q<uuid4r>, [">= 0.1.1"])
102
- s.add_runtime_dependency(%q<bunny>, [">= 0.6.0"])
103
- s.add_runtime_dependency(%q<redis>, [">= 0.1.2"])
104
- s.add_runtime_dependency(%q<amqp>, [">= 0.6.7"])
105
- s.add_runtime_dependency(%q<activesupport>, [">= 2.3.4"])
106
- s.add_development_dependency(%q<mocha>, [">= 0"])
107
- s.add_development_dependency(%q<rcov>, [">= 0"])
108
- else
109
- s.add_dependency(%q<uuid4r>, [">= 0.1.1"])
110
- s.add_dependency(%q<bunny>, [">= 0.6.0"])
111
- s.add_dependency(%q<redis>, [">= 0.1.2"])
112
- s.add_dependency(%q<amqp>, [">= 0.6.7"])
113
- s.add_dependency(%q<activesupport>, [">= 2.3.4"])
114
- s.add_dependency(%q<mocha>, [">= 0"])
115
- s.add_dependency(%q<rcov>, [">= 0"])
116
- end
117
- else
118
- s.add_dependency(%q<uuid4r>, [">= 0.1.1"])
119
- s.add_dependency(%q<bunny>, [">= 0.6.0"])
120
- s.add_dependency(%q<redis>, [">= 0.1.2"])
121
- s.add_dependency(%q<amqp>, [">= 0.6.7"])
122
- s.add_dependency(%q<activesupport>, [">= 2.3.4"])
123
- s.add_dependency(%q<mocha>, [">= 0"])
124
- s.add_dependency(%q<rcov>, [">= 0"])
125
- end
31
+ s.specification_version = 3
32
+ s.add_runtime_dependency("uuid4r", [">= 0.1.1"])
33
+ s.add_runtime_dependency("bunny", [">= 0.6.0"])
34
+ s.add_runtime_dependency("redis", [">= 2.0.3"])
35
+ s.add_runtime_dependency("amqp", [">= 0.6.7"])
36
+ s.add_runtime_dependency("activesupport", [">= 2.3.4"])
37
+ s.add_runtime_dependency("daemons", [">= 1.0.10"])
38
+ s.add_development_dependency("mocha", [">= 0"])
39
+ s.add_development_dependency("rcov", [">= 0"])
40
+ s.add_development_dependency("cucumber", [">= 0.7.2"])
41
+ s.add_development_dependency("daemon_controller", [">= 0"])
126
42
  end
127
43
 
data/bin/beetle ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ begin
4
+ require 'beetle/commands'
5
+ rescue LoadError
6
+ beetle_path = File.expand_path("../../lib", __FILE__)
7
+ $:.unshift(beetle_path)
8
+ require 'beetle/commands'
9
+ end
data/examples/README.rdoc CHANGED
@@ -10,5 +10,3 @@ windows and execute the following commands:
10
10
  rake rabbit:start2
11
11
  rake redis:start1
12
12
  rake redis:start2
13
-
14
- After running the redis_failover.rb script you will need to restart both redis servers.
data/examples/rpc.rb CHANGED
@@ -4,9 +4,10 @@ require File.expand_path(File.dirname(__FILE__)+"/../lib/beetle")
4
4
 
5
5
  # suppress debug messages
6
6
  Beetle.config.logger.level = Logger::DEBUG
7
-
7
+ Beetle.config.servers = "localhost:5672, localhost:5673"
8
8
  # instantiate a client
9
- client = Beetle::Client.new(:servers => "localhost:5672, localhost:5673")
9
+
10
+ client = Beetle::Client.new
10
11
 
11
12
  # register a durable queue named 'test'
12
13
  # this implicitly registers a durable topic exchange called 'test'
data/ext/mkrf_conf.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'rubygems/command.rb'
3
+ require 'rubygems/dependency_installer.rb'
4
+ begin
5
+ Gem::Command.build_args = ARGV
6
+ rescue NoMethodError
7
+ end
8
+ inst = Gem::DependencyInstaller.new
9
+ begin
10
+ if RUBY_VERSION < "1.9"
11
+ inst.install "SystemTimer", ">= 1.2"
12
+ end
13
+ rescue
14
+ exit(1)
15
+ end
16
+
17
+ f = File.open(File.join(File.dirname(__FILE__), "Rakefile"), "w") # create dummy rakefile to indicate success
18
+ f.write("task :default\n")
19
+ f.close
data/lib/beetle.rb CHANGED
@@ -17,8 +17,6 @@ module Beetle
17
17
  class UnknownQueue < Error; end
18
18
  # raised when no redis master server can be found
19
19
  class NoRedisMaster < Error; end
20
- # raised when two redis master servers are found
21
- class TwoRedisMasters < Error; end
22
20
 
23
21
  # AMQP options for exchange creation
24
22
  EXCHANGE_CREATION_KEYS = [:auto_delete, :durable, :internal, :nowait, :passive]
@@ -37,6 +35,8 @@ module Beetle
37
35
  autoload File.basename(libfile)[/^(.*)\.rb$/, 1].classify, libfile
38
36
  end
39
37
 
38
+ require "#{lib_dir}/redis_ext"
39
+
40
40
  # returns the default configuration object and yields it if a block is given
41
41
  def self.config
42
42
  #:yields: config
data/lib/beetle/base.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Beetle
2
2
  # Abstract base class shared by Publisher and Subscriber
3
3
  class Base
4
+ include Logging
4
5
 
5
6
  attr_accessor :options, :servers, :server #:nodoc:
6
7
 
@@ -15,14 +16,6 @@ module Beetle
15
16
 
16
17
  private
17
18
 
18
- def logger
19
- self.class.logger
20
- end
21
-
22
- def self.logger
23
- Beetle.config.logger
24
- end
25
-
26
19
  def error(text)
27
20
  logger.error text
28
21
  raise Error.new(text)
data/lib/beetle/client.rb CHANGED
@@ -19,6 +19,8 @@ module Beetle
19
19
  # order, so that no message is lost if message producers are accidentally started before
20
20
  # the corresponding consumers.
21
21
  class Client
22
+ include Logging
23
+
22
24
  # the AMQP servers available for publishing
23
25
  attr_reader :servers
24
26
 
@@ -37,14 +39,18 @@ module Beetle
37
39
  # the deduplication store to use for this client
38
40
  attr_reader :deduplication_store
39
41
 
42
+ # accessor for the beetle configuration
43
+ attr_reader :config
44
+
40
45
  # create a fresh Client instance from a given configuration object
41
46
  def initialize(config = Beetle.config)
47
+ @config = config
42
48
  @servers = config.servers.split(/ *, */)
43
49
  @exchanges = {}
44
50
  @queues = {}
45
51
  @messages = {}
46
52
  @bindings = {}
47
- @deduplication_store = DeduplicationStore.new(config.redis_hosts, config.redis_db)
53
+ @deduplication_store = DeduplicationStore.new(config)
48
54
  end
49
55
 
50
56
  # register an exchange with the given _name_ and a set of _options_:
@@ -69,8 +75,8 @@ module Beetle
69
75
  def register_queue(name, options={})
70
76
  name = name.to_s
71
77
  raise ConfigurationError.new("queue #{name} already configured") if queues.include?(name)
72
- opts = {:exchange => name, :key => name}.merge!(options.symbolize_keys)
73
- opts.merge! :durable => true, :passive => false, :exclusive => false, :auto_delete => false, :amqp_name => name
78
+ opts = {:exchange => name, :key => name, :auto_delete => false, :amqp_name => name}.merge!(options.symbolize_keys)
79
+ opts.merge! :durable => true, :passive => false, :exclusive => false
74
80
  exchange = opts.delete(:exchange).to_s
75
81
  key = opts.delete(:key)
76
82
  queues[name] = opts
@@ -139,10 +145,10 @@ module Beetle
139
145
 
140
146
  # this is a convenience method to configure exchanges, queues, messages and handlers
141
147
  # with a common set of options. allows one to call all register methods without the
142
- # register_ prefix.
148
+ # register_ prefix. returns self.
143
149
  #
144
150
  # Example:
145
- # client.configure :exchange => :foobar do |config|
151
+ # client = Beetle.client.new.configure :exchange => :foobar do |config|
146
152
  # config.queue :q1, :key => "foo"
147
153
  # config.queue :q2, :key => "bar"
148
154
  # config.message :foo
@@ -152,6 +158,7 @@ module Beetle
152
158
  # end
153
159
  def configure(options={}) #:yields: config
154
160
  yield Configurator.new(self, options)
161
+ self
155
162
  end
156
163
 
157
164
  # publishes a message. the given options hash is merged with options given on message registration.
@@ -195,10 +202,10 @@ module Beetle
195
202
  publisher.stop
196
203
  end
197
204
 
198
- # traces all messages received on all queues. useful for debugging message flow.
199
- def trace(&block)
205
+ # traces messages without consuming them. useful for debugging message flow.
206
+ def trace(messages=self.messages.keys, &block)
200
207
  queues.each do |name, opts|
201
- opts.merge! :durable => false, :auto_delete => true, :amqp_name => queue_name_for_tracing(name)
208
+ opts.merge! :durable => false, :auto_delete => true, :amqp_name => queue_name_for_tracing(opts[:amqp_name])
202
209
  end
203
210
  register_handler(queues.keys) do |msg|
204
211
  puts "-----===== new message =====-----"
@@ -207,7 +214,7 @@ module Beetle
207
214
  puts "MSGID: #{msg.msg_id}"
208
215
  puts "DATA: #{msg.data}"
209
216
  end
210
- subscriber.listen(messages.keys, &block)
217
+ listen(messages, &block)
211
218
  end
212
219
 
213
220
  # evaluate the ruby files matching the given +glob+ pattern in the context of the client instance.
@@ -218,11 +225,6 @@ module Beetle
218
225
  end
219
226
  end
220
227
 
221
- # returns the configured Logger instance
222
- def logger
223
- @logger ||= Beetle.config.logger
224
- end
225
-
226
228
  private
227
229
 
228
230
  class Configurator #:nodoc:all
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+
4
+ module Beetle
5
+ module Commands
6
+ # invokes given command by instantiating an appropriate command class
7
+ def self.execute(command)
8
+ if commands.include? command
9
+ require File.expand_path("../commands/#{command}", __FILE__)
10
+ "Beetle::Commands::#{command.classify}".constantize.execute
11
+ else
12
+ # me no likez no frikin heredocs
13
+ puts "\nCommand #{command} not known\n" if command
14
+ puts "Available commands are:"
15
+ puts
16
+ commands.each {|c| puts "\t #{c}"}
17
+ puts
18
+ exit 1
19
+ end
20
+ end
21
+
22
+ private
23
+ def self.commands
24
+ commands_dir = File.expand_path('../commands', __FILE__)
25
+ Dir[commands_dir + '/*.rb'].map {|f| File.basename(f)[0..-4]}
26
+ end
27
+ end
28
+ end
29
+
30
+ Beetle::Commands.execute(ARGV.shift)
@@ -0,0 +1,73 @@
1
+ require 'optparse'
2
+ require 'daemons'
3
+ require 'beetle'
4
+
5
+ module Beetle
6
+ module Commands
7
+ # Command to start a RedisConfigurationClient daemon.
8
+ #
9
+ # Usage: beetle configuration_client [options] -- [client options]
10
+ #
11
+ # client options:
12
+ # --redis-master-file FILE Write redis master server string to FILE
13
+ # --id, --client-id ID Set unique client id (default is minastirith.local)
14
+ # --amqp-servers LIST AMQP server list (e.g. 192.168.0.1:5672,192.168.0.2:5672)
15
+ # --config-file PATH Path to an external yaml config file
16
+ # --pid-dir DIR Write pid and log to DIR
17
+ # -v, --verbose Set log level to DEBUG
18
+ # -h, --help Show this message
19
+ #
20
+ class ConfigurationClient
21
+ # parses command line options and starts Beetle::RedisConfigurationClient as a daemon
22
+ def self.execute
23
+ command, controller_options, app_options = Daemons::Controller.split_argv(ARGV)
24
+
25
+ opts = OptionParser.new
26
+ opts.banner = "Usage: beetle configuration_client #{command} [options] -- [client options]"
27
+ opts.separator ""
28
+ opts.separator "client options:"
29
+
30
+ opts.on("--redis-master-file FILE", String, "Write redis master server string to FILE") do |val|
31
+ Beetle.config.redis_server = val
32
+ end
33
+
34
+ client_id = nil
35
+ opts.on("--id ID", "--client-id ID", String, "Set unique client id (default is #{RedisConfigurationClient.new.id})") do |val|
36
+ client_id = val
37
+ end
38
+
39
+ opts.on("--amqp-servers LIST", String, "AMQP server list (e.g. 192.168.0.1:5672,192.168.0.2:5672)") do |val|
40
+ Beetle.config.servers = val
41
+ end
42
+
43
+ opts.on("--config-file PATH", String, "Path to an external yaml config file") do |val|
44
+ Beetle.config.config_file = val
45
+ end
46
+
47
+ dir_mode = nil
48
+ dir = nil
49
+ opts.on("--pid-dir DIR", String, "Write pid and log to DIR") do |val|
50
+ dir_mode = :normal
51
+ dir = val
52
+ end
53
+
54
+ opts.on("-v", "--verbose", "Set log level to DEBUG") do |val|
55
+ Beetle.config.logger.level = Logger::DEBUG
56
+ end
57
+
58
+ opts.on_tail("-h", "--help", "Show this message") do
59
+ puts opts
60
+ exit
61
+ end
62
+
63
+ opts.parse!(app_options)
64
+
65
+ Daemons.run_proc("redis_configuration_client", :multiple => true, :log_output => true, :dir_mode => dir_mode, :dir => dir) do
66
+ client = Beetle::RedisConfigurationClient.new
67
+ client.id = client_id if client_id
68
+ client.start
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end