jruby-hornetq 0.2.3.alpha → 0.2.5.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|