杰瑞科技汇

Python与Go的RPC该如何选择?

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.

Python与Go的RPC该如何选择?-图1
(图片来源网络,侵删)

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:

Python与Go的RPC该如何选择?-图2
(图片来源网络,侵删)
// 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

  1. 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
  2. Generate the Go code:

    protoc --go_out=. --go_opt=paths=source_relative \
           --go-grpc_out=. --go-grpc_opt=paths=source_relative \
           greeter.proto

    This will create greeter.pb.go and greeter_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)

  1. Install the Python gRPC tools:

    pip install grpcio grpcio-tools
  2. Generate the Python code from the .proto file:

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

    This will create greeter_pb2.py and greeter_pb2_grpc.py.

  3. 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

  1. Start the Go server:
    go run server.go
  2. 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.

  1. Install the library:

    go get github.com/sourcegraph/jsonrpc2
  2. 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/http for clarity. Libraries like go-jsonrpc2 can 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.

  1. Install requests:

    pip install requests
  2. 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

  3. Start the Go server:

    go run server.go
  4. 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.
分享:
扫描分享到社交APP
上一篇
下一篇