Skip to content

Instantly share code, notes, and snippets.

@fallwith
Last active January 26, 2023 20:16
Show Gist options
  • Save fallwith/b0971d024f0ed671655cc40967f0c850 to your computer and use it in GitHub Desktop.
Save fallwith/b0971d024f0ed671655cc40967f0c850 to your computer and use it in GitHub Desktop.
Ruby grpc gem with gzip compression demonstration
#!/usr/bin/env ruby
# frozen_string_literal: true
# This standalone Ruby script is intended to demonstrate the use of the 'grpc'
# gem to establish a minimal unary rpc and spawn both a gRPC server and client
# and have them use gzip to compress content being sent to one another after
# the client requests the use of gzip at channel creation, stub creation, and
# call time.
#
# Instructions:
# - Make sure Ruby 2.6+ (Ruby 3.1 tested with the most) is available
# - Download this script somewhere as grpc_ruby_gzip.rb
# - In one terminal/shell session, run:
# ruby grpc_ruby_gzip.rb server
# - In another terminal/shell session, run:
# ruby grpc_ruby_gzip.rb client
#
# Optionally:
# - To disable compression, set COMPRESSION_ENABLED=false as an environment
# variable before launching the client.
# - To send more than 1 request to the server, set LOOP=<count> as an
# environment variable before launching the client.
require 'bundler/inline'
puts "Fetching and building the 'grpc' gem and its dependencies..."
gemfile do
source 'https://rubygems.org'
gem 'grpc'
end
require 'google/protobuf'
require 'grpc'
module Repro
# Helpers - common content shared by both client and server
module Helpers
HOST = 'localhost'
PORT = '50051'
SERVICE_NAME = 'repro'
def self.build_generated_pool
return if @built
Google::Protobuf::DescriptorPool.generated_pool.build do
add_message Repro::Helpers.request_name do
optional :message, :string, 1
end
add_message Repro::Helpers.response_name do
optional :message, :string, 1
end
end
@built = true
end
def self.host_and_port
"#{HOST}:#{PORT}"
end
def self.request
build_generated_pool
Google::Protobuf::DescriptorPool.generated_pool.lookup(request_name).msgclass
end
def self.request_name
"#{SERVICE_NAME}.Request"
end
def self.response
build_generated_pool
Google::Protobuf::DescriptorPool.generated_pool.lookup(response_name).msgclass
end
def self.response_name
"#{SERVICE_NAME}.Response"
end
end
# Service - a GenericService based class
class Service
include GRPC::GenericService
self.marshal_class_method = :encode
self.unmarshal_class_method = :decode
self.service_name = Helpers::SERVICE_NAME
rpc :Greet, Helpers.request, Helpers.response
end
# Client - a minimal Ruby gRPC client wishing to compress outbound payloads
class Client
COMPRESSION_ENABLED = ENV.fetch('COMPRESSION_ENABLED', 'true') == 'true'
COMPRESSION_OPTIONS = { default_algorithm: :gzip, default_level: :high }.freeze
CHANNEL_ARGS = GRPC::Core::CompressionOptions.new(COMPRESSION_OPTIONS).to_channel_arg_hash.freeze
CHANNEL_SETTINGS = { 'grpc.enable_deadline_checking' => 0 }.merge!(CHANNEL_ARGS).freeze
CHANNEL_SETTINGS_NO_COMPRESSION = { 'grpc.enable_deadline_checking' => 0,
'grpc.minimal_stack' => 1 }.merge!(CHANNEL_ARGS).freeze
METADATA = { 'grpc-internal-encoding-request' => 'gzip',
'grpc-encoding' => 'gzip',
'grpc-accept-encoding' => ['gzip'],
'content-coding' => 'gzip',
'content-encoding' => 'gzip' }.freeze
def channel
@channel ||= begin
settings = COMPRESSION_ENABLED ? CHANNEL_SETTINGS : CHANNEL_SETTINGS_NO_COMPRESSION
GRPC::Core::Channel.new(Helpers.host_and_port, settings, :this_channel_is_insecure)
end
end
def payload
'Reproduction' * 1000
end
def stub
@stub ||= Service.rpc_stub_class.new(Helpers.host_and_port,
:this_channel_is_insecure,
channel_override: channel,
channel_args: CHANNEL_ARGS)
end
def submit_request
req = Helpers.request.new(message: payload)
stub.greet(req, metadata: METADATA)
end
end
# Server - a minimal generic service based server
class Server < Service
def greet(request, _)
Helpers.response.new(message: request.message.reverse)
end
end
end
if $PROGRAM_NAME == __FILE__
if ARGV.size != 1
puts "Usage: #{$PROGRAM_NAME} server OR #{$PROGRAM_NAME} client"
exit 1
end
case ARGV.first
when 'server'
puts 'Creating server...'
server = GRPC::RpcServer.new
server.add_http2_port(Repro::Helpers.host_and_port, :this_port_is_insecure)
server.handle(Repro::Server)
puts 'Running server...'
server.run_till_terminated_or_interrupted([1, 'int', 'SIGTERM'])
when 'client'
puts 'Creating client...'
client = Repro::Client.new
loops = ENV.fetch('LOOPS', '1').to_i
loops.times do
puts 'Sending request...'
response = client.submit_request.message
if response == client.payload.reverse
puts 'Response received successfully!'
else
puts 'Unexpected response received.'
end
end
else
puts "Unknown argument #{ARGV.first}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment