class Gruf::Server

Represents a gRPC server. Automatically loads and augments gRPC handlers and services based on configuration values.

Attributes

options[R]

@return [Hash] Hash of options passed into the server

port[R]

@return [Integer] The port the server is bound to

Public Class Methods

new(opts = {}) click to toggle source

Initialize the server and load and setup the services

@param [Hash] opts

# File lib/gruf/server.rb, line 38
def initialize(opts = {})
  @options = opts || {}
  @interceptors = opts.fetch(:interceptor_registry, Gruf.interceptors)
  @interceptors = Gruf::Interceptors::Registry.new unless @interceptors.is_a?(Gruf::Interceptors::Registry)
  @services = []
  @started = false
  @stop_server = false
  @stop_server_cv = ConditionVariable.new
  @stop_server_mu = Monitor.new
  @server_mu = Monitor.new
  @hostname = opts.fetch(:hostname, Gruf.server_binding_url)
  @event_listener_proc = opts.fetch(:event_listener_proc, Gruf.event_listener_proc)
  setup
end

Public Instance Methods

add_interceptor(klass, opts = {}) click to toggle source

Add an interceptor to the server

@param [Class] klass The Interceptor to add to the registry @param [Hash] opts A hash of options for the interceptor @raise [ServerAlreadyStartedError] if the server is already started

# File lib/gruf/server.rb, line 137
def add_interceptor(klass, opts = {})
  raise ServerAlreadyStartedError if @started

  @interceptors.use(klass, opts)
end
add_service(klass) click to toggle source

Add a gRPC service stub to be served by gruf

@param [Class] klass @raise [ServerAlreadyStartedError] if the server is already started

# File lib/gruf/server.rb, line 124
def add_service(klass)
  raise ServerAlreadyStartedError if @started

  @services << klass unless @services.include?(klass)
end
clear_interceptors() click to toggle source

Clear the interceptor registry of interceptors

# File lib/gruf/server.rb, line 192
def clear_interceptors
  raise ServerAlreadyStartedError if @started

  @interceptors.clear
end
insert_interceptor_after(after_class, interceptor_class, opts = {}) click to toggle source

Insert an interceptor after another in the currently registered order of execution

@param [Class] after_class The interceptor that you want to add the new interceptor after @param [Class] interceptor_class The Interceptor to add to the registry @param [Hash] opts A hash of options for the interceptor

# File lib/gruf/server.rb, line 163
def insert_interceptor_after(after_class, interceptor_class, opts = {})
  raise ServerAlreadyStartedError if @started

  @interceptors.insert_after(after_class, interceptor_class, opts)
end
insert_interceptor_before(before_class, interceptor_class, opts = {}) click to toggle source

Insert an interceptor before another in the currently registered order of execution

@param [Class] before_class The interceptor that you want to add the new interceptor before @param [Class] interceptor_class The Interceptor to add to the registry @param [Hash] opts A hash of options for the interceptor

# File lib/gruf/server.rb, line 150
def insert_interceptor_before(before_class, interceptor_class, opts = {})
  raise ServerAlreadyStartedError if @started

  @interceptors.insert_before(before_class, interceptor_class, opts)
end
list_interceptors() click to toggle source

Return the current list of added interceptor classes

@return [Array<Class>]

# File lib/gruf/server.rb, line 174
def list_interceptors
  @interceptors.list
end
remove_interceptor(klass) click to toggle source

Remove an interceptor from the server

@param [Class] klass

# File lib/gruf/server.rb, line 183
def remove_interceptor(klass)
  raise ServerAlreadyStartedError if @started

  @interceptors.remove(klass)
end
server() click to toggle source

@return [GRPC::RpcServer] The GRPC server running

# File lib/gruf/server.rb, line 56
def server
  @server_mu.synchronize do
    @server ||= begin
      # For backward compatibility, we allow these options to be passed directly
      # in the Gruf::Server options, or via Gruf.rpc_server_options.
      server_options = {
        pool_size: options.fetch(:pool_size, Gruf.rpc_server_options[:pool_size]),
        max_waiting_requests: options.fetch(:max_waiting_requests, Gruf.rpc_server_options[:max_waiting_requests]),
        poll_period: options.fetch(:poll_period, Gruf.rpc_server_options[:poll_period]),
        pool_keep_alive: options.fetch(:pool_keep_alive, Gruf.rpc_server_options[:pool_keep_alive]),
        connect_md_proc: options.fetch(:connect_md_proc, Gruf.rpc_server_options[:connect_md_proc]),
        server_args: options.fetch(:server_args, Gruf.rpc_server_options[:server_args])
      }

      server = if @event_listener_proc
                 server_options[:event_listener_proc] = @event_listener_proc
                 Gruf::InstrumentableGrpcServer.new(**server_options)
               else
                 GRPC::RpcServer.new(**server_options)
               end

      @port = server.add_http2_port(@hostname, ssl_credentials)
      @services.each { |s| server.handle(s) }
      server
    end
  end
end
start!() click to toggle source

Start the gRPC server

:nocov:

# File lib/gruf/server.rb, line 88
def start!
  update_proc_title(:starting)

  server_thread = Thread.new do
    logger.info { "Starting gruf server at #{@hostname}..." }
    server.run
  end

  stop_server_thread = Thread.new do
    loop do
      break if @stop_server

      @stop_server_mu.synchronize { @stop_server_cv.wait(@stop_server_mu, 10) }
    end
    logger.info { 'Shutting down...' }
    server.stop
  end

  server.wait_till_running
  @started = true
  update_proc_title(:serving)
  stop_server_thread.join
  server_thread.join
  @started = false

  update_proc_title(:stopped)
  logger.info { 'Goodbye!' }
end

Private Instance Methods

controllers_path() click to toggle source

@param [String]

# File lib/gruf/server.rb, line 248
def controllers_path
  options.fetch(:controllers_path, Gruf.controllers_path)
end
load_controllers() click to toggle source

Auto-load all gRPC handlers

:nocov:

# File lib/gruf/server.rb, line 231
def load_controllers
  return unless File.directory?(controllers_path)

  path = File.realpath(controllers_path)
  $LOAD_PATH.unshift(path)
  Dir["#{path}/**/*.rb"].each do |f|
    next if f.include?('_pb') # exclude if people include proto generated files in app/rpc

    logger.info "- Loading gRPC service file: #{f}"
    load File.realpath(f)
  end
end
setup() click to toggle source

Setup server

:nocov:

# File lib/gruf/server.rb, line 204
def setup
  setup_signal_handlers
  load_controllers
end
setup_signal_handlers() click to toggle source

Register signal handlers

:nocov:

# File lib/gruf/server.rb, line 214
def setup_signal_handlers
  Signal.trap('INT') do
    @stop_server = true
    @stop_server_cv.broadcast
  end

  Signal.trap('TERM') do
    @stop_server = true
    @stop_server_cv.broadcast
  end
end
ssl_credentials() click to toggle source

Load the SSL/TLS credentials for this server

@return [GRPC::Core::ServerCredentials|Symbol]

:nocov:

# File lib/gruf/server.rb, line 258
def ssl_credentials
  return :this_port_is_insecure unless options.fetch(:use_ssl, Gruf.use_ssl)

  private_key = File.read(options.fetch(:ssl_key_file, Gruf.ssl_key_file))
  cert_chain = File.read(options.fetch(:ssl_crt_file, Gruf.ssl_crt_file))
  certs = [nil, [{ private_key: private_key, cert_chain: cert_chain }], false]
  GRPC::Core::ServerCredentials.new(*certs)
end
update_proc_title(state) click to toggle source

Updates proc name/title

@param [Symbol] state

:nocov:

# File lib/gruf/server.rb, line 274
def update_proc_title(state)
  Process.setproctitle("gruf #{Gruf::VERSION} -- #{state}")
end