rcom 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 95a36229bc381e17c5190154a0679add12a794bf
4
- data.tar.gz: ab8d5adbcb9b0a599a3da61badd4197dfec6c732
3
+ metadata.gz: 57eeb76581f7c4b2e8a370e59c286e47d4a3eeb8
4
+ data.tar.gz: d417c03fd9471cf183dcbb2216a62ac9d356bbb6
5
5
  SHA512:
6
- metadata.gz: d2199539d074722c55d225ac420f953fc6299d15c24569d7e2e9ce1f58031d3ddc1b3782a23b2c340083b13768a1a6a0f485958c5aee4284e5f840ebc5a67df2
7
- data.tar.gz: 28cf9fd25c94b73073c0cd3aeaa77fdccb2544c1a27a518d7da2401f1252c347eaea9d38fdeddd6f5c4f4b0a15396263dedf001440c873a815ffeba10ebcdc55
6
+ metadata.gz: 5be8b6c88113107329e7a061e04ffbd1785111f57c112df4c6edf5ae0833968595678812a0ff79f9e9688b8827eb08087a603c5055dcb2b517e460d809c649c0
7
+ data.tar.gz: 8bc4c1f4c67821cf3ea3190e6a8b9eb0430f7c09993e27bc13882363be3147a3263b5e0abc4b95e4e689378af3580c81993fa8989e9d0e85a4a52f8db61c5370
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Rcom
2
2
 
3
- Redis inter-service messaging: request-response, publish-subscribe and tasks. A thin, minimal layer on top of [Redis-rb](https://github.com/redis/redis-rb).
3
+ [Redis](http://redis.io) inter-service messaging: request-response, publish-subscribe and task queues. A thin, minimal layer on top of [Redis-rb](https://github.com/redis/redis-rb).
4
4
 
5
5
  ## Installation.
6
6
 
@@ -18,8 +18,121 @@ gem 'rcom'
18
18
 
19
19
  ## Usage.
20
20
 
21
+ Rcom supports the request-response, publish-subscribe and task queues patterns for inter-service messaging. Publishers are non-blocking, subscribers/consumers are blocking and should be run as independent processes. Processes communicate using MessagePack internally.
22
+
23
+ ### Node.
24
+
25
+ A node represents a Redis connection to a server address specified with an ENV variable.
26
+
27
+ ```ruby
28
+ # Specify this in your .env file.
29
+ ENV['local'] = 'redis://localhost'
30
+ node = Rcom::Node.new('local').connect
31
+ ```
32
+
33
+ ### Topics.
34
+
35
+ One service might need to update many different services about an event, following the publish-subscribe pattern. You can publish and subscribe to topics on a node, specifying a key.
36
+
37
+ - Publisher.
38
+
39
+ ```ruby
40
+ message = {
41
+ id: 1,
42
+ key: 'xxxccc'
43
+ }
44
+
45
+ node = Rcom::Node.new('local').connect
46
+ topic = Rcom::Topic.new(node: node, key: 'users')
47
+
48
+ topic.publish(message)
49
+ ```
50
+
51
+ - Subscriber.
52
+
53
+ ```ruby
54
+ node = Rcom::Node.new('local').connect
55
+ topic = Rcom::Topic.new(node: node, key: 'users')
56
+
57
+ topic.subscribe do |message|
58
+ p message
59
+ end
60
+ ```
61
+
62
+ ## Tasks.
63
+
64
+ A service might need to push expensive tasks into a queue and forget about them. Tasks will be processed by consumers listening to the queue.
65
+
66
+ - Publisher.
67
+
68
+ ```ruby
69
+ message = {
70
+ id: 1,
71
+ key: 'xxxccc'
72
+ }
73
+
74
+ node = Rcom::Node.new('local').connect
75
+ messages = Rcom::Task.new(node: node, queue: 'messages')
76
+
77
+ messages.publish(message)
78
+ ```
79
+
80
+ - Consumer.
81
+
82
+ ```ruby
83
+ node = Rcom::Node.new('local').connect
84
+ messages = Rcom::Task.new(node: node, queue: 'messages')
85
+
86
+ messages.subscribe do |message|
87
+ sleep 1
88
+ p message
89
+ end
90
+ ```
91
+
92
+ ## RPC, requests and responses.
93
+
94
+ In some cases services need real time informations from other services that can't be asynchronously processed. A service can create a request on a route. The other service listening on the same route will reply to the request.
95
+
96
+ - Publisher.
97
+
98
+ ```ruby
99
+ message = {
100
+ route: 'user.key',
101
+ args: 1
102
+ }
103
+
104
+ node = Rcom::Node.new('local').connect
105
+ auth = Rcom::Rpc.new(node: node, service: 'auth')
106
+
107
+ auth.request(message)
108
+ ```
109
+
110
+ - Consumer.
111
+
112
+ ```ruby
113
+ node = Rcom::Node.new('local').connect
114
+ auth = Rcom::Rpc.new(node: node, service: 'auth')
115
+
116
+ auth.subscribe do |request|
117
+ request.on('user.key') do |params|
118
+ request.reply = 'xxxccc'
119
+ end
120
+
121
+ request.on('user.password') do |params|
122
+ request.reply = 'not authorized'
123
+ end
124
+ end
125
+ ```
126
+
21
127
  ## Test.
22
128
 
129
+ - Be sure you have a local Redis server running.
130
+
131
+ - run tests with:
132
+ ```ruby
133
+ bundle exec rake test:spec
134
+ ```
135
+
23
136
  ## Contributing.
24
137
 
25
138
  1. Fork it ( https://github.com/badshark/rcom/fork )
data/lib/rcom/rpc.rb CHANGED
@@ -8,29 +8,38 @@ module Rcom
8
8
  end
9
9
 
10
10
  def request(params)
11
- request = {
12
- id: SecureRandom.hex,
13
- method: params[:method],
14
- args: params[:args]
15
- }
16
- node.rpush(service, request.to_msgpack)
17
- ch, response = node.brpop(request[:id], timeout=10)
18
- MessagePack.unpack(
19
- response,
20
- symbolize_keys: true
21
- )
11
+ begin
12
+ request = {
13
+ id: SecureRandom.hex,
14
+ route: params[:route],
15
+ args: params[:args] || ''
16
+ }
17
+
18
+ node.rpush(service, request.to_msgpack)
19
+ ch, response = node.brpop(request[:id], timeout=10)
20
+
21
+ MessagePack.unpack(
22
+ response,
23
+ symbolize_keys: true
24
+ )
25
+ rescue
26
+ return nil
27
+ end
22
28
  end
23
29
 
24
30
  def subscribe
25
31
  begin
26
32
  loop do
27
33
  ch, request = node.brpop(service)
34
+
28
35
  message = MessagePack.unpack(
29
36
  request,
30
37
  symbolize_keys: true
31
38
  )
32
39
  router = Rcom::Router.new(message)
40
+
33
41
  yield router
42
+
34
43
  node.rpush(message[:id], router.reply.to_msgpack)
35
44
  end
36
45
  rescue Interrupt => _
@@ -45,8 +54,8 @@ module Rcom
45
54
  @message = message
46
55
  end
47
56
 
48
- def on(method)
49
- return nil unless message[:method] == method
57
+ def on(route)
58
+ return nil unless message[:route] == route
50
59
  yield message[:args]
51
60
  end
52
61
  end
data/lib/rcom/task.rb CHANGED
@@ -8,7 +8,12 @@ module Rcom
8
8
  end
9
9
 
10
10
  def publish(message)
11
- node.lpush(queue, message.to_msgpack)
11
+ begin
12
+ node.lpush(queue, message.to_msgpack)
13
+ return true
14
+ rescue
15
+ return nil
16
+ end
12
17
  end
13
18
 
14
19
  def subscribe
data/lib/rcom/topic.rb CHANGED
@@ -8,7 +8,12 @@ module Rcom
8
8
  end
9
9
 
10
10
  def publish(message)
11
- node.publish(key, message.to_msgpack)
11
+ begin
12
+ node.publish(key, message.to_msgpack)
13
+ return true
14
+ rescue
15
+ return nil
16
+ end
12
17
  end
13
18
 
14
19
  def subscribe
data/lib/rcom/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rcom
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -2,12 +2,11 @@
2
2
  ENV['LOCAL'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  node = Rcom::Node.new('local').connect
8
- service = Rcom::Rpc.new(node: node, service: 'auth')
7
+ auth = Rcom::Rpc.new(node: node, service: 'auth')
9
8
 
10
- service.subscribe do |request|
9
+ auth.subscribe do |request|
11
10
  request.on('user.key') do |params|
12
11
  request.reply = 'xxxccc'
13
12
  end
@@ -2,12 +2,11 @@
2
2
  ENV['LOCAL'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  message = {
8
- method: 'user.key',
7
+ route: 'user.key',
9
8
  args: 1
10
9
  }
11
10
  node = Rcom::Node.new('local').connect
12
- service = Rcom::Rpc.new(node: node, service: 'auth')
13
- p service.request(message)
11
+ auth = Rcom::Rpc.new(node: node, service: 'auth')
12
+ p auth.request(message)
@@ -2,7 +2,6 @@
2
2
  ENV['LOCAL'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  node = Rcom::Node.new('local').connect
8
7
  messages = Rcom::Task.new(node: node, queue: 'messages')
@@ -2,7 +2,6 @@
2
2
  ENV['LOCAL'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  message = {
8
7
  id: 1,
@@ -2,13 +2,12 @@
2
2
  ENV['local'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  message = {
8
7
  id: 1,
9
8
  key: 'xxxccc'
10
9
  }
11
10
  node = Rcom::Node.new('local').connect
12
- topic = Rcom::Topic.new(node: node, key: 'users')
11
+ users = Rcom::Topic.new(node: node, key: 'users')
13
12
 
14
- topic.publish(message)
13
+ users.publish(message)
@@ -2,11 +2,10 @@
2
2
  ENV['local'] = 'redis://localhost'
3
3
 
4
4
  require 'rcom'
5
- require 'json'
6
5
 
7
6
  node = Rcom::Node.new('local').connect
8
- topic = Rcom::Topic.new(node: node, key: 'users')
7
+ users = Rcom::Topic.new(node: node, key: 'users')
9
8
 
10
- topic.subscribe do |message|
9
+ users.subscribe do |message|
11
10
  p message
12
11
  end
@@ -4,11 +4,15 @@ describe 'Rpc' do
4
4
  before do
5
5
  ENV['LOCAL'] = 'redis://localhost'
6
6
  @node = Rcom::Node.new('local').connect
7
+ @service = Rcom::Rpc.new(node: @node, service: 'auth')
7
8
  end
8
9
 
9
10
  it 'represents a remote procedure call' do
10
- service = Rcom::Rpc.new(node: @node, service: 'auth')
11
- service.must_be_instance_of Rcom::Rpc
11
+ @service.must_be_instance_of Rcom::Rpc
12
+ end
13
+
14
+ it 'returns nil if the request cannot be processed' do
15
+ @service.request(route: 'nonexistent').must_equal nil
12
16
  end
13
17
 
14
18
  it 'works in a request/response scenario' do
@@ -17,19 +21,16 @@ describe 'Rpc' do
17
21
  publisher = 'bundle exec ruby test/bin/rpc_publisher.rb'
18
22
  consumer = 'bundle exec ruby test/bin/rpc_consumer.rb'
19
23
 
20
- # Start the consumer, wait for it to be up,
21
- # start the publisher and wait for the message
22
- # on the consumer side. Process, then kill
23
- # the long-running consumer.
24
-
24
+ # Spawn consumer and wait for requests.
25
25
  consumer_pid = spawn(consumer)
26
26
  sleep 1
27
27
 
28
- Open3.popen3(publisher) do |stdin, stdout, stderr, wait_thr|
28
+ # Spawn a request and record stdout,
29
+ # then kill both consumer and publisher.
30
+ Open3.popen3(publisher) do |stdin, stdout, stderr, thr|
29
31
  response = stdout.gets
30
- Process.kill('INT', wait_thr.pid)
32
+ Process.kill('INT', thr.pid)
31
33
  end
32
-
33
34
  Process.kill('INT', consumer_pid)
34
35
 
35
36
  eval(response.chomp).must_equal user_key
@@ -1,15 +1,22 @@
1
1
  require_relative './_init'
2
2
 
3
3
  describe 'Task' do
4
- # Doesn't stop processes
5
4
  before do
6
5
  ENV['LOCAL'] = 'redis://localhost'
7
6
  @node = Rcom::Node.new('local').connect
7
+ @task = Rcom::Task.new(node: @node, queue: 'messages')
8
8
  end
9
9
 
10
10
  it 'represents a Task' do
11
- task = Rcom::Task.new(node: @node, queue: 'messages')
12
- task.must_be_instance_of Rcom::Task
11
+ @task.must_be_instance_of Rcom::Task
12
+ end
13
+
14
+ it 'can pubish a task' do
15
+ message = {
16
+ id: 1,
17
+ key: 'xxxccc'
18
+ }
19
+ @task.publish(message).must_equal true
13
20
  end
14
21
 
15
22
  it 'works in a pub/consumer scenario' do
@@ -26,6 +33,7 @@ describe 'Task' do
26
33
  # on the consumer side. Process, then kill
27
34
  # the long-running consumer.
28
35
  Open3.popen3(consumer) do |stdin, stdout, stderr, wait_thr|
36
+ sleep 1
29
37
  spawn(publisher)
30
38
  completed_job = stdout.gets
31
39
  Process.kill('INT', wait_thr.pid)
@@ -4,11 +4,19 @@ describe 'Topic' do
4
4
  before do
5
5
  ENV['LOCAL'] = 'redis://localhost'
6
6
  @node = Rcom::Node.new('local').connect
7
+ @topic = Rcom::Topic.new(node: @node, key: 'users')
7
8
  end
8
9
 
9
10
  it 'represents a Topic' do
10
- topic = Rcom::Topic.new(node: @node, key: 'users')
11
- topic.must_be_instance_of Rcom::Topic
11
+ @topic.must_be_instance_of Rcom::Topic
12
+ end
13
+
14
+ it 'can publish a message' do
15
+ message = {
16
+ id: 1,
17
+ key: 'xxxccc'
18
+ }
19
+ @topic.publish(message).must_equal true
12
20
  end
13
21
 
14
22
  it 'works in a pub/sub scenario' do
@@ -25,13 +33,13 @@ describe 'Topic' do
25
33
  # on the subscriber side. Then kill
26
34
  # the long-running subscriber.
27
35
  Open3.popen3(subscriber) do |stdin, stdout, stderr, wait_thr|
28
- sleep 1
36
+ sleep 2
29
37
  spawn(publisher)
30
38
  read_message = stdout.gets
31
39
  Process.kill('INT', wait_thr.pid)
32
40
  end
33
41
 
34
- output = eval read_message.chomp
42
+ output = eval(read_message.chomp)
35
43
  output.must_equal message
36
44
  end
37
45
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcom
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marco Lisci