asynk 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +3 -0
  4. data/Gemfile.lock +172 -0
  5. data/README.md +168 -0
  6. data/asynk.gemspec +27 -0
  7. data/bin/asynk +12 -0
  8. data/bin/asynkctl +93 -0
  9. data/consumer.rb +52 -0
  10. data/lib/asynk/benchmark.rb +21 -0
  11. data/lib/asynk/broker.rb +33 -0
  12. data/lib/asynk/cli.rb +146 -0
  13. data/lib/asynk/config.rb +33 -0
  14. data/lib/asynk/consumer.rb +98 -0
  15. data/lib/asynk/logging.rb +97 -0
  16. data/lib/asynk/message.rb +30 -0
  17. data/lib/asynk/publisher.rb +28 -0
  18. data/lib/asynk/response.rb +57 -0
  19. data/lib/asynk/server.rb +62 -0
  20. data/lib/asynk/sync_publisher.rb +36 -0
  21. data/lib/asynk/test_helper.rb +52 -0
  22. data/lib/asynk/version.rb +3 -0
  23. data/lib/asynk/worker.rb +56 -0
  24. data/lib/asynk.rb +65 -0
  25. data/myapp/.gitignore +17 -0
  26. data/myapp/Gemfile +16 -0
  27. data/myapp/Gemfile.lock +175 -0
  28. data/myapp/README.rdoc +28 -0
  29. data/myapp/Rakefile +6 -0
  30. data/myapp/app/assets/images/.keep +0 -0
  31. data/myapp/app/assets/javascripts/application.js +16 -0
  32. data/myapp/app/assets/stylesheets/application.css +15 -0
  33. data/myapp/app/consumers/wallet_events_consumer.rb +22 -0
  34. data/myapp/app/controllers/application_controller.rb +5 -0
  35. data/myapp/app/controllers/concerns/.keep +0 -0
  36. data/myapp/app/helpers/application_helper.rb +2 -0
  37. data/myapp/app/mailers/.keep +0 -0
  38. data/myapp/app/models/.keep +0 -0
  39. data/myapp/app/models/concerns/.keep +0 -0
  40. data/myapp/app/models/user.rb +2 -0
  41. data/myapp/app/views/layouts/application.html.erb +14 -0
  42. data/myapp/bin/bundle +3 -0
  43. data/myapp/bin/rails +8 -0
  44. data/myapp/bin/rake +8 -0
  45. data/myapp/bin/setup +29 -0
  46. data/myapp/bin/spring +15 -0
  47. data/myapp/config/application.rb +26 -0
  48. data/myapp/config/boot.rb +3 -0
  49. data/myapp/config/database.yml +32 -0
  50. data/myapp/config/environment.rb +5 -0
  51. data/myapp/config/environments/development.rb +41 -0
  52. data/myapp/config/environments/production.rb +79 -0
  53. data/myapp/config/environments/test.rb +42 -0
  54. data/myapp/config/initializers/assets.rb +11 -0
  55. data/myapp/config/initializers/backtrace_silencers.rb +7 -0
  56. data/myapp/config/initializers/cookies_serializer.rb +3 -0
  57. data/myapp/config/initializers/filter_parameter_logging.rb +4 -0
  58. data/myapp/config/initializers/inflections.rb +16 -0
  59. data/myapp/config/initializers/mime_types.rb +4 -0
  60. data/myapp/config/initializers/session_store.rb +3 -0
  61. data/myapp/config/initializers/wrap_parameters.rb +14 -0
  62. data/myapp/config/locales/en.yml +23 -0
  63. data/myapp/config/routes.rb +56 -0
  64. data/myapp/config/secrets.yml +22 -0
  65. data/myapp/config.ru +4 -0
  66. data/myapp/db/migrate/20150826104429_create_users.rb +10 -0
  67. data/myapp/db/schema.rb +26 -0
  68. data/myapp/db/seeds.rb +7 -0
  69. data/myapp/lib/assets/.keep +0 -0
  70. data/myapp/lib/tasks/.keep +0 -0
  71. data/myapp/log/.keep +0 -0
  72. data/myapp/public/404.html +67 -0
  73. data/myapp/public/422.html +67 -0
  74. data/myapp/public/500.html +66 -0
  75. data/myapp/public/favicon.ico +0 -0
  76. data/myapp/public/robots.txt +5 -0
  77. data/myapp/test/controllers/.keep +0 -0
  78. data/myapp/test/fixtures/.keep +0 -0
  79. data/myapp/test/fixtures/users.yml +9 -0
  80. data/myapp/test/helpers/.keep +0 -0
  81. data/myapp/test/integration/.keep +0 -0
  82. data/myapp/test/mailers/.keep +0 -0
  83. data/myapp/test/models/.keep +0 -0
  84. data/myapp/test/models/user_test.rb +7 -0
  85. data/myapp/test/test_helper.rb +10 -0
  86. data/myapp/vendor/assets/javascripts/.keep +0 -0
  87. data/myapp/vendor/assets/stylesheets/.keep +0 -0
  88. data/publisher.rb +15 -0
  89. data/test/consumer_example.rb +31 -0
  90. data/test/consumer_testing_example_test.rb +21 -0
  91. data/test/test_helper.rb +7 -0
  92. metadata +271 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b6ead08e1f2e9906b0871dd76ba54dd493c3873b
4
+ data.tar.gz: 872bbb7241da16c78dd5e54200d76f396852fd46
5
+ SHA512:
6
+ metadata.gz: d60bf2cafe43ce3ff502459ec7d0ddb007e45257387eb3e3078c1af22bf0ad6dfc6f5103b5ed4f955a1a7fa16a7f96d933577bf243c3aa1d1306e656404b25f9
7
+ data.tar.gz: 30a07b0855bf08a7086914a9189f294ea61850fe74bde41868d605d58f1c806bd61880f0ea9cf5c84ce67083c3261265d2efa4fe95a3abef2fd09496502964ca
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore the default SQLite database.
11
+ /db/*.sqlite3
12
+ /db/*.sqlite3-journal
13
+
14
+ # Ignore all logfiles and tempfiles.
15
+ /log/*
16
+ /log/.keep
17
+ /tmp
18
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,172 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ asynk (0.0.1)
5
+ activesupport
6
+ bunny
7
+ celluloid (~> 0.17.0)
8
+ celluloid-io
9
+ connection_pool
10
+ json (~> 1.0)
11
+
12
+ GEM
13
+ remote: http://rubygems.org/
14
+ specs:
15
+ actionmailer (4.2.3)
16
+ actionpack (= 4.2.3)
17
+ actionview (= 4.2.3)
18
+ activejob (= 4.2.3)
19
+ mail (~> 2.5, >= 2.5.4)
20
+ rails-dom-testing (~> 1.0, >= 1.0.5)
21
+ actionpack (4.2.3)
22
+ actionview (= 4.2.3)
23
+ activesupport (= 4.2.3)
24
+ rack (~> 1.6)
25
+ rack-test (~> 0.6.2)
26
+ rails-dom-testing (~> 1.0, >= 1.0.5)
27
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
28
+ actionview (4.2.3)
29
+ activesupport (= 4.2.3)
30
+ builder (~> 3.1)
31
+ erubis (~> 2.7.0)
32
+ rails-dom-testing (~> 1.0, >= 1.0.5)
33
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
34
+ activejob (4.2.3)
35
+ activesupport (= 4.2.3)
36
+ globalid (>= 0.3.0)
37
+ activemodel (4.2.3)
38
+ activesupport (= 4.2.3)
39
+ builder (~> 3.1)
40
+ activerecord (4.2.3)
41
+ activemodel (= 4.2.3)
42
+ activesupport (= 4.2.3)
43
+ arel (~> 6.0)
44
+ activesupport (4.2.3)
45
+ i18n (~> 0.7)
46
+ json (~> 1.7, >= 1.7.7)
47
+ minitest (~> 5.1)
48
+ thread_safe (~> 0.3, >= 0.3.4)
49
+ tzinfo (~> 1.1)
50
+ amq-protocol (2.0.0)
51
+ arel (6.0.3)
52
+ builder (3.2.2)
53
+ bunny (2.2.0)
54
+ amq-protocol (>= 2.0.0)
55
+ celluloid (0.17.1.2)
56
+ bundler
57
+ celluloid-essentials
58
+ celluloid-extras
59
+ celluloid-fsm
60
+ celluloid-pool
61
+ celluloid-supervision
62
+ dotenv
63
+ nenv
64
+ rspec-logsplit (>= 0.1.2)
65
+ timers (>= 4.1.1)
66
+ celluloid-essentials (0.20.2.1)
67
+ bundler
68
+ dotenv
69
+ nenv
70
+ rspec-logsplit (>= 0.1.2)
71
+ timers (>= 4.1.1)
72
+ celluloid-extras (0.20.1)
73
+ bundler
74
+ dotenv
75
+ nenv
76
+ rspec-logsplit (>= 0.1.2)
77
+ timers (>= 4.1.1)
78
+ celluloid-fsm (0.20.1)
79
+ bundler
80
+ dotenv
81
+ nenv
82
+ rspec-logsplit (>= 0.1.2)
83
+ timers (>= 4.1.1)
84
+ celluloid-io (0.17.1)
85
+ bundler
86
+ celluloid (>= 0.17.1.1)
87
+ dotenv
88
+ nenv
89
+ nio4r (>= 1.1)
90
+ rspec-logsplit (>= 0.1.2)
91
+ timers (>= 4.1.1)
92
+ celluloid-pool (0.20.1)
93
+ bundler
94
+ dotenv
95
+ nenv
96
+ rspec-logsplit (>= 0.1.2)
97
+ timers (>= 4.1.1)
98
+ celluloid-supervision (0.20.1.1)
99
+ bundler
100
+ dotenv
101
+ nenv
102
+ rspec-logsplit (>= 0.1.2)
103
+ timers (>= 4.1.1)
104
+ connection_pool (2.2.0)
105
+ dotenv (2.0.2)
106
+ erubis (2.7.0)
107
+ globalid (0.3.6)
108
+ activesupport (>= 4.1.0)
109
+ hitimes (1.2.2)
110
+ i18n (0.7.0)
111
+ json (1.8.3)
112
+ loofah (2.0.3)
113
+ nokogiri (>= 1.5.9)
114
+ mail (2.6.3)
115
+ mime-types (>= 1.16, < 3)
116
+ mime-types (2.6.1)
117
+ mini_portile (0.6.2)
118
+ minitest (5.8.0)
119
+ nenv (0.2.0)
120
+ nio4r (1.1.1)
121
+ nokogiri (1.6.6.2)
122
+ mini_portile (~> 0.6.0)
123
+ rack (1.6.4)
124
+ rack-test (0.6.3)
125
+ rack (>= 1.0)
126
+ rails (4.2.3)
127
+ actionmailer (= 4.2.3)
128
+ actionpack (= 4.2.3)
129
+ actionview (= 4.2.3)
130
+ activejob (= 4.2.3)
131
+ activemodel (= 4.2.3)
132
+ activerecord (= 4.2.3)
133
+ activesupport (= 4.2.3)
134
+ bundler (>= 1.3.0, < 2.0)
135
+ railties (= 4.2.3)
136
+ sprockets-rails
137
+ rails-deprecated_sanitizer (1.0.3)
138
+ activesupport (>= 4.2.0.alpha)
139
+ rails-dom-testing (1.0.7)
140
+ activesupport (>= 4.2.0.beta, < 5.0)
141
+ nokogiri (~> 1.6.0)
142
+ rails-deprecated_sanitizer (>= 1.0.1)
143
+ rails-html-sanitizer (1.0.2)
144
+ loofah (~> 2.0)
145
+ railties (4.2.3)
146
+ actionpack (= 4.2.3)
147
+ activesupport (= 4.2.3)
148
+ rake (>= 0.8.7)
149
+ thor (>= 0.18.1, < 2.0)
150
+ rake (10.4.2)
151
+ rspec-logsplit (0.1.3)
152
+ sprockets (3.3.3)
153
+ rack (~> 1.0)
154
+ sprockets-rails (2.3.2)
155
+ actionpack (>= 3.0)
156
+ activesupport (>= 3.0)
157
+ sprockets (>= 2.8, < 4.0)
158
+ thor (0.19.1)
159
+ thread_safe (0.3.5)
160
+ timers (4.1.1)
161
+ hitimes
162
+ tzinfo (1.2.2)
163
+ thread_safe (~> 0.1)
164
+
165
+ PLATFORMS
166
+ ruby
167
+
168
+ DEPENDENCIES
169
+ asynk!
170
+ minitest (~> 5.7, >= 5.7.0)
171
+ rails (~> 4)
172
+ rake (~> 10.0)
data/README.md ADDED
@@ -0,0 +1,168 @@
1
+ # Asynk
2
+
3
+ Asynk is a Ruby library for enabling synchronous/asynchronous inter-service communication, using RabbitMQ.
4
+
5
+ # Overview
6
+
7
+ It's takes concepts of ruby gem called Hutch, but using Cellouid under hood for creating workers for processing queues, which requires significant memory requirements.
8
+ Also as limitation of ruby you cannot make heavy computations in ruby and gather true concurrency. Asynk offers synchronous calls (RPC).
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'asynk'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install asynk
23
+
24
+ ## Usage
25
+
26
+
27
+ Firstly you should define consumer, Example of consumer
28
+
29
+ ```ruby
30
+ class V1::PaymentOrdersConsumer
31
+ include Asynk::Consumer
32
+
33
+ set_consume 'sample_app.v1.users.create', 'sample_app.v1.users.notifications'
34
+
35
+ set_queue_options ack: true
36
+ set_subscribe_arguments manual_ack: true
37
+ set_concurrency 2
38
+ set_route_ending_as_action true
39
+
40
+ # handling asynchronous request from amqp
41
+ def notifications(params)
42
+ # do here some works
43
+ ensure
44
+ ack! # required if you manually processing acknowledgments, available methods reject!, requeue!
45
+ end
46
+
47
+ # example for handling synchronous request
48
+ def create(params)
49
+ result = my_method # here we doing some work, and saving result of
50
+ result = Asynk::Response.new(status: :ok, body: result) # result should any object which implements to_json.
51
+ respond(result) # if producer expecting response, we should response with some data.
52
+ # Is not required to use any format of response. But preferred way is to use class Asynk::Response,
53
+ # which used as lightweight implementation of http. (Containing body, status, and error_message).
54
+ ensure
55
+ ack!
56
+ end
57
+ end
58
+ ```
59
+
60
+ Firstly you should define Class, and include Asynk::Consumer.
61
+ * `set_consume` list of topics to consume
62
+ * `set_queue_options` set for options which created after server initialized
63
+ * `set_subscribe_arguments` set options which passed to when subscribing queue to exchange
64
+ * `set_concurrency` amount of workers which will consume for each consumer
65
+ * `set_route_ending_as_action` this options defines, that last item of consume topic is used as method name (ex: sample_app.v1.users.show, last item show, will be called in this class.)
66
+
67
+ Also, after instance creation, has several methods,
68
+ * `log` logger object for sending logs for default Logger of Asynk.
69
+ * `ack!`, `reject!`, `requeue!` methods is used for handling messages, if you want manually work with acknowledgments
70
+
71
+
72
+
73
+ After declaring consumer, now you can send some request using default Asynk::Publisher, it has two options:
74
+ * `sync_publish` synchronous sending of message, and waiting for response from consumer. You should define timeouts, if consumers crashes it never receives a message. timeout - says the amount of time in seconds for waiting message, if timeout reaches it sends TimeoutError.
75
+ * `publish` just sending message, and forgets about it.
76
+
77
+ ```ruby
78
+ # here sending synchronous messages
79
+ Asynk::Publisher.sync_publish('sample_app.v1.users.create', { name: 'Tom', surname: 'Lane', timeout: 10 })
80
+
81
+ # here sending asynchronous messages
82
+ Asynk::Publisher.publish('sample_app.v1.users.notifications', { name: 'Tom', surname: 'Lane' })
83
+ ```
84
+
85
+
86
+ Asynk is using pool for channels for receiving and sending messages.
87
+ Usage in rails, should initialize authentication data and init connection to RabbitMQ server.
88
+
89
+ ```ruby
90
+ # Init authentication
91
+ Asynk.config[:mq_host] = ENV['MQ_HOST']
92
+ Asynk.config[:mq_username] = ENV['MQ_USERNAME']
93
+ Asynk.config[:mq_password] = ENV['MQ_PASSWORD']
94
+
95
+ # Init connection to server.
96
+ Asynk::Broker.connect
97
+
98
+ # here we initializing logger.
99
+ if Rails.env.stage? || Rails.env.production?
100
+ if Asynk.booted_inside? # if we booting inside asynk, it's separate process than rails server.
101
+ Asynk.logger.level = ::Logger::DEBUG
102
+ else # If we booted inside rails project, we can use rails's logger.
103
+ Asynk.logger = Rails.logger
104
+ end
105
+ else
106
+ Asynk.logger.level = ::Logger::INFO
107
+ Asynk.config[:publisher_execution_time] = false
108
+ end
109
+
110
+ ```
111
+ ## Config options
112
+ * `mq_exchange` exchange to use for publishing (`default` 'asynk_exchange_topic')
113
+ * `sync_publish_wait_timeout` time to wait for sync requests. If timeout reaches, the timeout raised. (`default` 10 seconds)
114
+ * `default_consumer_concurrency` numbers of workers to start per consumer (`default` 1)
115
+ * `logfile` log file for default logger of asynk (`default` 'log/asynk.log')
116
+ * `pidifle` Asynk consumers is running on different process, this file is used to store pid file (`default` 'tmp/pids/asynk.pid')
117
+ * `mq_host` host for connection to broker (RabbitMQ) (`default` 'localhost')
118
+ * `mq_port` port for connection to broker (RabbitMQ) (`default` 5672)
119
+ * `mq_vhost` vhost for connection to broker (RabbitMQ) (`default` '/')
120
+ * `mq_username` username for connection to broker (RabbitMQ) (`default` 'guest')
121
+ * `mq_password` password for connection to broker (RabbitMQ) (`default` 'guest')
122
+ * `publisher_execution_time` used for profiling time to send when using Asynk::Publisher (`default` true)
123
+ * `respond_back_execution_time` used for profiling time used for processing sync response (`default` true)
124
+ * `ignored_consumers` this parameter used for disabling unused consumers as array of strings with consumer class names(`default` [])
125
+
126
+
127
+ # Testing your consumers
128
+ Firstly you should include `Asynk::TestHelper` to your test class, and then call `sync_publish` method for sending request, if this is rpc call,
129
+ invoke the asynk_response method for getting response.
130
+
131
+ Example using with Rails and MiniTest.
132
+ ```ruby
133
+ # test_helper.rb
134
+ class ActiveSupport::TestCase
135
+ # include the test helper.
136
+ include Asynk::TestHelper
137
+
138
+ # wrapping the response with Asynk::Response class, otherwise it will be just string value.
139
+ def asynk_response
140
+ Asynk::Response.try_to_create_from_hash(super)
141
+ end
142
+ end
143
+
144
+
145
+ # some_consumer_test.rb
146
+
147
+ test 'should show profile' do
148
+ publish_sync 'some_route', { name: 'Chris' }
149
+
150
+ assert asynk_response.success? # testing for status of the response
151
+ assert asynk_response[:unread_messages] # testing the returned data
152
+ assert asynk_response[:unread_message_count]
153
+ end
154
+ ```
155
+
156
+ ## Known problems
157
+
158
+ * Poor documentation (source are poorly documented)
159
+ * Poor test coverage (there are almost no test)
160
+ * RPC calls implementation. Currently is implemented as continues loop, which tries get data from reply queue. Before it was implemented using Mutex, which caused huge time usage on handling them. I am not sure that current implementation is correct, but is id much faster in current tests. (On my machine 1-2 ms vs 7-8 ms).
161
+
162
+ ## Contributing
163
+
164
+ 1. Fork it ( https://github.com/[my-github-username]/gm_server/fork )
165
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
166
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
167
+ 4. Push to the branch (`git push origin my-new-feature`)
168
+ 5. Create a new Pull Request
data/asynk.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/asynk/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Danil Nurgaliev"]
6
+ gem.email = ["jkonalegi@gmail.com"]
7
+ gem.summary = "Async/sync inter sevrer communication tool."
8
+ gem.description = "Async/sync inter sevrer communication tool, based on RabbitMQ and Celluloid"
9
+ gem.license = "LGPL-3.0"
10
+ gem.homepage = "https://github.com/konalegi/asynk"
11
+
12
+ gem.files = `git ls-files -z`.split("\x0")
13
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "asynk"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Asynk::VERSION
18
+ gem.add_dependency 'celluloid', '~> 0.17'
19
+ gem.add_dependency 'celluloid-io', '~> 0.17'
20
+ gem.add_dependency 'connection_pool', '~> 2.2'
21
+ gem.add_dependency 'activesupport', '~> 4.2'
22
+ gem.add_dependency 'json', '~> 1.0'
23
+ gem.add_dependency 'bunny', '~> 2.3'
24
+ gem.add_development_dependency 'minitest', '~> 5.7', '>= 5.7.0'
25
+ gem.add_development_dependency 'rake', '~> 10.0'
26
+ gem.add_development_dependency 'rails', '~> 4'
27
+ end
data/bin/asynk ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/asynk/cli'
4
+
5
+ begin
6
+ cli = Asynk::CLI.instance
7
+ cli.run
8
+ rescue => e
9
+ STDERR.puts e.message
10
+ STDERR.puts e.backtrace.join("\n")
11
+ exit 1
12
+ end
data/bin/asynkctl ADDED
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+
5
+ class Asynkctl
6
+ DEFAULT_KILL_TIMEOUT = 10
7
+
8
+ attr_reader :stage, :pidfile, :kill_timeout
9
+
10
+ def self.print_usage
11
+ puts "#{File.basename($0)} - stop a Asynk process from the command line."
12
+ puts
13
+ puts "Usage: #{File.basename($0)} <command> <pidfile> <kill_timeout>"
14
+ puts " where <command> is either 'quiet' or 'stop'"
15
+ puts " <pidfile> is path to a pidfile"
16
+ puts " <kill_timeout> is number of seconds to wait until Asynk exits"
17
+ puts " (default: #{Asynkctl::DEFAULT_KILL_TIMEOUT}), after which Asynk will be KILL'd"
18
+ puts
19
+ puts "Be sure to set the kill_timeout LONGER than Asynk's -t timeout. If you want"
20
+ puts "to wait 60 seconds for jobs to finish, use `Asynk -t 60` and `Asynkctl stop"
21
+ puts " path_to_pidfile 61`"
22
+ puts
23
+ end
24
+
25
+ def initialize(stage, pidfile, timeout)
26
+ @stage = stage
27
+ @pidfile = pidfile
28
+ @kill_timeout = timeout
29
+
30
+ done('No pidfile given', :error) if !pidfile
31
+ done("Pidfile #{pidfile} does not exist", :warn) if !File.exist?(pidfile)
32
+ done('Invalid pidfile content', :error) if pid == 0
33
+
34
+ fetch_process
35
+
36
+ begin
37
+ send(stage)
38
+ rescue NoMethodError
39
+ done "Invalid command: #{stage}", :error
40
+ end
41
+ end
42
+
43
+ def fetch_process
44
+ Process.getpgid(pid)
45
+ rescue Errno::ESRCH
46
+ done "Process doesn't exist", :error
47
+ end
48
+
49
+ def done(msg, error = nil)
50
+ puts msg
51
+ exit(exit_signal(error))
52
+ end
53
+
54
+ def exit_signal(error)
55
+ (error == :error) ? 1 : 0
56
+ end
57
+
58
+ def pid
59
+ @pid ||= File.read(pidfile).to_i
60
+ end
61
+
62
+ def quiet
63
+ Process.kill(:USR1, pid)
64
+ end
65
+
66
+ def stop
67
+ Process.kill(:TERM, pid)
68
+ kill_timeout.times do
69
+ begin
70
+ Process.getpgid(pid)
71
+ rescue Errno::ESRCH
72
+ FileUtils.rm_f pidfile
73
+ done 'Asynk shut down gracefully.'
74
+ end
75
+ sleep 1
76
+ end
77
+ Process.kill(:KILL, pid)
78
+ FileUtils.rm_f pidfile
79
+ done 'Asynk shut down forcefully.'
80
+ end
81
+ alias_method :shutdown, :stop
82
+ end
83
+
84
+ if ARGV.length < 2
85
+ Asynkctl.print_usage
86
+ else
87
+ stage = ARGV[0]
88
+ pidfile = ARGV[1]
89
+ timeout = ARGV[2].to_i
90
+ timeout = Asynkctl::DEFAULT_KILL_TIMEOUT if timeout == 0
91
+
92
+ Asynkctl.new(stage, pidfile, timeout)
93
+ end
data/consumer.rb ADDED
@@ -0,0 +1,52 @@
1
+ require 'celluloid'
2
+ require 'celluloid/io'
3
+ require 'celluloid/autostart'
4
+ require 'bunny'
5
+
6
+ class Worker
7
+ include Celluloid::IO
8
+ finalizer :shutdown
9
+
10
+ def initialize(conn)
11
+ @ch = conn.create_channel
12
+ q = @ch.queue('task_queue', :durable => true)
13
+ q.subscribe manual_ack: true, &method(:on_event)
14
+ p 'worker accepting connections'
15
+ end
16
+
17
+ def on_event(delivery_info, properties, body)
18
+ p "#{Time.now.strftime('%FT %T.%L')} started work: #{body}"
19
+ sleep(10)
20
+ p "#{Time.now.strftime('%FT %T.%L')} completed work: #{body}"
21
+ @ch.ack(delivery_info.delivery_tag)
22
+ end
23
+
24
+ def shutdown
25
+ p 'trying to shutdown...'
26
+ @ch.close
27
+ p 'hey shutdown'
28
+ end
29
+ end
30
+
31
+ class Consumer
32
+ def initialize(options = {})
33
+ @size = (ENV['POOL'] || 2).to_i
34
+ @connection = Bunny.new(options)
35
+ @connection.start
36
+ @workers = @size.times.map{ Worker.new(@connection) }
37
+ end
38
+
39
+ def close_connection
40
+ futures = @workers.map { |w| w.future(:finalize) }
41
+ @connection.close if futures.all?
42
+ end
43
+ end
44
+
45
+ begin
46
+ @consumer = Consumer.new
47
+ rescue Interrupt => _
48
+ @consumer.close_connection
49
+ end
50
+
51
+ sleep
52
+
@@ -0,0 +1,21 @@
1
+ module Asynk
2
+ class Benchmark
3
+ class << self
4
+
5
+ def measure_around(&block)
6
+ start_time = Time.now.to_f
7
+ block.call
8
+ ((Time.now.to_f - start_time)*1000).round(2)
9
+ end
10
+
11
+ def start
12
+ Time.now.to_f
13
+ end
14
+
15
+ def end(start_time)
16
+ ((Time.now.to_f - start_time)*1000).round(2)
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,33 @@
1
+ # require 'carrot-top'
2
+
3
+ module Asynk
4
+ class Broker
5
+ class << self
6
+ def connect
7
+ @amqp_connection = Bunny.new(host: Asynk.config[:mq_host],
8
+ port: Asynk.config[:mq_port],
9
+ username: Asynk.config[:mq_username],
10
+ password: Asynk.config[:mq_password],
11
+ vhost: Asynk.config[:mq_vhost])
12
+ Asynk.logger.info [ "Connection to Rabbit with params host: #{Asynk.config[:mq_host]}:#{Asynk.config[:mq_port]}",
13
+ "username: '#{Asynk.config[:mq_username]}' ", "vhost: '#{Asynk.config[:mq_vhost]}'"
14
+ ].join(' ')
15
+
16
+ @amqp_connection.start
17
+
18
+ @pool = ConnectionPool.new(size: 10, timeout: 5) do
19
+ channel = @amqp_connection.create_channel(nil, nil)
20
+ [channel, channel.topic(Asynk.config[:mq_exchange]), channel.queue('', exclusive: true)]
21
+ end
22
+ end
23
+
24
+ def disconnect
25
+ @amqp_connection.close if @amqp_connection
26
+ @amqp_connection = nil
27
+ end
28
+
29
+ def pool; @pool; end
30
+ def amqp_connection; @amqp_connection; end
31
+ end
32
+ end
33
+ end