In today's rapidly evolving world of software development, building scalable and efficient distributed systems and microservices has become crucial for businesses to stay competitive. One of the key challenges in this realm is establishing seamless communication between various components of the system. Enter gRPC - a powerful communication protocol that has been gaining significant traction, especially when combined with Python.
In this blog, we will delve into the world of gRPC for Python and explore how it unlocks the potential of distributed systems and microservices. We'll cover the fundamental concepts, benefits, and provide practical examples of using gRPC to create high-performance APIs.
To get started, let's grasp the core concepts of gRPC. gRPC is an open-source, high-performance Remote Procedure Call (RPC) framework developed by Google. It enables efficient communication between different services running on various platforms and languages. The protocol uses Protocol Buffers (protobufs) as its Interface Definition Language (IDL), allowing for language-agnostic and compact data serialization.
Python is a versatile and widely-used programming language, known for its simplicity and readability. When combined with gRPC, Python becomes an excellent choice for building distributed systems and microservices. Some reasons to choose gRPC for Python are:
Let's dive into a practical example to see gRPC in action. We'll create a simple microservice architecture consisting of two services: a client that sends requests to a server, which in turn responds with the requested data.
Security is a paramount concern when dealing with distributed systems. In this section, we'll explore how to secure communications between gRPC services using Transport Layer Security (TLS) encryption.
As the number of services grows, load balancing and service discovery become critical components of the system architecture. We'll discuss how gRPC provides built-in support for load balancing and service discovery, making it easier to scale the microservices.
In a heterogeneous ecosystem, different technologies may coexist. gRPC offers solutions for interoperability between various frameworks, allowing Python-based services to communicate with non-Python services using the gRPC gateway and proxy.
Required packages i used
dnspython = "*"
grpcio-tools = "*"
pymongo = "*"
here is the basic proto file code
syntax = "proto3";
package unary;
service Unary{
rpc GetServerResponse(Message) returns (MessageResponse) {}
}
message Message{
string message = 1;
}
message MessageResponse{
string message = 1;
bool received = 2;
}
you can place the file in the protobufs folder
after this you must run the following command after creating the GRPC_PB2 folder
python -m grpc_tools.protoc -I ./protobufs --python_out=./GRPC_PB2 --grpc_python_out=./GRPC_PB2 ./protobufs/datafilter.proto
this will create two files in the GRPC_PB2 folder (datafilter_pb2.py and datafilter_pb2_grpc.py)
now we will create server side file named server.py with the following code.
import json
from concurrent import futures
import grpc
from pymongo import MongoClient
import GRPC_PB2.datafilter_pb2 as pb2
import GRPC_PB2.datafilter_pb2_grpc as pb2_grpc
class UnaryService(pb2_grpc.UnaryServicer):
def __init__(self, *args, **kwargs):
pass
def GetServerResponse(self, request, context):
# get the string from the incoming request
message = request.message
curser = MongoClient('mongodb://localhost:27017/admin')
student_coll = curser['localdb']['student']
print(f"==>> message: {message}")
query = list(student_coll.find({"name":f"{message}"},{"_id":0}))
query = json.dumps(query) if query else 'No Data Found'
result = {'message': query, 'received': True}
print(result)
return pb2.MessageResponse(**result)
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
pb2_grpc.add_UnaryServicer_to_server(UnaryService(), server)
server.add_insecure_port('[::]:50051')
server.start()
print("[+] GRPC server stated...")
server.wait_for_termination()
if __name__ == '__main__':
serve()
now we create client side code in client.py
import grpc
import GRPC_PB2.datafilter_pb2 as pb2
import GRPC_PB2.datafilter_pb2_grpc as pb2_grpc
# python -m grpc_tools.protoc --proto_path=. ./datafilter.proto --python_out=. --grpc_python_out=.
class UnaryClient(object):
"""
Client for gRPC functionality
"""
def __init__(self):
self.host = 'localhost'
self.server_port = 50051
# instantiate a channel
self.channel = grpc.insecure_channel(f'{self.host}:{self.server_port}')
# bind the client and the server
self.stub = pb2_grpc.UnaryStub(self.channel)
def get_url(self, message):
"""
Client function to call the rpc for GetServerResponse
"""
message = pb2.Message(message=message)
return self.stub.GetServerResponse(message)
if __name__ == '__main__':
print('[+] Connecting Student database..')
while True:
client = UnaryClient()
result = client.get_url(message=input("Enter Name: "))
print(f'{result}')
gRPC for Python brings a new dimension to building distributed systems and microservices. Its speed, efficiency, and language-agnostic nature make it a valuable tool for creating high-performance APIs and services. As you venture into the world of gRPC with Python, you'll find a vast community, extensive documentation, and a plethora of resources to support your journey. So why wait? Embrace gRPC and unlock the potential of your distributed applications!