Of course! This is an excellent and very common use case in modern microservices architecture. Python and Go are a powerful combination: Python is often used for rapid development, data science, and scripting, while Go is prized for its performance, concurrency, and robustness.

Let's break down how to implement RPC (Remote Procedure Call) between Python and Go. We'll cover the most popular and practical methods.
Summary of Approaches
| Method | Protocol | Pros | Cons | Best For |
|---|---|---|---|---|
| gRPC | HTTP/2, Protobuf | High performance, strongly typed, code generation, streaming. | Steeper learning curve, requires .proto files. |
High-performance microservices, internal communication. |
| JSON-RPC 2.0 | HTTP/JSON | Simple, human-readable (JSON), widely supported. | Slower than gRPC, no built-in code generation. | Simpler services, web-friendly integrations. |
| Custom REST/HTTP | HTTP/JSON | Ubiquitous, easy to debug, tooling everywhere. | Not strictly RPC (stateless), can be more verbose. | When you need maximum interoperability and simplicity. |
Method 1: gRPC (Recommended for Performance)
gRPC is Google's high-performance RPC framework. It uses Protocol Buffers (Protobuf) to define the service interface and serialize data, which is much faster and more compact than JSON.
Step 1: Define the Service in a .proto File
This file is the contract between your Python and Go services. It's language-agnostic.
Create a file named greeter.proto:

// syntax = "proto3";
package greeter;
// The greeting service definition.
service Greeter {
// Sends a greeting
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// The request message containing the user's name.
message HelloRequest {
string name = 1;
}
// The response message containing the greetings.
message HelloReply {
string message = 1;
}
Step 2: Generate Code for Go
-
Install the Go Protobuf compiler plugins:
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28 go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2
-
Generate the Go code:
protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ greeter.protoThis will create
greeter.pb.goandgreeter_grpc.pb.go.
Step 3: Write the Go Server (server.go)
package main
import (
"context"
"log"
"net"
"google.golang.org/grpc"
pb "your_module_path" // Replace with the module path of your .proto file
)
// server is used to implement helloworld.GreeterServer.
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func main() {
lis, err := net.Listen("tcp", ":50051")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Println("gRPC server listening on :50051")
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
Step 4: Write the Python Client (client.py)
-
Install the Python gRPC tools:
pip install grpcio grpcio-tools
-
Generate the Python code from the
.protofile:python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. greeter.proto
This will create
greeter_pb2.pyandgreeter_pb2_grpc.py. -
Write the client script:
import grpc import greeter_pb2 import greeter_pb2_grpc def run(): # Connect to the server with grpc.insecure_channel('localhost:50051') as channel: # Create a stub (client) stub = greeter_pb2_grpc.GreeterStub(channel) # Create a request message request = greeter_pb2.HelloRequest(name='Python Client') # Make the RPC call response = stub.SayHello(request) print("Response from Go server: " + response.message) if __name__ == '__main__': run()
How to Run
- Start the Go server:
go run server.go
- In another terminal, run the Python client:
python client.py
Output:
- Go Server:
Received: Python Client - Python Client:
Response from Go server: Hello Python Client
Method 2: JSON-RPC 2.0 (Simpler & Web-Friendly)
JSON-RPC is a stateless, light-weight remote procedure call protocol. It's simpler than gRPC and uses standard JSON over HTTP.
Step 1: Write the Go Server (server.go)
We'll use the popular go-jsonrpc2 library.
-
Install the library:
go get github.com/sourcegraph/jsonrpc2
-
Write the server code:
package main import ( "encoding/json" "fmt" "log" "net/http" ) // Define a struct for our methods type GreeterService struct{} // This is our RPC method func (s *GreeterService) SayHello(args struct{ Name string }, reply *string) error { *reply = fmt.Sprintf("Hello, %s!", args.Name) return nil } // A handler to manage the JSON-RPC connection func (s *GreeterService) ServeHTTP(w http.ResponseWriter, r *http.Request) { // We expect a POST request if r.Method != http.MethodPost { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } var req json.RawMessage if err := json.NewDecoder(r.Body).Decode(&req); err != nil { http.Error(w, "Invalid JSON", http.StatusBadRequest) return } // Create a dispatcher and register our service dispatcher := jsonrpc2.NewServer(&jsonrpc2.Handler{ Method: "greeter.sayHello", Handle: func(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) (interface{}, error) { var args struct{ Name string } if err := json.Unmarshal(req.Params, &args); err != nil { return nil, err } var reply string err = s.SayHello(args, &reply) return reply, err }, }) // Serve the request dispatcher.ServeHTTP(w, r) } func main() { http.Handle("/rpc", GreeterService{}) log.Println("JSON-RPC server listening on :8080") log.Fatal(http.ListenAndServe(":8080", nil)) }Note: The above example uses a more manual approach with
net/httpfor clarity. Libraries likego-jsonrpc2can also be used with WebSockets for a more persistent connection.
Step 2: Write the Python Client (client.py)
Python has a built-in json library, and we can use the requests library for HTTP.
-
Install requests:
pip install requests
-
Write the client script:
import json import requests url = "http://localhost:8080/rpc" # The JSON-RPC 2.0 request payload payload = { "jsonrpc": "2.0", "method": "greeter.sayHello", # Must match the method on the server "params": {"name": "Python Client"}, # Positional or named arguments "id": 1 # A request ID } headers = {'content-type': 'application/json'} response = requests.post(url, data=json.dumps(payload), headers=headers) # Parse the response result = response.json() if 'result' in result: print("Response from Go server:", result['result']) else: print("Error:", result['error'])How to Run
-
Start the Go server:
go run server.go
-
In another terminal, run the Python client:
python client.py
Output:
- Python Client:
Response from Go server: Hello, Python Client!
Method 3: Custom REST/HTTP (The Ubiquitous Approach)
This isn't strictly RPC in the classic sense (it's stateless HTTP), but it's the most common way services communicate. Python's requests and Go's net/http make this trivial.
Go Server (server.go)
package main
import (
"encoding/json"
"log"
"net/http"
)
type GreetingResponse struct {
Message string `json:"message"`
}
func helloHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
name := r.URL.Query().Get("name")
if name == "" {
name = "World"
}
response := GreetingResponse{Message: "Hello, " + name}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(response)
}
func main() {
http.HandleFunc("/hello", helloHandler)
log.Println("HTTP server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", nil))
}
Python Client (client.py)
import requests
url = "http://localhost:8080/hello?name=Python Client"
try:
response = requests.get(url)
response.raise_for_status() # Raise an exception for bad status codes
data = response.json()
print("Response from Go server:", data['message'])
except requests.exceptions.RequestException as e:
print(f"Error: {e}")
Recommendation
- For new, high-performance microservices: Use gRPC. The initial setup is more work, but the performance gains, type safety, and generated code are worth it for internal services.
- For simpler services or web integration: Use JSON-RPC 2.0. It's a great balance of simplicity and power, and it feels more "web-native" than gRPC.
- For maximum simplicity or quick prototypes: Use REST/HTTP. It's the most straightforward and universally understood method.
