gRPC - Hello World App with Python



Let us now create a basic "Hello World" like app that will use gRPC along with Python.

.proto file

First let us define the greeting.proto file in common_proto_files

syntax = "proto3";

service Greeter {
   rpc greet (ClientInput) returns (ServerOutput) {}
}
message ClientInput {
   string greeting = 1;
   string name = 2;
}
message ServerOutput {
   string message = 1;
}

Let us now take a closer look at each of the lines in the above block −

syntax = "proto3";

The "syntax" here represents the version of Protobuf we are using. So, we are using the latest version 3 and the schema thus can use all the syntax which is valid for version 3.

package tutorial;

The package here is used for conflict resolution if, say, we have multiple classes/members with the same name.

service Greeter {
   rpc greet(ClientInput) returns (ServerOutput) {}
}

This block represents the name of the service "Greeter" and the function name "greet" which can be called. The "greet" function takes in the input of type "ClientInput" and returns the output of type "ServerOutput". Now let us look at these types.

message ClientInput {
   string greeting = 1;
   string name = 2;
}

In the above block, we have defined the ClientInput which contains two attributes, "greeting" and the "name" both of them being strings. The client is supposed to send the object of type of "ClientInput" to the server.

message ServerOutput {
   string message = 1;
}

Here, we have also defined that, given a "ClientInput", the server would return the "ServerOutput" with a single attribute "message". The server is supposed to send the object of type "ServerOutput" to the client.

Now, let us generate the underlying code for the Protobuf classes and the gRPC classes. For doing that, we need to execute the following command −

python -m grpc_tools.protoc -I ..\common_proto_files\ --
python_out=../python --grpc_python_out=. greeting.proto

However, note that to execute the command, we need to install the correct dependency as mentioned in the setup section of the tutorial.

This should auto-generate the source code required for us to use gRPC. The source code would be placed under −

Protobuf class code: python/greeting_pb2.py
Protobuf gRPC code: python/greeting_pb2_grpcpb2.py

Setting up gRPC server

Now that we have defined the proto file which contains the function definition, let us setup a server which can call these functions.

Let us write our server code to serve the above function and save it in server.py

Example

from concurrent import futures

import grpc
import greeting_pb2
import greeting_pb2_grpc

class Greeter(greeting_pb2_grpc.GreeterServicer):
   def greet(self, request, context):
      print("Got request " + str(request))
      return greeting_pb2.ServerOutput(message='{0} {1}!'.format(request.greeting, request.name))
	  
def server():
   server = grpc.server(futures.ThreadPoolExecutor(max_workers=2))
   greeting_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
   server.add_insecure_port('[::]:50051')
   print("gRPC starting")
   server.start()
   server.wait_for_termination()
server()

The above code starts a gRPC server at a specified port and serves the functions and services which we had written in our proto file. Let us walk through the above code −

  • Starting from main method, we create a gRPC server at a specified port.

  • But before starting the server, we assign the server the service which we want to run, i.e., in our case, the Greeter service.

  • For this purpose, we need to pass the service instance to the server, so we go ahead and create a service instance, i.e., in our case, the Greeter.

  • The service instance need to provide an implementation of the method/function which is present in the .proto file, i.e., in our case, the greet method.

  • The method expects the an object of type as defined in the .proto file,i.e., for us, the request.

  • The method works on the above input, does the computation, and then is supposed to return the mentioned output in the .proto file, i.e., in our case, the ServerOutput.

Setting up gRPC client

Now that we have written the code for the server, let us setup a client which can call these functions.

Let us write our client code to call the above function and save it in client.py

Example

import grpc

import greeting_pb2
import greeting_pb2_grpc

def run():
   with grpc.insecure_channel('localhost:50051') as channel:
      stub = greeting_pb2_grpc.GreeterStub(channel)
      response = stub.greet(greeting_pb2.ClientInput(name='John', greeting = "Yo"))
   print("Greeter client received following from server: " + response.message)   
run()

The above code starts a gRPC server at a specified port and serves the functions and services which we had written in our proto file. Let us walk through the above code −

  • Starting from the main method, we have setup a Channel for gRPC communication with our server.

  • And then, we create a stub using the channel. This is where we use the service "Greeter" whose functions we plan to call. A stub is nothing but a wrapper which hides the complexity of the remote call from the caller.

  • Then, we simply create the expected input defined in the proto file, i.e., in our case, the ClientInput. We have hard-coded two arguments, i.e., name and the greeting.

  • We ultimately make the call and await the result from the server.

So, that is our client code.

Client Server Call

Now, that we have defined our proto file, written our server, and the client code, let us proceed and execute this code and see things in action.

For running the code, fire up two shells. Start the server on the first shell by executing the following command −

python .\server.py

Output

We would get the following output −

gRPC starting

The above output means that the server has started.

Now, let us start the client.

python .\client.py

We would see the following output −

Output

Greeter client received following from server: Yo John!

And now, if we open the server logs, we will get to see the following data −

gRPC starting
Got request greeting: "Yo"
name: "John"

So, as we see, the client was able to call the server as expected and the server responded with greeting the client back.

Advertisements