Published on

RPC(Remote Procedure Call) with Python example

Authors
  • avatar
    Name
    Gene Zhang
    Twitter

RPC

A Remote Procedure Call (RPC) allows a program to execute a procedure on another computer or server as if it were a local function call, abstracting away the complexities of network communication.

from xmlrpc.server import SimpleXMLRPCServer

def add(a, b):
    return a + b

def multiply(a, b):
    return a * b

server = SimpleXMLRPCServer(("localhost", 8000))
print("Listening on port 8000...")
server.register_function(add, "add")
server.register_function(multiply, "multiply")
server.serve_forever()

import xmlrpc.client

with xmlrpc.client.ServerProxy("http://localhost:8000/") as proxy:
    print("3 + 5 = %d" % proxy.add(3, 5))
    print("3 * 5 = %d" % proxy.multiply(3, 5))

gRPC

RPC vs gRPC

FeaturegRPCRPC (General)
Data SerializationProtocol Buffers (binary)Various (JSON, XML, Protobuf)
Transport ProtocolHTTP/2 (primarily)Varies (TCP, UDP, HTTP)
PerformanceGenerally fasterVaries depending on implementation
StreamingSupports streamingMay or may not support streaming
ComplexitySteeper learning curveVaries, can be simpler
Language SupportGood support for many languagesVaries, depends on implementation
MicroservicesWell-suited for microservicesCan be used for microservices, but may not be the best choice

gRPC Python Example

First, define the service in a .proto file:

// calculator.proto
syntax = "proto3";

package calculator;

service Calculator {
  rpc Add (AddRequest) returns (AddResponse);
  rpc Multiply (MultiplyRequest) returns (MultiplyResponse);
}

message AddRequest {
  int32 a = 1;
  int32 b = 2;
}

message AddResponse {
  int32 result = 1;
}

message MultiplyRequest {
  int32 a = 1;
  int32 b = 2;
}

message MultiplyResponse {
  int32 result = 1;
}

Generate Python code from the proto file:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. calculator.proto

gRPC Server implementation:

import grpc
from concurrent import futures
import calculator_pb2
import calculator_pb2_grpc

class CalculatorServicer(calculator_pb2_grpc.CalculatorServicer):
    def Add(self, request, context):
        result = request.a + request.b
        return calculator_pb2.AddResponse(result=result)

    def Multiply(self, request, context):
        result = request.a * request.b
        return calculator_pb2.MultiplyResponse(result=result)

def serve():
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    calculator_pb2_grpc.add_CalculatorServicer_to_server(
        CalculatorServicer(), server
    )
    server.add_insecure_port('[::]:50051')
    server.start()
    print("Server started, listening on port 50051...")
    server.wait_for_termination()

if __name__ == '__main__':
    serve()

gRPC Client implementation:

import grpc
import calculator_pb2
import calculator_pb2_grpc

def run():
    with grpc.insecure_channel('localhost:50051') as channel:
        stub = calculator_pb2_grpc.CalculatorStub(channel)

        # Call Add
        add_request = calculator_pb2.AddRequest(a=3, b=5)
        add_response = stub.Add(add_request)
        print(f"3 + 5 = {add_response.result}")

        # Call Multiply
        multiply_request = calculator_pb2.MultiplyRequest(a=3, b=5)
        multiply_response = stub.Multiply(multiply_request)
        print(f"3 * 5 = {multiply_response.result}")

if __name__ == '__main__':
    run()

Installation:

pip install grpcio grpcio-tools

In essence: gRPC is a modern, high-performance RPC framework built for efficient communication, especially in distributed systems and microservices. While traditional RPC is a broader concept, it can be less performant than gRPC due to its reliance on potentially less efficient data formats and transport protocols.