rcom 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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