Creating and using stubs
At this point in this chapter, we have a server that exposes a service implementation. Now we focus on the client side. Before a client can send RPCs to the server, it must first establish a connection to the server and then create a stub, whose methods are invoked to send RPCs to the server.
The mechanism for creating a gRPC client, establishing a connection to a server, and instantiating stubs differs from one target language to another. The gRPC documentation site’s tutorials provides examples for each of the officially supported languages.
The examples below will show the steps for building a Ruby client. At the end, we’ll have a Ruby client that is issuing RPCs to our Go server, demonstrating the interoperability across languages.
Before we start writing Ruby code, we’ll first generate the Ruby code from our proto
source file. Ruby code generation for messages and enums is built into a protoc
, so no
plugin is needed. We will need a plugin, however, to generate the gRPC-specific pieces.
All gRPC plugins (other than for Go, Java, and Dart) are in the main gRPC repo, which
is also where you will find installation instructions:
https://github.com/grpc/grpc/blob/master/INSTALL.md.
After installing everything and building the gRPC repo from source, you can then use
the gRPC Ruby plugin (aptly named grpc_ruby_plugin
):
protoc --ruby_out=
. --grpc_out=
.\
--plugin=
protoc-gen-grpc=
"
$(
which grpc_ruby_plugin)
"
\
./proto/sfapi.proto
The --ruby_out
parameter triggers the standard Ruby code generation, which is for
messages and enums defined in the proto source. The --grpc_out
parameter then
triggers gRPC-specific code generation for Ruby. (It generates Ruby, not some other
language, because of the --plugin
argument, telling it to invoke grpc_ruby_plugin
).
Like in the earlier example, when generating Go code, the arguments to these
--*_out
parameters is .
, which tells protoc
to generate code relative to the
current directory. This will end up generating Ruby source files in the same location
as the proto source, in the ./proto
directory.
Dialing the server
Now that we have Ruby code for our gRPC service, the first thing our client code must do is to connect to our running server. Only after a connection is established can we start sending RPCs to the server.
In Ruby, creating the connection and the stub is all done in one step. It is important to note that this means that stubs are not cheap to construct in Ruby as they are in other languages such as Go or Java, because they are coupled with a stateful connection to the server.
Below is an example that constructs the stub by dialing the server, which has defaulted to listening on port 8080:
Ruby
$LOAD_PATH
.
unshift
File
.
expand_path
(
"../"
,
__FILE__
)
require_relative
'./proto/sfapi_pb'
require_relative
'./proto/sfapi_services_pb'
stub
=
Starfriends
:
:Stub
.
new
(
"localhost:8080"
,
:this_channel_is_insecure
)
# Now we can use the stub to make RPCs
# ...
Here’s an equivalent program, but written in Go, to demonstrate how the connection is first setup, and then stubs are cheap wrappers around the connection:
Go
package
main
import
(
"google.golang.org/grpc"
"./proto"
)
func
main
()
{
// First we create the connection:
conn
,
err
:=
grpc
.
Dial
(
"localhost:8080"
,
grpc
.
WithInsecure
())
if
err
!=
nil
{
panic
(
err
)
}
// We can now create stubs that wrap conn:
stub
:=
proto
.
NewStarfriendsClient
(
conn
)
// Now we can use the stub to make RPCs
// ...
}
Here you can see the two steps. The conn
object is a stateful connection to the
server, and stub
is a cheap wrapper that can be used to issue RPCs. The
NewStarfriendsClient
factory method was created by protoc
. Such a factory is
generated for each service in the proto source.
Working with stubs
Now that you have a stub, you can invoke methods that result in sending RPCs to the Go server:
Ruby
# First we create the request message
request
=
GetFilmRequest
.
new
(
id
:
'4'
)
# Now we can use stub (created in the Ruby example above)
# to send an RPC to the server and get back the response
response
=
stub
.
get_film
(
request
)
puts
response
.
inspect
The above snippet issues the RPC and prints the response, which has the details for the movie with ID “4”. The output looks like so:
<GetFilmResponse: film: <Film: id: "4", title: "A New Hope", director: "George Lucas", producer: "Gary Kurtz, Rick McCallum", release_date: <Google::Protobuf::Timestamp: seconds: 233380800, nanos: 0>>>
One interesting thing to note is that protoc
converted the name of the method,
which was defined as GetFilm
in the proto source, to idiomatic naming conventions
for Ruby methods: get_film
. It does this for all languages, translating names in
the IDL into names that should feel “at home” in the language of the generated code.
The stub has one method for each method defined in the service. The kind of request each method expects is the same as defined in the proto IDL, and the kind of message that is returned also matches the response type defined in the proto IDL.