mbus 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/README.mediawiki +169 -0
  3. data/Rakefile +24 -0
  4. data/bin/console +11 -0
  5. data/bin/messagebus_swarm +77 -0
  6. data/lib/messagebus.rb +62 -0
  7. data/lib/messagebus/client.rb +166 -0
  8. data/lib/messagebus/cluster_map.rb +161 -0
  9. data/lib/messagebus/connection.rb +118 -0
  10. data/lib/messagebus/consumer.rb +447 -0
  11. data/lib/messagebus/custom_errors.rb +37 -0
  12. data/lib/messagebus/dottable_hash.rb +113 -0
  13. data/lib/messagebus/error_status.rb +42 -0
  14. data/lib/messagebus/logger.rb +45 -0
  15. data/lib/messagebus/message.rb +168 -0
  16. data/lib/messagebus/messagebus_types.rb +107 -0
  17. data/lib/messagebus/producer.rb +187 -0
  18. data/lib/messagebus/swarm.rb +49 -0
  19. data/lib/messagebus/swarm/controller.rb +296 -0
  20. data/lib/messagebus/swarm/drone.rb +195 -0
  21. data/lib/messagebus/swarm/drone/logging_worker.rb +53 -0
  22. data/lib/messagebus/validations.rb +68 -0
  23. data/lib/messagebus/version.rb +36 -0
  24. data/messagebus.gemspec +29 -0
  25. data/spec/messagebus/client_spec.rb +157 -0
  26. data/spec/messagebus/cluster_map_spec.rb +178 -0
  27. data/spec/messagebus/consumer_spec.rb +338 -0
  28. data/spec/messagebus/dottable_hash_spec.rb +137 -0
  29. data/spec/messagebus/message_spec.rb +93 -0
  30. data/spec/messagebus/producer_spec.rb +147 -0
  31. data/spec/messagebus/swarm/controller_spec.rb +73 -0
  32. data/spec/messagebus/validations_spec.rb +71 -0
  33. data/spec/spec_helper.rb +10 -0
  34. data/vendor/gems/stomp.rb +23 -0
  35. data/vendor/gems/stomp/client.rb +360 -0
  36. data/vendor/gems/stomp/connection.rb +583 -0
  37. data/vendor/gems/stomp/errors.rb +39 -0
  38. data/vendor/gems/stomp/ext/hash.rb +24 -0
  39. data/vendor/gems/stomp/message.rb +68 -0
  40. metadata +138 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 689df9b9f0b86245507faeda6239d9ca24ff2cf1
4
+ data.tar.gz: 6e95ce8920e6f92951dc28dd967e7049d16ad160
5
+ SHA512:
6
+ metadata.gz: 9d8ada5f134ebf4c992d831ac28ad3cc786c40172c22e1279668afbf774858daeb8f7f9b03dd55ad549cff0ab91b36972c74630e16535f8735ea97b6ed80d19c
7
+ data.tar.gz: cdfe47c4cfbaa25d92a072cd67776b2aa0ff79f145223176c71764154a4845f3be32d0ff2fed93d4c1f357d2d7b0d4dcbcddb2fb37fc6c7ab39bc2c86c27e4a6
@@ -0,0 +1,169 @@
1
+ = Messagebus =
2
+
3
+ A gem for publishing and consuming hornetq messagebus messages.
4
+
5
+ == Installation ==
6
+
7
+ $ gem install mbus
8
+
9
+ == Dependencies ==
10
+
11
+ This Gem depends upon the following:
12
+
13
+ === Runtime ===
14
+
15
+ * messagebus (vendored an old version for now)
16
+ * thrift
17
+ * json
18
+
19
+ === Development & Tests ===
20
+
21
+ * bundle install
22
+ * Execute rspec spec
23
+
24
+ == Configuration ==
25
+
26
+ The messagebus is configured via a single yaml file. The configuration fields are defined below and an
27
+ example of a full configuration also provided.
28
+
29
+ * enable_auto_init_connections: turn on producer connections once initialized
30
+ * log_file: path for log
31
+ * log_level: logger level minimum
32
+ * logger: pass a logger directly to the messagebus client
33
+ * worker_log_file: path for worker log
34
+ * cluster_defaults
35
+ ** user: hornetq user
36
+ ** passwd: hornetq password
37
+ ** receipt_wait_timeout_ms: TODO
38
+ ** conn_lifetime_sec: TODO
39
+ * clusters: array of haproxy clusters
40
+ ** type: producer-cluster or consumer-cluster
41
+ ** address: host and port of haproxy delimited by colon
42
+ ** destinations: array of queue and topic destinations
43
+ * swarm_config
44
+ ** fork: true or false -- whether to use forks or threads when booting drones
45
+ * workers
46
+ ** destination: the destination name
47
+ ** worker: the worker class to process messages with
48
+ ** subscription_id: swarm_drone_run_spec_id
49
+ ** ack_on_error: whether to send an ack for a job that failed
50
+ ** drones: number of drones to boot up for parallel processing
51
+
52
+ '''Example config'''
53
+ enable_auto_init_connections: true
54
+ log_file: log/messagebus-client.log
55
+ worker_log_file: log/messagebus-consumer.log
56
+
57
+ :swarm_config:
58
+ # :fork: true
59
+
60
+ :workers:
61
+ -
62
+ :destination: jms.topic.testTopic2
63
+ :worker: SimpleDroneRunSpecWorker
64
+ :subscription_id: swarm_drone_run_spec_id
65
+
66
+ :ack_on_error: false
67
+ :drones: 1
68
+
69
+ :cluster_defaults:
70
+ :user: guest
71
+ :passwd: guest
72
+ :receipt_wait_timeout_ms: 1500
73
+ :conn_lifetime_sec: 300
74
+ :enable_dynamic_serverlist_fetch: true
75
+
76
+ clusters:
77
+ -
78
+ name: orders-messagebus-cluster
79
+ producer_address: localhost:61613
80
+ consumer_address: localhost:61613
81
+ destinations:
82
+ - jms.topic.testTopic2
83
+
84
+ == Getting Started ==
85
+
86
+ You will find an example listed of loading a configuration, instantiation of a client, and
87
+ finally invocation of the publish method. There are three different message types that can
88
+ be published on the bus, string, binary, or json. An example of each are listed with concrete
89
+ code to use.
90
+
91
+ In its current state, the messagebus gem should be vendored into your application. This will
92
+ change once the internal rubygems server is integrated into the company. For now, unpack the
93
+ gem and include "thrift" 0.9.0 into your Gemfile next to the messagebus declaration.
94
+
95
+ === Edit Gemfile ===
96
+
97
+ gem "thrift", "0.9.0"
98
+ gem "messagebus", "x.x.x", :path => "vendor/gems/messagebus-x.x.x", :require => "messagebus"
99
+
100
+
101
+ === Bundle your gems ===
102
+
103
+ $ bundle
104
+
105
+ == Producing Messages ==
106
+ === Run interactive ruby ===
107
+
108
+ $ irb
109
+
110
+ ruby>
111
+ require "messagebus" # Notice this will happen by default in the bundler
112
+
113
+ ruby>
114
+ binary_to_publish = "\xfe\x3e\x5e"
115
+ config = YAML.load_file("./config/messagebus.yml")
116
+ client = Messagebus::Client.new(config.merge(:logger => Logger.new("a/directory/to/my/file.log")))
117
+ client.start
118
+ client.publish "jms.queue.testQueue1", binary_to_publish
119
+
120
+ ruby>
121
+ string_to_publish = "the quick brown fox jumped over the lazy dogs back"
122
+ config = YAML.load_file("./config/messagebus.yml")
123
+ client = Messagebus::Client.new(config.merge(:logger => Logger.new("a/directory/to/my/file.log")))
124
+ client.publish "jms.queue.testQueue1", string_to_publish
125
+
126
+ ruby>
127
+ hash_to_publish = { :ruby => { :loves => :the_bus } }
128
+ config = YAML.load_file("./config/messagebus.yml")
129
+ client = Messagebus::Client.new(config.merge(:logger => Logger.new("a/directory/to/my/file.log")))
130
+ client.publish "jms.queue.testQueue1", hash_to_publish
131
+
132
+ ruby>
133
+ object_to_publish = DomainObjectThatRespondsToToJson # object_to_publish.to_json will happen automatically
134
+ config = YAML.load_file("./config/messagebus.yml")
135
+ client = Messagebus::Client.new(config.merge(:logger => Logger.new("a/directory/to/my/file.log")))
136
+ client.publish "jms.queue.testQueue1", object_to_publish
137
+
138
+ === Watch Tail Processing Log ===
139
+
140
+ The tail will start displaying new log messages.
141
+
142
+ $ tail -f log/messagebus-client.log
143
+
144
+
145
+ == Consuming messages ==
146
+ You have 2 options. Option 1 is write your own consumer class. See [[https://github.com/groupon/Message-Bus/blob/master/mbus-ruby/examples/consumer_topic_example.rb|consumer_topic_example.rb]]
147
+ for how to write your own consumer.
148
+
149
+ Your other option is to use the Message Swarm code. Try examples/swarm_example.sh. See [[https://github.com/groupon/Message-Bus/wiki/Swarm-Consumer|Swarm Guide]] for more on that.
150
+
151
+ == More detail needed? ==
152
+
153
+ Contact us via email! dpe@groupon.com
154
+
155
+ If something is not behaving intuitively, it is a bug and should be reported.
156
+ Report it here: https://github.groupon.com/data-infrastructure/ruby-messagebus
157
+
158
+ == Note on Patches/Pull Requests ==
159
+
160
+ * Fork the project.
161
+ * Make your feature addition or bug fix.
162
+ * Commit, do not mess with rakefile, version, or history.
163
+ * Send me a pull request. Bonus points for topic branches.
164
+
165
+ == Copyright ==
166
+
167
+ Copyright (c) 2011 Groupon. Released under the MIT License.
168
+
169
+ See LICENSE for details.
@@ -0,0 +1,24 @@
1
+ require "rubygems"
2
+ require "rubygems/package_task"
3
+ require "rspec/core/rake_task"
4
+
5
+ SPEC = begin
6
+ file = File.expand_path(File.dirname(__FILE__) + '/messagebus.gemspec')
7
+ eval(File.read(file), binding, file)
8
+ end
9
+
10
+ Gem::PackageTask.new(SPEC) {}
11
+
12
+ desc "Run all specs"
13
+ RSpec::Core::RakeTask.new(:spec) do |t|
14
+ t.rspec_opts = %w[--color --format=progress]
15
+ t.pattern = 'spec/**/*_spec.rb'
16
+ end
17
+
18
+ desc "Run code coverage"
19
+ RSpec::Core::RakeTask.new(:coverage) do |t|
20
+ t.rspec_opts = %w[--color --format=progress]
21
+ t.rcov = true
22
+ t.rcov_opts = %w[--exclude='gems,spec']
23
+ t.pattern = 'spec/**/*_spec.rb'
24
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ base = File.dirname(__FILE__)
4
+ $:.unshift File.expand_path(File.join(base, "..", "lib"))
5
+
6
+ require "irb"
7
+ require "yaml"
8
+ require "messagebus"
9
+
10
+ IRB.start
11
+
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+
5
+ require 'messagebus'
6
+ require 'messagebus/swarm/controller'
7
+
8
+ options = {
9
+ :requires => [],
10
+ :configuration_source => Messagebus::Swarm::Controller::ConfigurationSource.new(:default, nil)
11
+ }
12
+
13
+ parser = OptionParser.new do |opts|
14
+ opts.banner = "Usage: swarm [options] COMMAND"
15
+
16
+ opts.separator ""
17
+ opts.separator "Options:"
18
+
19
+ opts.on("--config_path PATH", "A path to a yaml configuration file") do |config_path|
20
+ options[:configuration_source] = Messagebus::Swarm::Controller::ConfigurationSource.new(:path, config_path)
21
+ end
22
+
23
+ opts.on("--config_eval RUBY_CODE", "Ruby that evaluates to a configuration hash. This is ran after any requires") do |config_eval|
24
+ options[:configuration_source] = Messagebus::Swarm::Controller::ConfigurationSource.new(:eval, config_eval)
25
+ end
26
+
27
+ opts.on("-h", "--help", "Show help message") do
28
+ puts opts
29
+ end
30
+
31
+ opts.on("-r", "--require FILE", "A file to require before starting the swarm. Repeatable.") do |file|
32
+ options[:requires] << file
33
+ end
34
+
35
+ opts.on("--pidfile PID_FILE", "A file to write the PID to") do |file|
36
+ options[:pidfile] = file
37
+ end
38
+
39
+ opts.separator ""
40
+ opts.separator "Commands:"
41
+ opts.separator " start Start all workers in the configuration"
42
+ opts.separator " start DESTINATION COUNT Start COUNT workers for DESTINATION swarm listeners"
43
+ opts.separator " stop pid Kill a swarm"
44
+ opts.separator ""
45
+ end
46
+
47
+ parser.parse!
48
+
49
+ Messagebus::Swarm::Controller.require_files(options[:requires])
50
+ if pidfile = options[:pidfile]
51
+ pid = Process.pid
52
+ Messagebus::Swarm::Controller.write_pid(pidfile)
53
+ at_exit do
54
+ # we need this check to make sure we only bother doing this if
55
+ # we are the parent process that's shutting down
56
+ # at_exit is carried down when forking
57
+ Messagebus::Swarm::Controller.delete_pid(pidfile) if Process.pid == pid
58
+ end
59
+ end
60
+
61
+ case ARGV[0]
62
+ when 'start'
63
+ destination_name, drone_count = ARGV[1], ARGV[2]
64
+
65
+ log_file = options[:configuration_source].configuration_hash["worker_log_file"]
66
+ logger = log_file ? Logger.new(log_file) : Messagebus::Client.logger
67
+
68
+ Messagebus::Swarm::Controller.start(options[:configuration_source], logger, destination_name, drone_count)
69
+ exit(0)
70
+ when 'stop'
71
+ pid = ARGV[1] or raise "Must specify a pid of the process to stop"
72
+ Messagebus::Swarm::Controller.stop(pid.to_i)
73
+ exit(0)
74
+ else
75
+ puts parser.help
76
+ exit(1)
77
+ end
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2012, Groupon, Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # Neither the name of GROUPON nor the names of its contributors may be
16
+ # used to endorse or promote products derived from this software without
17
+ # specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
31
+ $:.unshift(File.join(File.dirname(__FILE__), "..", "vendor/gems"))
32
+
33
+ require "messagebus/version"
34
+ require 'messagebus/logger'
35
+ require 'messagebus/message'
36
+ require "messagebus/messagebus_types"
37
+ require 'messagebus/custom_errors'
38
+ require 'messagebus/error_status'
39
+ require 'messagebus/validations'
40
+ require 'messagebus/connection'
41
+ require 'messagebus/producer'
42
+ require 'messagebus/consumer'
43
+ require 'messagebus/cluster_map'
44
+ require 'messagebus/client'
45
+
46
+ require 'thrift'
47
+ require 'base64'
48
+ require 'stomp'
49
+
50
+ module Messagebus
51
+ # These are deprecated and not used anymore
52
+ DEST_TYPE_QUEUE = 'queue'
53
+ DEST_TYPE_TOPIC = 'topic'
54
+
55
+ ACK_TYPE_AUTO_CLIENT = 'autoClient'
56
+ ACK_TYPE_CLIENT = 'client'
57
+
58
+ LOG_DEFAULT_FILE = '/tmp/messagebus_client.log'
59
+
60
+ SERVER_REGEX = /^[a-zA-Z0-9\_.-]+:[0-9]+$/
61
+ end
62
+
@@ -0,0 +1,166 @@
1
+ # Copyright (c) 2012, Groupon, Inc.
2
+ # All rights reserved.
3
+ #
4
+ # Redistribution and use in source and binary forms, with or without
5
+ # modification, are permitted provided that the following conditions
6
+ # are met:
7
+ #
8
+ # Redistributions of source code must retain the above copyright notice,
9
+ # this list of conditions and the following disclaimer.
10
+ #
11
+ # Redistributions in binary form must reproduce the above copyright
12
+ # notice, this list of conditions and the following disclaimer in the
13
+ # documentation and/or other materials provided with the distribution.
14
+ #
15
+ # Neither the name of GROUPON nor the names of its contributors may be
16
+ # used to endorse or promote products derived from this software without
17
+ # specific prior written permission.
18
+ #
19
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21
+ # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22
+ # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23
+ # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24
+ # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
25
+ # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30
+
31
+ require "messagebus/dottable_hash"
32
+ require "benchmark"
33
+ require "logger"
34
+
35
+ module Messagebus
36
+ class Client
37
+ class InvalidDestinationError < StandardError; end
38
+ class InitializationError < StandardError; end
39
+
40
+ attr_reader :config, :logger, :cluster_map, :last_reload_time
41
+
42
+ def self.start(config_hash)
43
+ client = new(config_hash)
44
+ client.start
45
+
46
+ if block_given?
47
+ begin
48
+ yield client
49
+ ensure
50
+ client.stop
51
+ end
52
+ end
53
+ client
54
+ end
55
+
56
+ def initialize(config_hash)
57
+
58
+ @@logger = if provided_logger = config_hash.delete(:logger)
59
+ provided_logger
60
+ elsif log_file = config_hash["log_file"]
61
+ Logger.new(log_file)
62
+ else
63
+ Logger.new(Messagebus::LOG_DEFAULT_FILE)
64
+ end
65
+
66
+ # This is required to do a deep clone of config hash object
67
+ @config = DottableHash.new(Marshal.load(Marshal.dump(config_hash)))
68
+ @config.merge!(@config.cluster_defaults) if @config.cluster_defaults
69
+ @@logger.level = Logger::Severity.const_get(@config.log_level.upcase) if @config.log_level
70
+
71
+ @enable_client_logger_thread_debugging = config.enable_client_logger_thread_debugging
72
+
73
+ logger.debug "Initializing Messagebus client."
74
+ @cluster_map = ClusterMap.new(@config)
75
+ #added for reloading config on interval
76
+ @last_reload_time = Time.now
77
+ end
78
+
79
+ # Starts up all the connections to the bus.
80
+ # Optionally takes a block to which it yields self. When the block is
81
+ # passed, it will auto close the connections after the block finishes.
82
+ def start
83
+ if @config.enable_auto_init_connections
84
+ logger.info "auto enable connections set, starting clusters."
85
+ @cluster_map.start
86
+ else
87
+ logger.info "Config['enable_auto_init_connections'] is false, will not start any messagebus producers."
88
+ end
89
+ end
90
+
91
+ def stop
92
+ cluster_map.stop
93
+ end
94
+
95
+ def logger
96
+ @@logger ||= @config['log_file'] ? Logger.new(@config['log_file']) : Logger.new(Messagebus::LOG_DEFAULT_FILE)
97
+ end
98
+
99
+ def publish(destination_name, object, delay_ms = 0, safe = true, binary = false, headers = {})
100
+
101
+ if !(@config.enable_auto_init_connections)
102
+ logger.warn "Config['enable_auto_init_connections'] is false, not publishing destination_name=#{destination_name}, message_contents=#{object.inspect}"
103
+ false
104
+ else
105
+ producer = cluster_map.find(destination_name)
106
+ if producer.nil?
107
+ logger.error "Not publishing due to unconfigured destionation name. destination_name=#{destination_name}, message=#{object.inspect}"
108
+ raise InvalidDestinationError, "Destination #{destination_name} not found"
109
+ end
110
+
111
+ if binary
112
+ message = Messagebus::Message.create(object, nil, binary)
113
+ else
114
+ message = Messagebus::Message.create(object)
115
+ end
116
+
117
+ logger.info "Publishing to destination_name=#{destination_name}, message_id=#{message.message_id}, message_contents=#{object.inspect}"
118
+
119
+ begin
120
+ publish_result = nil
121
+ duration = Benchmark.realtime do
122
+ publish_result = producer.publish(destination_name, message, headers(delay_ms).merge(headers), safe)
123
+ end
124
+ duration = (duration * 1_000).round
125
+
126
+ if publish_result
127
+ logger.info "Message publishing to #{destination_name} took #{duration} result=success destination_name=#{destination_name}, message_id=#{message.message_id}, duration=#{duration}ms"
128
+ true
129
+ else
130
+ logger.error "Failed to publish message result=fail destination_name=#{destination_name}, message_id=#{message.message_id}, duration=#{duration}ms, message_contents=#{object.inspect}"
131
+ false
132
+ end
133
+ rescue => e
134
+ logger.error "Failed to publish message result=error destination_name=#{destination_name}, message_id=#{message.message_id}, duration=#{duration}ms, message_contents=#{object.inspect}, error=#{e.inspect}, backtrace=#{e.backtrace.join("|")}"
135
+ false
136
+ end
137
+ end
138
+ end
139
+
140
+ def reload_config_on_interval(config, interval = 300)
141
+ logger.info "Relaoding client configs after interval=#{interval}"
142
+ now = Time.now
143
+ if(now - @last_reload_time) >= interval
144
+ @cluster_map.update_config(config)
145
+ @last_reload_time = now
146
+ end
147
+ end
148
+
149
+ def headers(delay)
150
+ headers = {}
151
+ unless delay == 0
152
+ schedule_time = (Time.now.to_i * 1000 + delay).to_s
153
+ headers.merge!({
154
+ Messagebus::Producer::SCHEDULED_DELIVERY_TIME_MS_HEADER => schedule_time
155
+ })
156
+ end
157
+ headers
158
+ end
159
+
160
+ class << self
161
+ def logger
162
+ @@logger ||= Logger.new(Messagebus::LOG_DEFAULT_FILE)
163
+ end
164
+ end
165
+ end
166
+ end