drbqs 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,19 @@
1
+ = drbqs
2
+
3
+ Description goes here.
4
+
5
+ == Contributing to drbqs
6
+
7
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
8
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
9
+ * Fork the project
10
+ * Start a feature/bugfix branch
11
+ * Commit and push until you are happy with your contribution
12
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
13
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2011 Takayuki YAMAGUCHI. See LICENSE.txt for
18
+ further details.
19
+
data/Rakefile ADDED
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "drbqs"
16
+ gem.homepage = "http://github.com/ytaka/drbqs"
17
+ gem.license = "GPL3"
18
+ gem.summary = "dRuby Queueing System"
19
+ gem.description = "Queuing system over network that is implemented by dRuby."
20
+ gem.email = "d@ytak.info"
21
+ gem.authors = ["Takayuki YAMAGUCHI"]
22
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
23
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
24
+ # gem.add_runtime_dependency 'jabber4r', '> 0.1'
25
+ # gem.add_development_dependency 'rspec', '> 1.2.3'
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
36
+ spec.pattern = 'spec/**/*_spec.rb'
37
+ spec.rcov = true
38
+ end
39
+
40
+ task :default => :spec
41
+
42
+ require 'yard'
43
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.0
data/lib/drbqs.rb ADDED
@@ -0,0 +1,12 @@
1
+ require 'thread'
2
+ require 'logger'
3
+ require 'fileutils'
4
+ require 'drb'
5
+ require 'drb/acl'
6
+ require 'rinda/tuplespace'
7
+ require 'rinda/rinda'
8
+
9
+ module DRbQS
10
+ autoload :Server, 'drbqs/server'
11
+ autoload :Client, 'drbqs/client'
12
+ end
@@ -0,0 +1,44 @@
1
+ require 'drbqs/connection'
2
+ require 'drbqs/task_client'
3
+
4
+ module DRbQS
5
+
6
+ class Client
7
+ def initialize(access_uri, opts = {})
8
+ @access_uri = access_uri
9
+ @logger = Logger.new(opts[:log_file] || 'drbqs_client.log')
10
+ @logger.level = opts[:log_level] || Logger::ERROR
11
+ @connection = nil
12
+ @task_client = nil
13
+ end
14
+
15
+ def connect
16
+ obj = DRbObject.new_with_uri(@access_uri)
17
+ @connection = ConnectionClient.new(obj[:message], @logger)
18
+ node_id = @connection.get_id
19
+ @task_client = TaskClient.new(node_id, obj[:queue], obj[:result], @logger)
20
+ end
21
+
22
+ def calculate
23
+ cn = Thread.new do
24
+ loop do
25
+ @task_client.add_new_task
26
+ @connection.respond_alive_signal
27
+ @task_client.send_result
28
+ sleep(1)
29
+ end
30
+ end
31
+ exec = Thread.new do
32
+ loop do
33
+ marshal_obj, method_sym, args = @task_client.get
34
+ obj = Marshal.load(marshal_obj)
35
+ result = obj.__send__(method_sym, *args)
36
+ @task_client.transmit(result)
37
+ end
38
+ end
39
+ exec.join
40
+ cn.join
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,35 @@
1
+ module DRbQS
2
+ # The class of connection to s.erver.
3
+ class ConnectionClient
4
+ def initialize(message, logger = nil)
5
+ @message = message
6
+ @logger = logger
7
+ @id = nil
8
+ end
9
+
10
+ def create_id_string
11
+ t = Time.now
12
+ sprintf("%d%d%d", t.to_i, t.usec, rand(1000))
13
+ end
14
+ private :create_id_string
15
+
16
+ def get_id
17
+ s = create_id_string
18
+ @message.write([:connect, s])
19
+ @id = @message.take([s, Fixnum])[1]
20
+ end
21
+
22
+ def respond_alive_signal
23
+ begin
24
+ node_id, sym = @message.take([@id, Symbol], 0)
25
+ case sym
26
+ when :alive_p
27
+ @message.write([:alive, @id])
28
+ when :exit
29
+ Kernel.exit
30
+ end
31
+ rescue
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,92 @@
1
+ module DRbQS
2
+ class NodeList
3
+ def initialize
4
+ @id = 0
5
+ @list = {}
6
+ @check = []
7
+ end
8
+
9
+ def get_new_id(id_str)
10
+ @id += 1
11
+ @list[@id] = id_str
12
+ @id
13
+ end
14
+
15
+ def each(&block)
16
+ @list.each(&block)
17
+ end
18
+
19
+ def set_check_connection
20
+ @check = @list.keys
21
+ end
22
+
23
+ def delete_not_alive
24
+ @check.each do |id|
25
+ @list.delete(id)
26
+ end
27
+ deleted = @check
28
+ @check = []
29
+ deleted
30
+ end
31
+
32
+ def set_alive(id)
33
+ @check.delete(id)
34
+ end
35
+
36
+ def empty?
37
+ @list.size == 0
38
+ end
39
+ end
40
+
41
+ class MessageServer
42
+ def initialize(message, logger = nil)
43
+ @message = message
44
+ @node_list = NodeList.new
45
+ @logger = logger
46
+ end
47
+
48
+ def get_message
49
+ begin
50
+ mes = @message.take([Symbol, nil], 0)
51
+ manage_message(*mes)
52
+ rescue Rinda::RequestExpiredError
53
+ end
54
+ end
55
+
56
+ def manage_message(mes, arg)
57
+ @logger.debug("Get message") { [mes, arg] } if @logger
58
+ case mes
59
+ when :connect
60
+ a = [arg, @node_list.get_new_id(arg)]
61
+ @logger.debug("New node") { a } if @logger
62
+ @message.write(a)
63
+ when :alive
64
+ @node_list.set_alive(arg)
65
+ else
66
+ puts "Invalid message from #{arg.to_s}"
67
+ end
68
+ end
69
+ private :manage_message
70
+
71
+ def check_connection
72
+ deleted = @node_list.delete_not_alive
73
+ @logger.info("IDs of deleted nodes") { deleted } if @logger
74
+ @node_list.each do |id, str|
75
+ @message.write([id, :alive_p])
76
+ end
77
+ @node_list.set_check_connection
78
+ deleted
79
+ end
80
+
81
+ def send_exit
82
+ @node_list.each do |node_id, id_str|
83
+ @message.write([node_id, :exit])
84
+ end
85
+ end
86
+
87
+ def node_not_exist?
88
+ @node_list.empty?
89
+ end
90
+ end
91
+
92
+ end
@@ -0,0 +1,96 @@
1
+ module DRbQS
2
+ class Task
3
+ attr_reader :hook
4
+
5
+ def initialize(obj, method_sym, args = [], &hook)
6
+ begin
7
+ @marshal_obj = Marshal.dump(obj)
8
+ rescue
9
+ raise "Can not dump an instance of #{obj.class}."
10
+ end
11
+ @method_sym = method_sym
12
+ @args = args
13
+ @hook = hook
14
+ end
15
+
16
+ def drb_args(task_id)
17
+ [task_id, @marshal_obj, @method_sym, @args]
18
+ end
19
+ end
20
+
21
+ class QueueServer
22
+
23
+ def initialize(queue, result, logger = nil)
24
+ @queue = queue
25
+ @result = result
26
+ @task_id = 0
27
+ @cache = {}
28
+ @calculating = {}
29
+ @logger = logger
30
+ end
31
+
32
+ def queue_task(task_id)
33
+ @queue.write(@cache[task_id].drb_args(task_id))
34
+ end
35
+ private :queue_task
36
+
37
+ # &hook take two arguments: a QueueServer object and a result of task.
38
+ def add(task)
39
+ @task_id += 1
40
+ @logger.info("New task: #{@task_id}") if @logger
41
+ @cache[@task_id] = task
42
+ queue_task(@task_id)
43
+ end
44
+
45
+ def get_accept_signal
46
+ begin
47
+ sym, task_id, node_id = @result.take([:accept, Fixnum, Fixnum], 0)
48
+ @calculating[node_id] = task_id
49
+ @logger.info("Accept: task #{task_id} by node #{node_id}.") if @logger
50
+ rescue
51
+ end
52
+ end
53
+
54
+ def requeue_for_deleted_node_id(deleted)
55
+ deleted.each do |node_id|
56
+ if task_id = @calculating[node_id]
57
+ queue_task(task_id)
58
+ @calculating.delete(task_id)
59
+ end
60
+ end
61
+ end
62
+
63
+ def get_result
64
+ begin
65
+ loop do
66
+ sym, task_id, result = @result.take([:result, Fixnum, nil], 0)
67
+ @logger.info("Get: result of #{task_id}.") if @logger
68
+ @calculating.delete(task_id)
69
+ task = @cache.delete(task_id)
70
+ if hook = task.hook
71
+ hook.call(self, result)
72
+ end
73
+ end
74
+ rescue
75
+ end
76
+ end
77
+
78
+ # If queue is empty, return true. Otherwise, false.
79
+ # Even if there are calculating tasks,
80
+ # the method can return true.
81
+ def empty?
82
+ @cache.size - @calculating.size == 0
83
+ end
84
+
85
+ # If there are no tasks in queue and calculating,
86
+ # return true. Otherwise, false.
87
+ def finished?
88
+ @cache.size == 0
89
+ end
90
+
91
+ def calculating_task_number
92
+ @calculating.size
93
+ end
94
+ end
95
+
96
+ end
@@ -0,0 +1,106 @@
1
+ require 'drbqs/message'
2
+ require 'drbqs/queue'
3
+
4
+ module DRbQS
5
+ class CheckAlive
6
+ def initialize(interval)
7
+ @interval = interval || 300
8
+ @last = Time.now
9
+ end
10
+
11
+ def significant_interval?
12
+ (Time.now - @last) >= @interval
13
+ end
14
+
15
+ def set_checking
16
+ @last = Time.now
17
+ end
18
+ end
19
+
20
+ ROOT_DEFAULT_PORT = 13500
21
+
22
+ class Server
23
+ attr_reader :queue
24
+
25
+ def initialize(opts = {})
26
+ @port = opts[:port] || ROOT_DEFAULT_PORT
27
+ @acl = opts[:acl]
28
+ @ts = {
29
+ :message => Rinda::TupleSpace.new,
30
+ :queue => Rinda::TupleSpace.new,
31
+ :result => Rinda::TupleSpace.new
32
+ }
33
+ @logger = Logger.new(opts[:log_file] || 'drbqs_server.log')
34
+ @logger.level = opts[:log_level] || Logger::ERROR
35
+ @message = MessageServer.new(@ts[:message], @logger)
36
+ @queue= QueueServer.new(@ts[:queue], @ts[:result], @logger)
37
+ @check_alive = CheckAlive.new(opts[:check_alive])
38
+ @empty_queue_hook = nil
39
+ end
40
+
41
+ def start
42
+ DRb.install_acl(@acl) if @acl
43
+ uri = "druby://:#{@port}"
44
+ DRb.start_service(uri, @ts)
45
+ @logger.info("Start DRb service") { uri } if @logger
46
+ end
47
+
48
+ def check_connection(force = nil)
49
+ if force || @check_alive.significant_interval?
50
+ @logger.info("Check connection") if @logger
51
+ @message.check_connection
52
+ @check_alive.set_checking
53
+ end
54
+ end
55
+ private :check_connection
56
+
57
+ def set_empty_queue_hook(&block)
58
+ if block_given?
59
+ @empty_queue_hook = block
60
+ else
61
+ @empty_queue_hook = nil
62
+ end
63
+ end
64
+
65
+ def set_finish_hook(&block)
66
+ if block_given?
67
+ @finish_hook = block
68
+ else
69
+ @finish_hook = nil
70
+ end
71
+ end
72
+
73
+ def exec_hook
74
+ if @empty_queue_hook && @queue.empty?
75
+ @logger.info("Execute empty queue hook.") if @logger
76
+ @empty_queue_hook.call(self)
77
+ end
78
+ if @finish_hook && @queue.finished?
79
+ @logger.info("Execute finish hook.") if @logger
80
+ @finish_hook.call(self)
81
+ end
82
+ end
83
+ private :exec_hook
84
+
85
+ WAIT_NODE_EXIT = 3
86
+
87
+ def exit
88
+ @message.send_exit
89
+ until @message.node_not_exist?
90
+ sleep(WAIT_NODE_EXIT)
91
+ check_connection(true)
92
+ end
93
+ Kernel.exit
94
+ end
95
+
96
+ def wait
97
+ loop do
98
+ @message.get_message
99
+ check_connection
100
+ @queue.get_result
101
+ exec_hook
102
+ sleep(1)
103
+ end
104
+ end
105
+ end
106
+ end