RPC - Remote Procedure Call
Today we will try to understand
FYI: Understanding RPCs - found this an amazing post for RPC understanding. Mine is a concise version with an example.
Link for the code Github
Content
- What is RPC
- When to use RPC
- How to use RPC
- Simple Code example
- Path forward
RPC (Remote Procedure Call) can be considered as a simple API call, but the question is when to use this style of calling. Let's uncover this one by one with a simple example.
Example
ServiceA -> ServiceB
Data to transfer: customerName
and address
JSON Data:
{
"customerName": "Kanishk Verma",
"address": "Banglore Some Place, India"
}
JSON payload size: 65 bytes (UTF-8)
Now Rpc Data :
12 1B 42 61 6E 67 6C 6F 72 65 20 53 6F 6D 65 20 50 6C 61 63 65 2C 20 49 6E 64 69 61 ...
payload size 48 bytes
Benefits: Direct payload size reduction can lead to significant benefits when making millions of requests. However, this payload of bytes doesn't look readable. But it doesn't need to be readable as it's meant for machines to understand each other.
When to use RPC? When you want services to communicate with each other internally in a distributed environment, i.e., where you have thousands of microservices and millions of requests. Scale is the basic requirement of RPC. It has strong community support; for instance, Thrift is directly supported by Facebook/Meta.
How to Use? This is the most interesting part which should make your understanding clearer. But remember, practice makes a coder better. So after reading this, try to implement some calls on your own.
Now Steps involved in RPC :
- Using gRPC (Protocol buffer) define the messages and services which we will recieve
customer.proto
file which helps to serialize deserialize data and provide methods to call
syntax = "proto3";
package customer;
message Customer {
string customerName = 1;
string address = 2;
}
service CustomerService {
rpc GetCustomer (CustomerRequest) returns (Customer);
}
message CustomerRequest {
string customerName = 1;
}
protoc --python_out=. --grpc_python_out=. customer.proto
Use the Protobuf compiler (protoc) to generate code for your chosen language. For example, to generate Python code:
- Now simply generate server.py which serves the data
from concurrent import futures
import grpc
import customer_pb2
import customer_pb2_grpc
class CustomerService(customer_pb2_grpc.CustomerServiceServicer):
def GetCustomer(self, request, context):
# Mock data
if request.customerName == "Kanishk Verma":
return customer_pb2.Customer(
customerName="Kanishk Verma",
address="Banglore Some Place, India"
)
else:
context.set_details("Customer not found")
context.set_code(grpc.StatusCode.NOT_FOUND)
return customer_pb2.Customer()
def serve():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
customer_pb2_grpc.add_CustomerServiceServicer_to_server(CustomerService(), server)
server.add_insecure_port('[::]:50051')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
serve()
- client to call and get the data from server
import grpc
import customer_pb2
import customer_pb2_grpc
def run():
with grpc.insecure_channel('localhost:50051') as channel:
stub = customer_pb2_grpc.CustomerServiceStub(channel)
response = stub.GetCustomer(customer_pb2.CustomerRequest(customerName="Kanishk Verma"))
print("Customer details received: ", response)
if __name__ == '__main__':
run()
Summary of whats Happening :
- On the Client Side:
Define the Data Structure:
You define a .proto file that describes the data you want to send (like a blueprint). For example, it describes what a "CustomerRequest" and "Customer" should look like. Generate Code from the .proto File:
Use a tool to automatically create code from your .proto file. This code helps you easily create and manage your data structures in your programming language. Create and Populate the Request Object:
In your code, create an instance of the "CustomerRequest" object and fill it with the data you want to send (e.g., customer name). Serialize the Request:
Convert the "CustomerRequest" object into a compact binary format that can be sent over the network. This step is called serialization. Send the Serialized Request:
Send the serialized binary data to the server over the network.
On the Server Side:
Receive the Serialized Request:
The server receives the binary data sent by the client. Deserialize the Request:
Convert the binary data back into a "CustomerRequest" object that the server can work with. This step is called deserialization. Process the Request:
The server processes the request, for example, by looking up customer details in a database. Create and Populate the Response Object:
Create an instance of the "Customer" object and fill it with the response data (e.g., customer name and address). Serialize the Response:
Convert the "Customer" object into a binary format that can be sent back to the client. This step is serialization again, but for the response. Send the Serialized Response:
Send the serialized binary response data back to the client over the network. On the Client Side (Receiving the Response): Receive the Serialized Response:
The client receives the binary data sent by the server. Deserialize the Response:
Convert the binary data back into a "Customer" object that the client can work with. This step is deserialization. Use the Response Data:
The client now has the response data (e.g., customer name and address) and can use it as needed in the application.
Summary
Serialize: Convert your structured data into a compact binary format. Send: Transmit the serialized binary data over the network. Deserialize: Convert the received binary data back into structured data. These steps ensure that data can be efficiently transferred between systems while maintaining the structure and integrity of the information.
Hopefully you were able to understand something today and implement simple service. Although other protocol like Thrift and all are there but please get your hands dirty and feel free to connect with me on these tech discussions.
Comments