asynk 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +172 -0
- data/README.md +168 -0
- data/asynk.gemspec +27 -0
- data/bin/asynk +12 -0
- data/bin/asynkctl +93 -0
- data/consumer.rb +52 -0
- data/lib/asynk/benchmark.rb +21 -0
- data/lib/asynk/broker.rb +33 -0
- data/lib/asynk/cli.rb +146 -0
- data/lib/asynk/config.rb +33 -0
- data/lib/asynk/consumer.rb +98 -0
- data/lib/asynk/logging.rb +97 -0
- data/lib/asynk/message.rb +30 -0
- data/lib/asynk/publisher.rb +28 -0
- data/lib/asynk/response.rb +57 -0
- data/lib/asynk/server.rb +62 -0
- data/lib/asynk/sync_publisher.rb +36 -0
- data/lib/asynk/test_helper.rb +52 -0
- data/lib/asynk/version.rb +3 -0
- data/lib/asynk/worker.rb +56 -0
- data/lib/asynk.rb +65 -0
- data/myapp/.gitignore +17 -0
- data/myapp/Gemfile +16 -0
- data/myapp/Gemfile.lock +175 -0
- data/myapp/README.rdoc +28 -0
- data/myapp/Rakefile +6 -0
- data/myapp/app/assets/images/.keep +0 -0
- data/myapp/app/assets/javascripts/application.js +16 -0
- data/myapp/app/assets/stylesheets/application.css +15 -0
- data/myapp/app/consumers/wallet_events_consumer.rb +22 -0
- data/myapp/app/controllers/application_controller.rb +5 -0
- data/myapp/app/controllers/concerns/.keep +0 -0
- data/myapp/app/helpers/application_helper.rb +2 -0
- data/myapp/app/mailers/.keep +0 -0
- data/myapp/app/models/.keep +0 -0
- data/myapp/app/models/concerns/.keep +0 -0
- data/myapp/app/models/user.rb +2 -0
- data/myapp/app/views/layouts/application.html.erb +14 -0
- data/myapp/bin/bundle +3 -0
- data/myapp/bin/rails +8 -0
- data/myapp/bin/rake +8 -0
- data/myapp/bin/setup +29 -0
- data/myapp/bin/spring +15 -0
- data/myapp/config/application.rb +26 -0
- data/myapp/config/boot.rb +3 -0
- data/myapp/config/database.yml +32 -0
- data/myapp/config/environment.rb +5 -0
- data/myapp/config/environments/development.rb +41 -0
- data/myapp/config/environments/production.rb +79 -0
- data/myapp/config/environments/test.rb +42 -0
- data/myapp/config/initializers/assets.rb +11 -0
- data/myapp/config/initializers/backtrace_silencers.rb +7 -0
- data/myapp/config/initializers/cookies_serializer.rb +3 -0
- data/myapp/config/initializers/filter_parameter_logging.rb +4 -0
- data/myapp/config/initializers/inflections.rb +16 -0
- data/myapp/config/initializers/mime_types.rb +4 -0
- data/myapp/config/initializers/session_store.rb +3 -0
- data/myapp/config/initializers/wrap_parameters.rb +14 -0
- data/myapp/config/locales/en.yml +23 -0
- data/myapp/config/routes.rb +56 -0
- data/myapp/config/secrets.yml +22 -0
- data/myapp/config.ru +4 -0
- data/myapp/db/migrate/20150826104429_create_users.rb +10 -0
- data/myapp/db/schema.rb +26 -0
- data/myapp/db/seeds.rb +7 -0
- data/myapp/lib/assets/.keep +0 -0
- data/myapp/lib/tasks/.keep +0 -0
- data/myapp/log/.keep +0 -0
- data/myapp/public/404.html +67 -0
- data/myapp/public/422.html +67 -0
- data/myapp/public/500.html +66 -0
- data/myapp/public/favicon.ico +0 -0
- data/myapp/public/robots.txt +5 -0
- data/myapp/test/controllers/.keep +0 -0
- data/myapp/test/fixtures/.keep +0 -0
- data/myapp/test/fixtures/users.yml +9 -0
- data/myapp/test/helpers/.keep +0 -0
- data/myapp/test/integration/.keep +0 -0
- data/myapp/test/mailers/.keep +0 -0
- data/myapp/test/models/.keep +0 -0
- data/myapp/test/models/user_test.rb +7 -0
- data/myapp/test/test_helper.rb +10 -0
- data/myapp/vendor/assets/javascripts/.keep +0 -0
- data/myapp/vendor/assets/stylesheets/.keep +0 -0
- data/publisher.rb +15 -0
- data/test/consumer_example.rb +31 -0
- data/test/consumer_testing_example_test.rb +21 -0
- data/test/test_helper.rb +7 -0
- 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
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
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
|
data/lib/asynk/broker.rb
ADDED
@@ -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
|