Last active
January 26, 2023 20:16
-
-
Save fallwith/b0971d024f0ed671655cc40967f0c850 to your computer and use it in GitHub Desktop.
Ruby grpc gem with gzip compression demonstration
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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