module SMService
Constants
- ENDPOINT_IN
- ENDPOINT_OUT
- KEEPALIVE_WAIT_TIME
- LOGGER
- REGISTER_RETRY
- REGISTER_WAIT_TIME
- VERSION
Attributes
Public Class Methods
When registering node, following parameters is mandatory:
name: name of service/node with its id; actions: list of actions, should be in lowercase and compatible to naming standard for ruby methods actions could be presented as array or as string if service registering only single action
Example: SMService::SomeService.new(name: 'dummy-ruby', actions: %w(a b c ping pong))
Following actions should be defined as methods for new service, i.e.
def action_ping
(headers, message)
LOGGER.info "#{self.class} - Processing action: PING with headers: #{headers.inspect} and message: #{message.inspect}" # ... business logic here end
Prohibited names for actions is register and update, those are reserved to let service communicate with SM. Check SMService::Dummy source code (in examples folder) for detais
# File lib/smservice.rb, line 39 def initialize(name:, actions: []) @service_name = name @actions = [%w(ping), actions].flatten.sort.uniq @mutex = Mutex.new context = ZMQ::Context.new @socket_in = context.socket ZMQ::DEALER @socket_out = context.socket ZMQ::DEALER @socket_in.setsockopt ZMQ::IDENTITY, service_name @socket_in.connect ENDPOINT_IN @socket_out.setsockopt ZMQ::IDENTITY, service_name @socket_out.connect ENDPOINT_OUT end
Public Instance Methods
# File lib/smservice.rb, line 126 def action_ping(headers, message) LOGGER.info "#{self.class} - Processing action 'ping'. Headers: #{headers.inspect}, message: #{message.inspect}" end
# File lib/smservice.rb, line 117 def action_register(headers, message) if headers['action'] == 'REGISTER' && message['result'] == 'OK' @registered = true LOGGER.info "#{self.class} - Action: REGISTER (successful), headers: #{headers.inspect}, message: #{message.inspect}" else LOGGER.info "#{self.class} - Action: REGISTER (failure), headers: #{headers.inspect}, message: #{message.inspect}" end end
# File lib/smservice.rb, line 134 def action_reply(headers, message) LOGGER.info "#{self.class} - Processing action 'reply'. Headers: #{headers.inspect}, message: #{message.inspect}" end
# File lib/smservice.rb, line 130 def action_update(headers, message) LOGGER.info "#{self.class} - Processing action 'update'. Headers: #{headers.inspect}, message: #{message.inspect}" end
Requesting another service, registered at SM to execute given action, example: execute(action: 'create_customer_portal', message: {my_request: 'should create a customer', my_data: 'add whatever data needed'})
# File lib/smservice.rb, line 141 def execute(action:, reply_to: nil, message: nil) LOGGER.info "#{self.class} - SM execute request. Action: #{action.inspect}, message: #{message.inspect}" action = {service: action, reply_to: (reply_to || @service_name)}.to_msgpack message = message.to_msgpack @socket_out.send_strings [action, message] end
# File lib/smservice.rb, line 101 def keep_alive!(wait_time: KEEPALIVE_WAIT_TIME) Thread.start do LOGGER.info "#{self.class} - Starting registration update loop with periodic interval #{wait_time} sec" loop do break unless registered? sleep wait_time request(action: 'UPDATE') end LOGGER.info 'Registration update loop terminated' end end
# File lib/smservice.rb, line 75 def poller loop do pull end end
# File lib/smservice.rb, line 69 def poller! Thread.start do poller end end
# File lib/smservice.rb, line 81 def register(wait_time: REGISTER_WAIT_TIME) if wait_time.to_f <= 0 loop do register_service break if registered? # not registered, retrying in 10 seconds sleep REGISTER_RETRY end else Timeout::timeout(wait_time) do loop do register_service break if registered? # not registered, retrying in 10 seconds sleep REGISTER_RETRY end end end end
# File lib/smservice.rb, line 113 def registered? @registered end
starting infinite loop in order to process service logic. start! or service_poller methods could be modified implementing different services, i.e. to run as threaded metod
# File lib/smservice.rb, line 58 def start! register keep_alive! poller! @started = true end
# File lib/smservice.rb, line 65 def started? @started end
Private Instance Methods
# File lib/smservice.rb, line 166 def pull response = [] @mutex.synchronize do @socket_in.recv_strings(response) end headers, message = response headers = MessagePack.unpack(headers) message = MessagePack.unpack(message) LOGGER.info "#{self.class} - SM response received by pull. Headers: #{headers.inspect}, message: #{message.inspect}" # Validates is action_name exists as class method and calling it. # In case, if Service Manager return headers['action'] == 'METHOD_NAME', # ruby expecting that somethinf like this would be defined: # # def action_method_name(headers, message) # # ... some business logic here # end # # Two actions defined as part of module, it is : # - action_register, that confirms registration of node with Service Manager, and # - action_update, that confirms successfule registration update, we need it to support keep-alive # action_name = "action_#{(headers['action'] || headers['service']).downcase}".to_sym if respond_to? action_name.to_sym send(action_name, headers, message) else LOGGER.error "#{self.class} - Method #{action_name} does not exist in class instance for action name #{headers['action']}" end end
# File lib/smservice.rb, line 150 def register_service LOGGER.info "#{self.class} - Registering service #{service_name}" request(action: 'REGISTER', message: {services: @actions}) pull end
Sending request to the Service Manager
# File lib/smservice.rb, line 157 def request(action:, message: nil) LOGGER.info "#{self.class} - SM request. Action: #{action.inspect}, message: #{message.inspect}" action = {action: action}.to_msgpack message = message.to_msgpack @mutex.synchronize do @socket_in.send_strings [action, message] end end