Using gRPC API (Python) to make a call
Requirement:
Python 3.x version
IDE (Suggestion VS code)
pip
version 9.0.1 or higher
Pre-requisite knowledge:
Basic understanding of RPC
Basic understanding of protocol buffers
What is gRPC?
Imagine you want to communicate with a friend who only speaks a different language. You need a translator, but not just any translator. You need one that can translate quickly and efficiently, without losing any meaning or context. That's where gRPC comes in.
gRPC is like having a super-efficient translator for communication between different parts of a computer program, often across different systems or even different languages. It's a framework developed by Google that allows you to define and manage remote procedure calls (RPC) in a way that's fast, reliable, and language-agnostic.
Here's a breakdown:
Language Agnostic Communication: Just like a good translator can handle multiple languages, gRPC allows different parts of your software, written in different programming languages, to communicate seamlessly.
Efficient Communication: Think of gRPC as a high-speed train compared to traditional methods like HTTP, which is like a slow-moving truck. gRPC is designed to be fast and efficient, making communication between different parts of your system quick and responsive.
Protocol Buffers (Protobuf): Protobuf is like a universal language that gRPC uses to define the messages sent between different parts of your system. It's like having a standardized format for communication, ensuring that everyone understands each other perfectly. We will understand it more in the next section.
Streaming Support: Imagine having a real-time conversation with your friend instead of just sending letters back and forth. gRPC supports streaming, allowing continuous communication between different parts of your system, like a phone call instead of a series of text messages.
Error Handling and Status Codes: Just like a good translator can handle misunderstandings or mistakes in translation, gRPC provides robust error handling and status codes, ensuring that communication issues are handled gracefully.
gRPC lets you define four kinds of service methods, all of which are used in the service:
A simple RPC where the client sends a request to the server using the stub and waits for a response to come back, just like a normal function call.
A response-streaming RPC is where the client sends a request to the server and gets a stream to read a sequence of messages back. The client reads from the returned stream until there are no more messages. As you can see in the example, you specify a response-streaming method by placing the
stream
keyword before the response type.A request-streaming RPC where the client writes a sequence of messages and sends them to the server, again using a provided stream. Once the client has finished writing the messages, it waits for the server to read them all and return its response. You specify a request-streaming method by placing the
stream
keyword before the request type.A bidirectionally-streaming RPC where both sides send a sequence of messages using a read-write stream. The two streams operate independently, so clients and servers can read and write in whatever order they like: for example, the server could wait to receive all the client messages before writing its responses, or it could alternately read a message then write a message, or some other combination of reads and writes. The order of messages in each stream is preserved. You specify this type of method by placing the
stream
keyword before both the request and the response.
For more information please refer here.
Protocol Buffers:
Protocol Buffers (protobuf) are a way of encoding structured data in a format that's both efficient and language-independent. Think of them as a universal language for data.
Here's a breakdown:
Universal Language: Just like Esperanto is a constructed language designed to be easy for people from different backgrounds to understand, protobuf is a structured data format designed to be easy for different software systems to understand and exchange.
Efficient Encoding: Imagine you have a recipe written in a language that only you understand. You want to share it with others, but it's long and complicated. Protobuf is like condensing that recipe into a simpler, more compact format that's still easy for others to understand. It's like turning a long novel into a short summary that captures all the important information.
Language Independence: Just as math is the same in every language, protobuf allows data to be represented in a way that's independent of the programming language being used. It's like writing a mathematical equation that anyone, regardless of their native language, can understand and solve.
Schema Definition: Protobuf uses a schema, which is like a blueprint or template for how data should be structured. This schema defines the types of data that can be included and how they should be arranged. It's like having a form to fill out where each field has a specific purpose and format.
Efficient Transmission: Imagine you're sending a message to someone far away. Instead of writing a long letter with lots of unnecessary details, you use a telegraph to send a concise, encoded message. Protobuf works similarly, encoding data in a way that's efficient for transmission over networks or storage on disk.
Versioning and Evolution: Just as languages evolve over time, software systems also change. Protobuf supports versioning and evolution, allowing data schemas to be updated without breaking compatibility with older versions. It's like being able to update a dictionary to include new words without rendering older editions obsolete.
In summary, protobuf is like a universal, efficient, and adaptable language for encoding structured data, making it easy for different software systems to communicate and exchange information effectively.
For more information please refer here.
Making RPC Using gRPC API-Python:
Now we will understand how to make a remote procedure call using gRPC, there are some set of instructions.
Install gRPC:
python -m pip install grpcio
Or, to install it system wide:
sudo python -m pip install grpcio
gRPC tools
Python’s gRPC tools include the protocol buffer compiler protoc
and the special plugin for generating server and client code from .proto
service definitions.
To install gRPC tools, run:
python -m pip install grpcio-tools
Step-1 Define a Service:
import math
# Function to calculate the square root of a number
def square_root(x):
# Using the sqrt function from the math module to compute the square root
return math.sqrt(x)
Here's what each part does:
import math
: This imports the math module, which provides mathematical functions and constants.def square_root(x):
: This defines a function namedsquare_root
that takes one argument,x
.return math.sqrt(x)
: This line calculates the square root of the inputx
using thesqrt
function from themath
module and returns the result.
This file defines a utility function for calculating the square root, which can be used by both the client and server implementations.
Step-2 Create a Proto file:
syntax = 'proto3';
// Define a message called Sample
message Sample {
// Define a field 'value' of type float with field number 1
float value = 1;
}
// Define a service called calculator
service Calculator {
// Define an RPC method called SquareRoot that takes a Sample request and returns a Sample response
rpc SquareRoot(Sample) returns (Sample) {}
}
Here's what each part does:
syntax = 'proto3';
: This line specifies that the protocol buffer should be parsed according to the syntax of version 3.message Sample { ... }
: This defines a message type namedSample
. Messages are used to define the structure of data that can be exchanged using gRPC.float value = 1;
: This line defines a field namedvalue
with typefloat
and a field number1
within theSample
message. Field numbers must be unique within a message and are used for identifying fields in the binary representation of the message.service Calculator { ... }
: This defines a service namedCalculator
. Services in gRPC are used to define RPC methods that can be called remotely.rpc SquareRoot(Sample) returns (Sample) {}
: This line defines an RPC method namedSquareRoot
within theCalculator
service. It takes aSample
request message and returns aSample
response message. RPC methods in gRPC define the operations that clients can perform remotely on the server.
If you see any error in VS code you can use some extension:
Step-3 Generating Python Files from Proto file:
Generating Python files from a Protocol Buffers (protobuf) file serves a crucial purpose in developing applications that use gRPC (Google Remote Procedure Call) for communication between different parts of the system. Here's a simplified explanation of why we need to do this and how it works:
Defining Service Interfaces: The protobuf file (
Sample.proto
in your case) contains the definition of your service interface and the message types used for communication. It defines the structure of the data exchanged between different parts of your system.Language Agnostic: Protocol Buffers are language-agnostic, meaning you can define your service interface once and generate code in multiple programming languages. This allows you to have interoperability between different parts of your system implemented in different languages.
Code Generation: When you run the
protoc
command with appropriate plugins (-python_out
for Python code generation and-grpc_python_out
for gRPC-specific code generation), it reads your protobuf file and generates Python code that represents your service interface and message types.Boilerplate Code: The generated Python code includes classes and methods that handle serialization and deserialization of data according to the specified message format. It also includes server and client classes that handle the communication logic for gRPC calls.
Integration with Your Application: Once the Python code is generated, you can integrate it into your application. You use these generated classes to implement your gRPC server and client logic. The generated code provides a convenient and type-safe way to work with your service interface.
Compile-Time Safety: By generating code from the protobuf file, you ensure that your server and client code adhere to the service interface specified in the protobuf file. This provides compile-time safety, catching errors early in the development process.
Run the below command to generate Python file:
protoc -I=. --python_out=. --grpc_python_out=. Sample.proto
After running this there will be 2 generated pyhton files.
Step-4 Define a Server:
import grpc
import Sample_pb2
import Sample_pb2_grpc
import time
from concurrent import futures
# Importing the utility function for calculating the square root
import calculator
# Define a class that implements the calculator service defined in Sample_pb2_grpc
class calculatorServicer(Sample_pb2_grpc.calculatorServicer):
# Implementation of the SquareRoot RPC method
def SquareRoot(self, request, context):
# Create a response object
response = Sample_pb2.Sample()
# Call the square_root function from the calculator module to compute the square root
response.value = calculator.square_root(request.value)
# Return the response
return response
# Create a gRPC server
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# Add the CalculatorServicer to the server
Sample_pb2_grpc.add_calculatorServicer_to_server(calculatorServicer(), server)
# Open port 50051 for insecure communication
server.add_insecure_port('[::]:50051')
# Start the server
server.start()
# Keep the server running until terminated
server.wait_for_termination()
Here's what each part does:
import grpc
,import Sample_pb2
,import Sample_pb2_grpc
,import time
,from concurrent import futures
: These lines import necessary modules for implementing the gRPC server.import calculator
: This line imports the utility function for calculating the square root, which will be used in the implementation of theSquareRoot
RPC method.class CalculatorServicer(Sample_pb2_grpc.CalculatorServicer):
: This defines a classCalculatorServicer
that implements the gRPC service defined inSample_pb2_grpc
.def SquareRoot(self, request, context):
: This defines the implementation of theSquareRoot
RPC method. It takes a request of typeSample
and acontext
object. It computes the square root of the value in the request and returns a response.server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
: This line creates a gRPC server with a thread pool executor that can handle up to 10 concurrent RPC calls.Sample_pb2_grpc.add_CalculatorServicer_to_server(CalculatorServicer(), server)
: This line adds theCalculatorServicer
to the gRPC server.server.add_insecure_port('[::]:50051')
: This line opens port 50051 for insecure communication. Note that for production use, you should use secure communication with SSL/TLS.server.start()
: This line starts the gRPC server.server.wait_for_termination()
: This line keeps the server running until it's terminated.
Step-5 Define a Client:
import grpc
import Sample_pb2_grpc
import Sample_pb2
# Open a gRPC channel to the server
channel = grpc.insecure_channel('localhost:50051')
# Create a stub for the calculator service
stub = Sample_pb2_grpc.calculatorStub(channel)
# Create a request message
request = Sample_pb2.Sample(value=16)
# Make the RPC call to the server
response = stub.SquareRoot(request)
# Print the response
print(response.value)
Here's what each part does:
import grpc
,import Sample_pb2_grpc
,import Sample_pb2
: These lines import necessary modules and generated code for working with gRPC.channel = grpc.insecure_channel('
localhost:50051
')
: This line opens a gRPC channel to the server running on localhost at port 50051. Note thatinsecure_channel
is used here for simplicity, but in a production environment, you should use secure channels with SSL/TLS.stub = Sample_pb2_grpc.calculatorStub(channel)
: This line creates a stub, which is used to call RPC methods on the server. The calculatorStub class is generated from the protobuf file and provides methods corresponding to each RPC method defined in theCalculator
service.request = Sample_pb2.Sample(value=16)
: This line creates a request message of typeSample
, with a value of 16. TheSample
message type is generated from the protobuf file and corresponds to the message used in theSquareRoot
RPC method.response = stub.SquareRoot(request)
: This line makes the RPC call to the server'sSquareRoot
method with the given request and stores the response.print(response.value)
: This line prints the value received in the response message, which represents the square root of the value sent in the request.
Run Server:
python server.py
Run Client:
python client.py
You Will see output as : 4.0
Conclusion:
In conclusion, the utilization of gRPC API with Python offers a robust framework for efficient communication between various components of a software system. By leveraging gRPC, developers can overcome language barriers and ensure fast, reliable, and language-agnostic communication.
The key features of gRPC, including language-agnostic communication, efficient transmission, and support for streaming, enable seamless interaction between different parts of a system. Moreover, the integration of Protocol Buffers adds a layer of efficiency and adaptability, making data exchange straightforward and independent of programming languages.
By following the outlined steps, developers can easily implement remote procedure calls using the gRPC API in Python. From defining services and messages to generating Python files and implementing server and client logic, the process ensures a smooth and structured approach to building distributed systems.
Overall, gRPC API in Python opens up opportunities for building scalable and interoperable software solutions, facilitating efficient communication and enhancing the performance of distributed systems. Embracing gRPC empowers developers to create robust and resilient applications that meet the demands of modern software development.
Subscribe to my newsletter
Read articles from Harshit Nagpal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by