gearman-ruby 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +1 -0
  2. data/HOWTO +146 -0
  3. data/LICENSE +20 -0
  4. data/README +9 -0
  5. data/Rakefile +41 -0
  6. data/TODO +8 -0
  7. data/VERSION.yml +4 -0
  8. data/examples/calculus_client.rb +41 -0
  9. data/examples/calculus_worker.rb +42 -0
  10. data/examples/client.rb +19 -0
  11. data/examples/client_background.rb +14 -0
  12. data/examples/client_data.rb +16 -0
  13. data/examples/client_echo.rb +16 -0
  14. data/examples/client_exception.rb +17 -0
  15. data/examples/client_prefix.rb +15 -0
  16. data/examples/evented_client.rb +23 -0
  17. data/examples/evented_worker.rb +26 -0
  18. data/examples/gearman_environment.sh +25 -0
  19. data/examples/scale_image.rb +30 -0
  20. data/examples/scale_image_worker.rb +34 -0
  21. data/examples/server.rb +15 -0
  22. data/examples/worker.rb +23 -0
  23. data/examples/worker_data.rb +16 -0
  24. data/examples/worker_echo.rb +20 -0
  25. data/examples/worker_echo_pprof.rb +5 -0
  26. data/examples/worker_exception.rb +14 -0
  27. data/examples/worker_prefix.rb +25 -0
  28. data/gearman-ruby.gemspec +111 -0
  29. data/lib/gearman.rb +29 -0
  30. data/lib/gearman/client.rb +80 -0
  31. data/lib/gearman/evented/client.rb +99 -0
  32. data/lib/gearman/evented/reactor.rb +86 -0
  33. data/lib/gearman/evented/worker.rb +118 -0
  34. data/lib/gearman/job.rb +38 -0
  35. data/lib/gearman/protocol.rb +110 -0
  36. data/lib/gearman/server.rb +94 -0
  37. data/lib/gearman/task.rb +99 -0
  38. data/lib/gearman/taskset.rb +11 -0
  39. data/lib/gearman/util.rb +52 -0
  40. data/lib/gearman/worker.rb +39 -0
  41. data/test/basic_integration_test.rb +121 -0
  42. data/test/crash_test.rb +69 -0
  43. data/test/job_test.rb +30 -0
  44. data/test/protocol_test.rb +132 -0
  45. data/test/test_helper.rb +31 -0
  46. data/test/util_test.rb +12 -0
  47. data/test/worker_test.rb +45 -0
  48. metadata +133 -0
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg/
data/HOWTO ADDED
@@ -0,0 +1,146 @@
1
+ = GEARMAN
2
+
3
+ "Gearman provides a generic application framework to farm out work to other
4
+ machines or processes that are better suited to do the work. It allows you to
5
+ do work in parallel, to load balance processing, and to call functions between
6
+ languages. It can be used in a variety of applications, from high-availability
7
+ web sites to the transport of database replication events. In other words, it
8
+ is the nervous system for how distributed processing communicates."
9
+
10
+ - http://www.gearman.org/
11
+
12
+
13
+ == Setting up a basic environment
14
+
15
+ A very basic German environment will look like this:
16
+
17
+ ----------
18
+ | Client |
19
+ ----------
20
+ |
21
+ --------------
22
+ | Job Server |
23
+ --------------
24
+ |
25
+ ----------------------------------------------
26
+ | | | |
27
+ ---------- ---------- ---------- ----------
28
+ | Worker | | Worker | | Worker | | Worker |
29
+ ---------- ---------- ---------- ----------
30
+
31
+ And the behavior will be the following:
32
+
33
+ * JobServer: Acts as a message passing point.
34
+ * Client: Sends tasks to the JobServer. Will be connected to only one JobServer
35
+ in case more than one exits for failover purposes.
36
+ * Worker: Anounce his 'abilities' to the JobServer and waits for tasks.
37
+
38
+ For the JobServer we recommend to use the offical Perl version, there's also a
39
+ more performant C implementation of the server with support for persistent
40
+ queues, bells and whistles but is not stable enough for production use at the
41
+ time of this document was wrote.
42
+
43
+ The Client and the Worker can be implemented in any language. This way you can
44
+ send tasks from a Ruby client server, to a Perl or C worker in order to get
45
+ better performance.
46
+
47
+ == Installing the required software
48
+
49
+ For the JobServer we recommend to use the offical Perl version, to install it:
50
+
51
+ * Mac OS X: sudo port install p5-gearman-server
52
+ * Debian/Ubuntu: sudo apt-get install gearman-server
53
+
54
+ To get the Ruby libraries by Xing:
55
+
56
+ git clone git://github.com/xing/gearman-ruby.git
57
+
58
+ == Gearman demo
59
+
60
+ Now you're ready for you first experience with Gearman. In the cloned repository
61
+ you'll find an 'examples' directory.
62
+
63
+ Run the 'gearman_environment.sh' to build an environment like the one showed in
64
+ the diagram above.
65
+
66
+ * Client: Will ask you for an arithmetic operation, like: 2+3
67
+ The code of the client is in: 'examples/calculus_client.rb'
68
+
69
+ * JobServer: The Perl server.
70
+
71
+ * Workers: You'll have 4 worker, one for each of the basic arithmetic
72
+ operations.
73
+ The code of the worker is in: 'examples/calculus_worker.rb'
74
+
75
+ There are other demos in the examples folder you can give a look at. Each demo usually
76
+ consist in the client and server scripts.
77
+
78
+ === Creating clients and tasks
79
+
80
+ In order to get a job scheduled by a Gearman server using the gearman ruby library, there
81
+ are three main objects you must interact with: Gearman::Client, Gearman::Task and Gearman::TaskSet.
82
+ Let's review all of them briefly:
83
+
84
+ - Gearman::Client -> the portion of the library storing the data about the connection to
85
+ the Gearman server.
86
+ - Gearman::Task -> a job execution request that will be dispatched by the Gearman server to
87
+ worker and whose result data will be returned to the client.
88
+ - Gearman::TaskSet -> a collection of tasks to be executed. The Taskset object will track the
89
+ execution of the tasks with the info returned from the Gearman server
90
+ and notify the client with the results or errors when all the tasks
91
+ have completed their execution.
92
+
93
+ To send a new task to the Gearman server, the client must build a new Gearman::Task object, add it to
94
+ a Gearman::TaskSet that must hold a reference to a Gearman::Client and send the wait message to
95
+ the TaskSet.
96
+ The following code taken from examples/client.rb shows the process:
97
+
98
+ ----------------------------------------------------
99
+ servers = ['localhost:4730', 'localhost:4731']
100
+
101
+ client = Gearman::Client.new(servers)
102
+ taskset = Gearman::TaskSet.new(client)
103
+
104
+ task = Gearman::Task.new('sleep', 20)
105
+ task.on_complete {|d| puts d }
106
+
107
+ taskset.add_task(task)
108
+ taskset.wait(100)
109
+ ----------------------------------------------------
110
+
111
+ The name of the function to be executed is the first parameter to the constructor of the Task object.
112
+ Take into account that the string you pass as a parameter will be used 'as it' by the Gearman server
113
+ to locate a suitable worker for that function.
114
+ The second parameter is the argument to be sent to the worker that will execute, if the arguments for
115
+ the task are complex, a serialization format like YAML or XML must be agreeded with the workers.
116
+ The last and optional parameter is a hash of options. The following options are currently available:
117
+
118
+ - :priority -> (:high | :low) the priority of the job, a high priority job is executed before a low on
119
+ - :background -> (true | false) a background task will return no further information to the client.
120
+
121
+ The execution of a task in a Gearman remote worker can fail, the worker can throw an exception, etc.
122
+ All these events can be handled by the client of the ruby library registering callback blocks.
123
+ The following events are currently available:
124
+
125
+ - on_complete -> the task was executed succesfully
126
+ - on_fail -> the task fail for some unknown reason
127
+ - on_retry -> after failure, the task is gonna be retried, the number of retries is passed
128
+ - on_exception -> the remote worker send an exception notification, the exception text is passed
129
+ - on_stauts -> a status update is sent by the remote worker
130
+
131
+ In order to receive exception notifications in the client, this option must be sent in the server, the
132
+ method option_request can be used this task. The following example, extracted from the examples/client_exception.rb
133
+ demo script shows the process:
134
+
135
+ ----------------------------------------------------
136
+ client = Gearman::Client.new(servers)
137
+ #try this out
138
+ client.option_request("exceptions")
139
+ ----------------------------------------------------
140
+
141
+ This feature will only works if the server and workers have implemented support for the OPT_REQ and WORK_EXCEPTION
142
+ messages of the Gearman protocol.
143
+
144
+
145
+ Enjoy.
146
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 XING AG
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,9 @@
1
+ gearman-ruby
2
+ ===============
3
+
4
+ Library for the Gearman distributed job system
5
+
6
+ COPYRIGHT
7
+ =========
8
+
9
+ Copyright (c) 2009 XING AG. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,41 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+ require 'rcov/rcovtask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |s|
9
+ s.name = "gearman-ruby"
10
+ s.summary = "Library for the Gearman distributed job system"
11
+ s.email = "kim.altintop@gmail.com"
12
+ s.homepage = "http://github.com/kim/gearman-ruby"
13
+ s.description = "Library for the Gearman distributed job system"
14
+ s.authors = ["Kim Altintop"]
15
+ s.add_dependency 'eventmachine', '>= 0.12.8'
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ Rake::TestTask.new do |t|
22
+ t.libs << 'lib'
23
+ t.pattern = 'test/**/*_test.rb'
24
+ t.verbose = false
25
+ end
26
+
27
+ Rake::RDocTask.new do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'Gearman Ruby'
30
+ rdoc.options << '--line-numbers' << '--inline-source'
31
+ rdoc.rdoc_files.include('README*')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ Rcov::RcovTask.new do |t|
36
+ t.libs << "test"
37
+ t.test_files = FileList['test/*_test.rb']
38
+ t.verbose = true
39
+ end
40
+
41
+ task :default => :test
data/TODO ADDED
@@ -0,0 +1,8 @@
1
+ - Failover strategies
2
+ - Client:
3
+ * If connected for the first time, try to connect to at least one server from the server array
4
+ * If already connected to a server, and it goes down, the client should go down as well
5
+ - Worker:
6
+ * If connected for the first time, try to connect to as many servers as it can.
7
+ Loop trough the bad servers, trying to reconnect to them as well.
8
+ * If a already connected to a server, and it goes down, wait and try to reconnect again.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 2
4
+ :minor: 0
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require '../lib/gearman'
4
+ #Gearman::Util.debug = true
5
+
6
+ # Connect to the local server (at the default port 4730)
7
+ client = Gearman::Client.new('localhost')
8
+ taskset = Gearman::Taskset.new
9
+
10
+ # Get something to echo
11
+ puts '[client] Write a basic arithmetic operation:'
12
+ input = gets
13
+
14
+ operations = input.chomp.scan(/\d+[\+\-\*\/]\d+/).compact
15
+ puts "[client] The following operations were found: #{operations.inspect}"
16
+
17
+ # Setup a task for operation
18
+ operations.each do |op|
19
+ # Determining the operation
20
+ case op
21
+ when /\+/
22
+ type, data = 'addition', op.split('+')
23
+ when /\-/
24
+ type, data = 'subtraction', op.split('-')
25
+ when /\*/
26
+ type, data = 'multiplication', op.split('*')
27
+ when /\//
28
+ type, data = 'division', op.split('/')
29
+ end
30
+
31
+ task = Gearman::Task.new(type, Marshal.dump(data.map {|v| v.to_i}))
32
+ task.on_complete {|r| puts "[client] #{type} result is: #{r}" }
33
+ task.on_warning {|w| puts "[client] warn: #{w}" }
34
+ task.on_fail { puts "[client] calculation failed" }
35
+
36
+ # Sending the task to the server
37
+ puts "[client] Sending values: #{data.inspect}, to the '#{type}' worker"
38
+ taskset << task
39
+ end
40
+
41
+ client.run(taskset)
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require '../lib/gearman'
4
+
5
+ #Gearman::Util.debug = true
6
+
7
+ worker = Gearman::Worker.new('localhost')
8
+
9
+ # Additon ability
10
+ worker.add_ability('addition') do |data,job|
11
+ values = Marshal.load(data)
12
+ puts "[addition_worker] Calculating #{values.inspect}..."
13
+ # sleep 5
14
+ values.first + values.last
15
+ end
16
+
17
+ # Subtraction ability
18
+ worker.add_ability('subtraction') do |data,job|
19
+ values = Marshal.load(data)
20
+ puts "[subtraction_worker] Calculating #{values.inspect}..."
21
+ # sleep 5
22
+ values.first - values.last
23
+ end
24
+
25
+ # Multiplication worker
26
+ worker.add_ability('multiplication') do |data,job|
27
+ values = Marshal.load(data)
28
+ puts "[multiplication_worker] Calculating #{values.inspect}..."
29
+ # sleep 5
30
+ values.first * values.last
31
+ end
32
+
33
+ # Division worker
34
+ worker.add_ability('division') do |data,job|
35
+ values = Marshal.load(data)
36
+ puts "[division_worker] Calculating #{data.inspect}..."
37
+ # sleep 5
38
+ values.first / values.last
39
+ end
40
+
41
+ # Running the workers
42
+ worker.work
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ #require 'gearman'
3
+ require '../lib/gearman'
4
+ Gearman::Util.debug = true
5
+
6
+ servers = ['localhost:4730', 'localhost:4731']
7
+
8
+ client = Gearman::Client.new(servers)
9
+ taskset = Gearman::Taskset.new
10
+
11
+ task = Gearman::Task.new('sleep', 2)
12
+ task.on_complete {|d| puts "TASK 1: #{d}" }
13
+ taskset << task
14
+
15
+ task = Gearman::Task.new('sleep', 2)
16
+ task.on_complete {|d| puts "TASK 2: #{d}" }
17
+ taskset << task
18
+
19
+ client.run(taskset)
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ #require 'gearman'
3
+ require '../lib/gearman'
4
+ Gearman::Util.debug = true
5
+
6
+ servers = ['localhost:4730', 'localhost:4731']
7
+
8
+ client = Gearman::Client.new(servers)
9
+
10
+ task = Gearman::Task.new('sleep', 20, :background => true, :poll_status_interval => 1)
11
+ task.on_complete {|d| puts d } #never called
12
+ task.on_status {|d| puts "Status: #{d}"}
13
+
14
+ client.run(task)
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ #require 'gearman'
3
+ require '../lib/gearman'
4
+ Gearman::Util.debug = true
5
+
6
+ servers = ['localhost:4730']
7
+
8
+ client = Gearman::Client.new(servers)
9
+ taskset = Gearman::Taskset.new
10
+
11
+ task = Gearman::Task.new('chunked_transfer')
12
+ task.on_data {|d| puts d }
13
+ task.on_complete {|d| puts d }
14
+
15
+ taskset << task
16
+ client.run taskset
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require '../lib/gearman'
3
+
4
+ servers = ['localhost:4730']
5
+ client = Gearman::Client.new(servers)
6
+ taskset = Gearman::TaskSet.new(client)
7
+
8
+ 100000.times do |i|
9
+ task = Gearman::Task.new('echo', "hello #{i}")
10
+ task.on_complete {|d| puts d }
11
+ taskset.add_task(task)
12
+ end
13
+ taskset.wait(100)
14
+
15
+ taskset.add_task(Gearman::Task.new('quit'))
16
+ taskset.wait(100)
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require '../lib/gearman'
3
+ Gearman::Util.debug = true
4
+
5
+ servers = ['localhost:4730']
6
+
7
+ client = Gearman::Client.new(servers)
8
+
9
+ task = Gearman::Task.new('fail_with_exception', "void")
10
+ task.retries = 2
11
+ task.on_complete {|d| puts d }
12
+ task.on_exception {|ex| puts "This should never be called" }
13
+ task.on_warning {|warning| puts "WARNING: #{warning}" }
14
+ task.on_retry { puts "PRE-RETRY HOOK: retry no. #{task.retries_done}" }
15
+ task.on_fail { puts "TASK FAILED, GIVING UP" }
16
+
17
+ client.run task
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ #require 'gearman'
3
+ require '../lib/gearman'
4
+ Gearman::Util.debug = true
5
+
6
+ servers = ['localhost:4730', 'localhost:4731']
7
+
8
+ ability_name_with_prefix = Gearman::Util.ability_name_with_prefix("test","sleep")
9
+
10
+ client = Gearman::Client.new(servers)
11
+
12
+ task = Gearman::Task.new(ability_name_with_prefix, 20)
13
+ task.on_complete {|d| puts d }
14
+
15
+ client.run task
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ require "ruby-debug"
3
+ require '../lib/gearman'
4
+
5
+ Gearman::Util.debug = true
6
+
7
+ servers = ['localhost:4730', 'localhost:4731']
8
+ client = Gearman::Client.new(servers)
9
+
10
+ taskset = Gearman::Taskset.new
11
+
12
+ task = Gearman::Task.new('sleep', 2)
13
+ task.on_status {|numerator, denominator| puts "TASK 1: Completed #{numerator} of #{denominator}"}
14
+ task.on_complete {|d| puts "TASK 1: #{d}" }
15
+ taskset << task
16
+
17
+ task = Gearman::Task.new('sleep', 15, :poll_status_interval => 2, :uuid => nil)
18
+ task.on_status {|numerator, denominator| puts "TASK 2: Completed #{numerator} of #{denominator}"}
19
+ task.on_data {|data| puts "TASK 2 DATA: #{data}" }
20
+ task.on_complete {|d| puts "TASK 2: #{d}" }
21
+ taskset << task
22
+
23
+ client.run(taskset)