jruby-hornetq 0.2.3.alpha → 0.2.5.alpha
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.
- data/README.md +2 -0
- data/Rakefile +1 -1
- data/examples/advanced/transaction-failover/producer.rb +0 -1
- data/examples/client/advanced/batch_client.rb +3 -3
- data/examples/client/resque/hornetq_job.rb +197 -0
- data/examples/client/resque/processor.rb +41 -0
- data/examples/client/resque/readme.md +33 -0
- data/examples/client/resque/resque_conf.rb +1 -0
- data/examples/client/resque/resque_worker.rb +22 -0
- data/examples/client/resque/sleep_job.rb +45 -0
- data/examples/simple_worker/README +14 -0
- data/examples/simple_worker/hornetq.yml +34 -0
- data/examples/simple_worker/producer.rb +54 -0
- data/examples/simple_worker/test_object.rb +11 -0
- data/examples/simple_worker/worker.rb +49 -0
- data/lib/hornetq.rb +6 -3
- data/lib/hornetq/client.rb +16 -13
- data/lib/hornetq/client/factory.rb +420 -355
- data/lib/hornetq/client/message_handler.rb +63 -0
- data/lib/hornetq/client/org_hornetq_api_core_client_client_session.rb +157 -2
- data/lib/hornetq/client/org_hornetq_core_client_impl_client_consumer_impl.rb +94 -0
- data/lib/hornetq/client/org_hornetq_core_client_impl_client_message_impl.rb +0 -7
- data/lib/hornetq/client/{requestor.rb → requestor_pattern.rb} +3 -2
- data/lib/hornetq/client/{server.rb → server_pattern.rb} +3 -3
- data/lib/hornetq/client/session_pool.rb +0 -1
- data/lib/hornetq/common/log_delegate.rb +46 -0
- data/lib/hornetq/common/logging.rb +32 -0
- data/lib/hornetq/common/org_hornetq_core_logging_logger.rb +58 -0
- metadata +37 -9
data/README.md
CHANGED
@@ -44,6 +44,8 @@ HornetQ
|
|
44
44
|
|
45
45
|
For information on the HornetQ messaging and queuing system, see:
|
46
46
|
|
47
|
+
For more documentation on any of the classes, see: http://hornetq.sourceforge.net/docs/hornetq-2.1.0.Final/api/index.html?org/hornetq/api/core/client/package-summary.html
|
48
|
+
|
47
49
|
Concepts & Terminology
|
48
50
|
----------------------
|
49
51
|
|
data/Rakefile
CHANGED
@@ -8,7 +8,7 @@ desc "Build gem"
|
|
8
8
|
task :gem do |t|
|
9
9
|
gemspec = Gem::Specification.new do |s|
|
10
10
|
s.name = 'jruby-hornetq'
|
11
|
-
s.version = '0.2.
|
11
|
+
s.version = '0.2.5.alpha'
|
12
12
|
s.authors = ['Reid Morrison', 'Brad Pardee']
|
13
13
|
s.email = ['rubywmq@gmail.com', 'bpardee@gmail.com']
|
14
14
|
s.homepage = 'https://github.com/ClarityServices/jruby-hornetq'
|
@@ -14,7 +14,6 @@ constants = config['constants']
|
|
14
14
|
|
15
15
|
# Create a HornetQ session
|
16
16
|
HornetQ::Client::Factory.session(config['client']) do |session|
|
17
|
-
session.delete_queue(constants[:queue]) rescue nil
|
18
17
|
session.create_queue(constants[:address], constants[:queue], constants[:durable])
|
19
18
|
producer = session.create_producer(constants[:address])
|
20
19
|
start_time = Time.now
|
@@ -25,7 +25,7 @@ request_address = 'jms.queue.ExampleQueue'
|
|
25
25
|
|
26
26
|
config = YAML.load_file(File.dirname(__FILE__) + '/hornetq.yml')['development']
|
27
27
|
|
28
|
-
class
|
28
|
+
class BatchClientPattern
|
29
29
|
def initialize(session, request_address)
|
30
30
|
@producer = session.create_producer(request_address)
|
31
31
|
reply_queue = "#{request_address}.#{Java::java.util::UUID.randomUUID.toString}"
|
@@ -62,7 +62,7 @@ class BatchClient
|
|
62
62
|
#print "Sending #{total_count} messages"
|
63
63
|
start_time = Time.now
|
64
64
|
total_count.times do |i|
|
65
|
-
message = @session.create_message(HornetQ::Client::Message::TEXT_TYPE,
|
65
|
+
message = @session.create_message(HornetQ::Client::Message::TEXT_TYPE,true)
|
66
66
|
message.reply_to_queue_name = @consumer.queue_name
|
67
67
|
message.body = "Request Current Time. #{i}"
|
68
68
|
@producer.send(message)
|
@@ -99,7 +99,7 @@ end
|
|
99
99
|
HornetQ::Client::Factory.session(config) do |session|
|
100
100
|
batching_size = total_count if batching_size > total_count
|
101
101
|
|
102
|
-
client =
|
102
|
+
client = BatchClientPattern.new(session, request_address)
|
103
103
|
|
104
104
|
# Start receive thread
|
105
105
|
receive_thread = Thread.new {client.receive}
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# Allow examples to be run in-place without requiring a gem install
|
2
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../../../lib'
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'resque/job_with_status' # in rails you would probably do this in an initializer
|
6
|
+
require 'yaml'
|
7
|
+
require 'hornetq'
|
8
|
+
require 'sync'
|
9
|
+
|
10
|
+
# The Batch Client Pattern submits requests to a server following the Server Pattern
|
11
|
+
#
|
12
|
+
# A good example of when to use the Batch Client Pattern is when processing large
|
13
|
+
# batch files. Rather than just flood the queue with every record from a file
|
14
|
+
# the Batch Client Pattern can be used to only send out a batch of requests at
|
15
|
+
# a time and when sufficient responses have been received, send another batch.
|
16
|
+
#
|
17
|
+
# The following additional features can be implemented in this pattern
|
18
|
+
# * Stop the batch if say 80% of records fail in the first batch, etc.
|
19
|
+
# * Support pause and resume of batch processing
|
20
|
+
# * Support restart and recoverability
|
21
|
+
#
|
22
|
+
# See the Resque example for implementations of some of the above capabilities
|
23
|
+
#
|
24
|
+
class BatchClientPattern
|
25
|
+
def initialize(session, request_address)
|
26
|
+
@producer = session.create_producer(request_address)
|
27
|
+
reply_queue = "#{request_address}.#{Java::java.util::UUID.randomUUID.toString}"
|
28
|
+
begin
|
29
|
+
session.create_temporary_queue(reply_queue, reply_queue)
|
30
|
+
rescue NativeException => exc
|
31
|
+
p exc
|
32
|
+
end
|
33
|
+
@consumer = session.create_consumer(reply_queue)
|
34
|
+
@session = session
|
35
|
+
session.start
|
36
|
+
|
37
|
+
@counter_sync = Sync.new
|
38
|
+
@processed = 0
|
39
|
+
end
|
40
|
+
|
41
|
+
# Before re-using a batch pattern, reset all internal counters
|
42
|
+
def reset
|
43
|
+
@counter_sync.synchronize(:EX) do
|
44
|
+
@failed = 0
|
45
|
+
@sucessful = 0
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Return the current message count
|
50
|
+
def processed
|
51
|
+
@counter_sync.synchronize(:SH) { @sucessful + @processed }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Increment Successful response counter by supplied count
|
55
|
+
def inc_sucessful(count=1)
|
56
|
+
@counter_sync.synchronize(:EX) { @sucessful += count }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Return the current message count
|
60
|
+
def sucessful
|
61
|
+
@counter_sync.synchronize(:SH) { @sucessful }
|
62
|
+
end
|
63
|
+
|
64
|
+
# Increment Successful response counter by supplied count
|
65
|
+
def inc_failed(count=1)
|
66
|
+
@counter_sync.synchronize(:EX) { @failed += count }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Return the current message count
|
70
|
+
def failed
|
71
|
+
@counter_sync.synchronize(:SH) { @failed }
|
72
|
+
end
|
73
|
+
|
74
|
+
# Send x messages
|
75
|
+
def send(total_count)
|
76
|
+
#print "Sending #{total_count} messages"
|
77
|
+
start_time = Time.now
|
78
|
+
total_count.times do |i|
|
79
|
+
message = @session.create_message(HornetQ::Client::Message::TEXT_TYPE,false)
|
80
|
+
message.reply_to_queue_name = @consumer.queue_name
|
81
|
+
message.body = "Request Current Time. #{i}"
|
82
|
+
@producer.send(message)
|
83
|
+
print "."
|
84
|
+
#puts "Sent:#{message}"
|
85
|
+
end
|
86
|
+
duration = Time.now - start_time
|
87
|
+
#printf "\nSend %5d msg, %5.2f s, %10.2f msg/s\n", total_count, duration, total_count/duration
|
88
|
+
end
|
89
|
+
|
90
|
+
# Receive Reply messages calling the supplied block for each message
|
91
|
+
# passing in the message received from the server.
|
92
|
+
# After the block returns, the message is automatically acknowledged
|
93
|
+
def receive(&block)
|
94
|
+
print "Receiving messages"
|
95
|
+
raise "Missing mandatory block" unless block
|
96
|
+
begin
|
97
|
+
while reply = @consumer.receive
|
98
|
+
block.call(reply)
|
99
|
+
reply.acknowledge
|
100
|
+
end
|
101
|
+
rescue Exception => exc
|
102
|
+
p exc
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def close
|
107
|
+
@producer.close
|
108
|
+
@consumer.close
|
109
|
+
@session.delete_queue(@consumer.queue_name)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# sleeps for _length_ seconds updating the status every second
|
114
|
+
#
|
115
|
+
#
|
116
|
+
# Create a Resque Job with the ability to report status
|
117
|
+
#
|
118
|
+
class HornetQJob < Resque::JobWithStatus
|
119
|
+
|
120
|
+
# Set the name of the queue to use for this Job Worker
|
121
|
+
@queue = "hornetq_job"
|
122
|
+
|
123
|
+
# Must be an instance method for Resque::JobWithStatus
|
124
|
+
def perform
|
125
|
+
total_count = (options['total_count'] || 4000).to_i
|
126
|
+
batching_size = (options['batching_size'] || 80).to_i
|
127
|
+
address = options['address'] || 'processor'
|
128
|
+
receive_thread = nil
|
129
|
+
|
130
|
+
# Create a HornetQ session
|
131
|
+
count = 0
|
132
|
+
HornetQ::Client::Factory.session('hornetq://localhost') do |session|
|
133
|
+
batching_size = total_count if batching_size > total_count
|
134
|
+
|
135
|
+
client = BatchClient.new(session, address)
|
136
|
+
|
137
|
+
# Start receive thread
|
138
|
+
receive_thread = Thread.new do
|
139
|
+
client.receive do |message|
|
140
|
+
print '@'
|
141
|
+
client.inc_sucessful
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
times = (total_count/batching_size).to_i
|
146
|
+
puts "Performing #{times} loops"
|
147
|
+
times.times do |i|
|
148
|
+
at(count, total_count, "At #{count} of #{total_count}")
|
149
|
+
client.send(batching_size)
|
150
|
+
count += batching_size
|
151
|
+
# Wait for at least 80% of responses
|
152
|
+
loop do
|
153
|
+
#puts "Waiting for receive"
|
154
|
+
sleep 0.1
|
155
|
+
received_count = client.counter
|
156
|
+
#puts "\nReceived #{received_count} messages"
|
157
|
+
if received_count >= 0.8 * count
|
158
|
+
puts ""
|
159
|
+
break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
while client.counter < total_count
|
165
|
+
sleep 0.1
|
166
|
+
print "*"
|
167
|
+
end
|
168
|
+
client.close
|
169
|
+
end
|
170
|
+
receive_thread.kill if receive_thread
|
171
|
+
completed('num' => total_count, 'description' => "Completed #{count} of #{total_count}")
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
|
176
|
+
# Submit a new request at the command line
|
177
|
+
if __FILE__ == $0
|
178
|
+
# Make sure you have a worker running
|
179
|
+
# jruby resque_worker.rb
|
180
|
+
|
181
|
+
options = {
|
182
|
+
'total_count' => (ARGV[0] || 4000).to_i,
|
183
|
+
'batching_size' => (ARGV[1] || 40).to_i,
|
184
|
+
'address' => 'processor'
|
185
|
+
}
|
186
|
+
# running the job
|
187
|
+
puts "Creating the HornetQJob"
|
188
|
+
job_id = HornetQJob.create(options)
|
189
|
+
puts "Got back #{job_id}"
|
190
|
+
|
191
|
+
# check the status until its complete
|
192
|
+
while status = Resque::Status.get(job_id) and !status.completed? && !status.failed? &&!status.killed?
|
193
|
+
sleep 1
|
194
|
+
puts status.inspect
|
195
|
+
end
|
196
|
+
puts status.inspect
|
197
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
#
|
2
|
+
# Processor:
|
3
|
+
# Process requests submitted by Resque Worker and reply
|
4
|
+
#
|
5
|
+
|
6
|
+
# Allow examples to be run in-place without requiring a gem install
|
7
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../../lib'
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
require 'hornetq'
|
11
|
+
|
12
|
+
# Let server shutdown on its own after 5 minutes of inactivity. Set to 0 to wait forever
|
13
|
+
timeout = (ARGV[0] || 300000).to_i
|
14
|
+
|
15
|
+
q_name = 'processor'
|
16
|
+
|
17
|
+
HornetQ::Client::Factory.start('hornetq://localhost') do |session|
|
18
|
+
begin
|
19
|
+
# Create durable queue with matching address
|
20
|
+
session.create_queue(q_name, q_name, true)
|
21
|
+
rescue
|
22
|
+
# Ignore when queue already exists
|
23
|
+
end
|
24
|
+
|
25
|
+
server = session.create_server(q_name, timeout)
|
26
|
+
|
27
|
+
puts "Waiting for Requests..."
|
28
|
+
server.run do |request_message|
|
29
|
+
print "."
|
30
|
+
|
31
|
+
# Create Reply Message
|
32
|
+
reply_message = session.create_message(HornetQ::Client::Message::TEXT_TYPE, false)
|
33
|
+
reply_message.body = "Echo [#{request_message.body}]"
|
34
|
+
|
35
|
+
# The result of the block is the message to be sent back, or nil if no reply
|
36
|
+
reply_message
|
37
|
+
end
|
38
|
+
|
39
|
+
# Server will stop after timeout period after no messages received. Set to 0 to wait forever
|
40
|
+
server.close
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
Overview
|
2
|
+
|
3
|
+
This example shows how to use Resque to initiate a Job which in turn submits all
|
4
|
+
its work to processors.
|
5
|
+
|
6
|
+
## Architecture
|
7
|
+
|
8
|
+
The architecture for this example uses:
|
9
|
+
### Initiator
|
10
|
+
|
11
|
+
An _initiator_ which enqueues the job to be completed
|
12
|
+
|
13
|
+
### Resque Worker
|
14
|
+
|
15
|
+
A _resque worker_ which manages the job itself. The _resque worker_ in turn submits
|
16
|
+
requests to the _task worker_.
|
17
|
+
|
18
|
+
### Processor
|
19
|
+
|
20
|
+
A _processor_ performs the actual work and receives its requests over a
|
21
|
+
hornetq queue. It replies back to the _resque worker_ when complete
|
22
|
+
|
23
|
+
|
24
|
+
Requirements
|
25
|
+
|
26
|
+
Install the following gems
|
27
|
+
* resque
|
28
|
+
* resque-status
|
29
|
+
** https://github.com/quirkey/resque-status
|
30
|
+
|
31
|
+
Start the Resque Job worker
|
32
|
+
|
33
|
+
Start the Task workers
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'resque/status_server'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Start up a Resque Worker to run the Job Request
|
2
|
+
require 'rubygems'
|
3
|
+
require 'resque'
|
4
|
+
require 'resque/job_with_status'
|
5
|
+
require 'sleep_job'
|
6
|
+
require 'hornetq_job'
|
7
|
+
|
8
|
+
# Number of workers to start (each on its own thread)
|
9
|
+
thread_count = (ARGV[0] || 1).to_i
|
10
|
+
|
11
|
+
# Configure Redis client connection
|
12
|
+
Resque.redis = "localhost:6379"
|
13
|
+
|
14
|
+
# How long to keep Job status for in seconds
|
15
|
+
Resque::Status.expire_in = (24 * 60 * 60) # 24hrs in seconds
|
16
|
+
|
17
|
+
# Start the worker instance(s) in which Jobs are run
|
18
|
+
resque_worker = Resque::Worker.new("hornetq_job")
|
19
|
+
resque_worker.log "Starting worker #{resque_worker}"
|
20
|
+
resque_worker.verbose = true
|
21
|
+
resque_worker.very_verbose = true
|
22
|
+
resque_worker.work(5) # Redis Poll interval in seconds
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'resque/job_with_status' # in rails you would probably do this in an initializer
|
3
|
+
|
4
|
+
# sleeps for _length_ seconds updating the status every second
|
5
|
+
#
|
6
|
+
#
|
7
|
+
# Create a Resque Job with the ability to report status
|
8
|
+
#
|
9
|
+
class SleepJob < Resque::JobWithStatus
|
10
|
+
|
11
|
+
# Set the name of the queue to use for this Job Worker
|
12
|
+
@queue = "sleep_job"
|
13
|
+
|
14
|
+
# Must be an instance method for Resque::JobWithStatus
|
15
|
+
def perform
|
16
|
+
total = options['length'].to_i || 10
|
17
|
+
num = 0
|
18
|
+
while num < total
|
19
|
+
at(num, total, "At #{num} of #{total}")
|
20
|
+
sleep(1)
|
21
|
+
num += 1
|
22
|
+
end
|
23
|
+
completed
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
# Submit a new request at the command line
|
29
|
+
if __FILE__ == $0
|
30
|
+
# Make sure you have a worker running
|
31
|
+
# jruby resque_worker.rb
|
32
|
+
|
33
|
+
count = (ARGV[0] || 10).to_i
|
34
|
+
|
35
|
+
# running the job
|
36
|
+
puts "Creating the SleepJob"
|
37
|
+
job_id = SleepJob.create :length => count
|
38
|
+
puts "Got back #{job_id}"
|
39
|
+
|
40
|
+
# check the status until its complete
|
41
|
+
while status = Resque::Status.get(job_id) and !status.completed? && !status.failed?
|
42
|
+
sleep 1
|
43
|
+
puts status.inspect
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# Window 1:
|
2
|
+
# Step 1
|
3
|
+
hornetq_server hornetq.yml standalone_server
|
4
|
+
# Wait for HornetQ Server version ... started
|
5
|
+
|
6
|
+
# Window 2:
|
7
|
+
# Step 2
|
8
|
+
./producer.rb
|
9
|
+
|
10
|
+
# Window 3:
|
11
|
+
# Step 3
|
12
|
+
./worker.rb
|
13
|
+
|
14
|
+
#Should be able to cntl-c and restart producer and worker without issues.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
standalone_server:
|
2
|
+
:uri: hornetq://localhost:15445
|
3
|
+
:data_directory: ./data
|
4
|
+
:persistence_enabled: true
|
5
|
+
:security_enabled: false
|
6
|
+
|
7
|
+
client:
|
8
|
+
:connector:
|
9
|
+
:uri: hornetq://localhost:15445
|
10
|
+
:session:
|
11
|
+
:username: guest
|
12
|
+
:password: guest
|
13
|
+
:session_pool:
|
14
|
+
:pool_size: 3
|
15
|
+
:pool_warn_timeout: 0.25
|
16
|
+
:username: guest
|
17
|
+
:password: guest
|
18
|
+
|
19
|
+
:addresses:
|
20
|
+
address1:
|
21
|
+
:serialize: ruby_marshal
|
22
|
+
:unique: true
|
23
|
+
:durable: true
|
24
|
+
:queues:
|
25
|
+
queue1_1:
|
26
|
+
queue1_2:
|
27
|
+
|
28
|
+
address2:
|
29
|
+
:serialize: json
|
30
|
+
:unique: false
|
31
|
+
:durable: false
|
32
|
+
:queues:
|
33
|
+
queue2_1:
|
34
|
+
queue2_2:
|