remote_service 0.1.2 → 0.2.0

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.
data/.travis.yml CHANGED
@@ -11,4 +11,8 @@ before_install:
11
11
  - docker-compose up -d
12
12
 
13
13
  script:
14
- - bundle exec rake test
14
+ - bundle exec rake test
15
+
16
+ addons:
17
+ code_climate:
18
+ repo_token: 683ec4c66a12e8212b328523e894dd74064f340847357fb208b5bc5a9fb7e31e
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- remote_service (0.1.1)
4
+ remote_service (0.2.0)
5
5
  msgpack (~> 1.0)
6
6
  nats (~> 0.8.0)
7
7
 
@@ -29,4 +29,4 @@ DEPENDENCIES
29
29
  remote_service!
30
30
 
31
31
  BUNDLED WITH
32
- 1.12.5
32
+ 1.13.1
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016 TODO: Write your name
3
+ Copyright (c) 2016 Marek Galovic
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # RemoteService ![img](https://travis-ci.com/marekgalovic/ruby-remote-service.svg?token=tzyPCMPPikt2LiEzxR71&branch=master) [![Gem Version](https://badge.fury.io/rb/remote_service.svg)](https://badge.fury.io/rb/remote_service)
1
+ # RemoteService ![img](https://travis-ci.com/marekgalovic/ruby-remote-service.svg?token=tzyPCMPPikt2LiEzxR71&branch=master) [![Code Climate](https://codeclimate.com/github/marekgalovic/ruby-remote-service/badges/gpa.svg)](https://codeclimate.com/github/marekgalovic/ruby-remote-service) [![Gem Version](https://badge.fury.io/rb/remote_service.svg)](https://badge.fury.io/rb/remote_service)
2
2
  Remote services made easy. This gem is basically RPC client/server implemented on top of awesome [NATS](http://nats.io/) project. Every service you want to use is exposed through class that extends `RemoteService::Proxy`. Service itself extends `RemoteService::Service` and should define all methods that you want to call from clients.
3
3
 
4
4
  ## Installation
@@ -59,6 +59,9 @@ sleep(0.1) # wait for non-blocking call to execute
59
59
  puts ServiceA.all(123, keyword: 'value')
60
60
  ```
61
61
 
62
+ # Todo
63
+ Service worker threads
64
+
62
65
  ## Contributing
63
66
 
64
67
  Bug reports and pull requests are welcome on GitHub at https://github.com/marekgalovic/ruby-remote-service. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
data/examples/client.rb CHANGED
@@ -4,7 +4,7 @@ require "bundler/setup"
4
4
  require "remote_service"
5
5
 
6
6
  class ServiceA < RemoteService::Proxy
7
- timeout 100
7
+ timeout 1000
8
8
  end
9
9
 
10
10
  class ServiceB < RemoteService::Proxy
@@ -14,13 +14,14 @@ end
14
14
  RemoteService.logger.level = Logger::DEBUG
15
15
  RemoteService.connect(brokers: ['nats://127.0.0.1:4222', 'nats://127.0.0.1:5222', 'nats://127.0.0.1:6222'])
16
16
 
17
+
17
18
  clients = []
18
- 4.times do
19
+ 16.times do
19
20
  clients << Thread.new do
20
21
  loop do
21
22
  ServiceA.all(123, keyword: 'value')
22
- ServiceB.users
23
- sleep(0.01)
23
+ # ServiceB.users
24
+ sleep(1)
24
25
  end
25
26
  end
26
27
  end
@@ -7,4 +7,5 @@ class ServiceA < RemoteService::Service
7
7
  end
8
8
  end
9
9
 
10
- ServiceA.start(brokers: ['nats://127.0.0.1:5222'])
10
+ RemoteService.logger.level = Logger::INFO
11
+ ServiceA.start(brokers: ['nats://localhost:4222'])
@@ -17,7 +17,7 @@ module RemoteService
17
17
  private
18
18
 
19
19
  def call_service
20
- RemoteService::Queue.instance.request(queue, {action: action, params: params}) do |response|
20
+ Queue.instance.request(queue, {action: action, params: params}) do |response|
21
21
  yield(response['result'], response['error'])
22
22
  end
23
23
  end
@@ -5,7 +5,7 @@ module RemoteService
5
5
  class Nats
6
6
  attr_reader :brokers
7
7
 
8
- def initialize(brokers:)
8
+ def initialize(brokers)
9
9
  @brokers = brokers
10
10
  @mutex = Mutex.new
11
11
  end
@@ -15,8 +15,8 @@ module RemoteService
15
15
  connect(&block)
16
16
  end
17
17
 
18
- def stop
19
- NATS.stop
18
+ def exit
19
+ @conn_thread.exit
20
20
  end
21
21
 
22
22
  def publish(to_queue, message)
@@ -55,7 +55,7 @@ module RemoteService
55
55
 
56
56
  def connection_thread
57
57
  lock = Util::Lock.new
58
- Thread.new do
58
+ @conn_thread = Thread.new do
59
59
  connect do |connection|
60
60
  lock.unlock(connection)
61
61
  end
@@ -6,14 +6,19 @@ module RemoteService
6
6
  class Queue
7
7
  include Singleton
8
8
 
9
+ EXIT_SIGNALS = ['INT', 'TERM', 'SIGQUIT']
10
+
9
11
  def connect(brokers, &block)
10
- @conn = RemoteService::Connector::Nats.new(brokers)
12
+ brokers ||= ENV.fetch('REMOTE_SERVICE_BROKERS', 'nats://127.0.0.1:4222').split(',')
13
+ @conn = Connector::Nats.new(brokers)
11
14
  @conn.start(&block)
12
15
  end
13
16
 
14
- def service(service_handler, workers=16)
17
+ def service(service_handler, workers, monitor_interval)
18
+ workers ||= ENV.fetch('REMOTE_SERVICE_WORKERS', 4)
19
+ monitor_interval ||= ENV.fetch('REMOTE_SERVICE_MONITOR_INTERVAL', 5)
20
+ @worker_pool = WorkerPool.new(workers.to_i, monitor_interval.to_i)
15
21
  @service_handler = service_handler
16
- @workers = workers
17
22
  start_service_subscriber
18
23
  end
19
24
 
@@ -40,17 +45,20 @@ module RemoteService
40
45
 
41
46
  def start_service_subscriber
42
47
  RemoteService.logger.debug "SERVICE QUEUE: #{service_queue_name}"
43
- @conn.subscribe(service_queue_name) do |request, reply_to|
44
- begin
45
- payload = decode(request)
46
- RemoteService.logger.debug "FETCHED - REPLY_TO:[#{reply_to}] PAYLOAD:[#{payload}]"
47
- @service_handler.handle(payload, reply_to)
48
- rescue => e
49
- RemoteService.logger.error(e)
50
- Queue.instance.publish(
51
- reply_to,
52
- {result: nil, error: {name: e.class.name, message: e.message, backtrace: e.backtrace}},
53
- )
48
+ @worker_pool.start
49
+ @conn.subscribe(service_queue_name) do |*args|
50
+ @worker_pool.run(*args) do |request, reply_to|
51
+ begin
52
+ payload = decode(request)
53
+ RemoteService.logger.debug "FETCHED - REPLY_TO:[#{reply_to}] PAYLOAD:[#{payload}]"
54
+ @service_handler.handle(payload, reply_to)
55
+ rescue => e
56
+ RemoteService.logger.error(e)
57
+ Queue.instance.publish(
58
+ reply_to,
59
+ {result: nil, error: {name: e.class.name, message: e.message, backtrace: e.backtrace}},
60
+ )
61
+ end
54
62
  end
55
63
  end
56
64
  end
@@ -70,5 +78,15 @@ module RemoteService
70
78
  def decode(payload)
71
79
  MessagePack.unpack(payload)
72
80
  end
81
+
82
+ def setup_signal_handlers
83
+ EXIT_SIGNALS.each do |sig|
84
+ trap(sig) do
85
+ @worker_pool.exit if service?
86
+ @conn.exit if !service?
87
+ EM.stop
88
+ end
89
+ end
90
+ end
73
91
  end
74
92
  end
@@ -25,10 +25,10 @@ module RemoteService
25
25
  end
26
26
 
27
27
  class << self
28
- def start(brokers, workers=16)
28
+ def start(brokers:nil, workers:nil, monitor_interval:nil)
29
29
  queue = Queue.instance
30
30
  queue.connect(brokers) do
31
- queue.service(self.instance, workers)
31
+ queue.service(self.instance, workers, monitor_interval)
32
32
  end
33
33
  end
34
34
  end
@@ -1,3 +1,3 @@
1
1
  module RemoteService
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -0,0 +1,61 @@
1
+ module RemoteService
2
+ class WorkerPool
3
+ attr_reader :queue, :threads
4
+
5
+ def initialize(worker_count, monitor_interval)
6
+ @worker_count = worker_count
7
+ @monitor_interval = monitor_interval
8
+ @threads = []
9
+ @queue = ::Queue.new
10
+ end
11
+
12
+ def run(*args, &block)
13
+ queue.push({ args: args, callable: block })
14
+ end
15
+
16
+ def start
17
+ spawn_workers
18
+ monitor_thread
19
+ RemoteService.logger.info "WORKER POOL - WORKERS: #{threads.size}"
20
+ end
21
+
22
+ def join
23
+ threads.each do |thread|
24
+ thread.join
25
+ end
26
+ monitor_thread.join
27
+ end
28
+
29
+ def exit
30
+ threads.each do |thread|
31
+ thread.exit
32
+ end
33
+ monitor_thread.exit
34
+ end
35
+
36
+ private
37
+
38
+ def spawn_workers
39
+ @worker_count.times do
40
+ @threads << Thread.new do
41
+ loop do
42
+ data = queue.pop
43
+ data[:callable].call(*data[:args])
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ def monitor_thread
50
+ @monitor_thread ||= begin
51
+ RemoteService.logger.info "WORKER POOL - MONITOR INTERVAL: #{@monitor_interval}s"
52
+ Thread.new do
53
+ loop do
54
+ RemoteService.logger.info "WORKER POOL - WAITING: #{queue.size}"
55
+ sleep(@monitor_interval)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -2,6 +2,7 @@ require "remote_service/version"
2
2
  require "remote_service/errors"
3
3
  require "remote_service/util/lock"
4
4
  require "remote_service/connector/nats"
5
+ require "remote_service/worker_pool"
5
6
  require "remote_service/queue"
6
7
  require "remote_service/call"
7
8
  require "remote_service/base"
@@ -13,7 +14,7 @@ module RemoteService
13
14
  extend self
14
15
  attr_writer :logger
15
16
 
16
- def connect(brokers, &block)
17
+ def connect(brokers:, &block)
17
18
  queue = Queue.instance
18
19
  queue.connect(brokers, &block)
19
20
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: remote_service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marek Galovic
@@ -102,7 +102,9 @@ executables: []
102
102
  extensions: []
103
103
  extra_rdoc_files: []
104
104
  files:
105
+ - ".codeclimate.yml"
105
106
  - ".gitignore"
107
+ - ".rubocop.yml"
106
108
  - ".travis.yml"
107
109
  - Gemfile
108
110
  - Gemfile.lock
@@ -124,6 +126,7 @@ files:
124
126
  - lib/remote_service/service.rb
125
127
  - lib/remote_service/util/lock.rb
126
128
  - lib/remote_service/version.rb
129
+ - lib/remote_service/worker_pool.rb
127
130
  - remote_service.gemspec
128
131
  homepage: https://github.com/marekgalovic/ruby-remote-service
129
132
  licenses: