本项目演示了如何使用JSON-RPC协议实现Go语言和Python之间的跨语言通信。项目包含两种场景的完整实现,展示了双向的RPC调用能力。
项目概述
JSON-RPC是一种轻量级的远程过程调用协议,使用JSON作为数据交换格式。相比gRPC,JSON-RPC更加简单易用,特别适合跨语言通信场景。
本demo项目实现了一个简单的算术运算服务,包含乘法(Multiply)和加法(Plus)两个方法,展示了以下两种场景:
- 场景一:Go语言作为服务器,Python作为客户端
- 场景二:Python作为服务器,Go语言作为客户端
场景一:Go服务器 + Python客户端
Go服务器实现
Go服务器使用标准库中的net/rpc
和net/rpc/jsonrpc
包来实现JSON-RPC服务。
文件:json_rpc_server.go
package main
import (
"log"
"net"
"net/rpc"
"net/rpc/jsonrpc"
)
type Arith int
type Args struct {
A, B int
}
// 乘法
func (t *Arith) Mulitply(args *Args, reply *int) error {
*reply = args.A * args.B
return nil
}
func (t *Arith) Plus(args *Args, reply *int) error {
*reply = args.A + args.B
return nil
}
func main() {
addr := ":1234"
server := rpc.NewServer()
server.Register(new(Arith))
l, e := net.Listen("tcp", addr)
if e != nil {
log.Fatalln("listen error:", e)
} else {
log.Println("rpc listening ", addr)
}
defer l.Close()
for {
conn, err := l.Accept()
if err != nil {
log.Print("rpc.Serve: accept:", err.Error())
return
}
go server.ServeCodec(jsonrpc.NewServerCodec(conn))
}
}
Python客户端实现
Python客户端使用socket和json标准库实现JSON-RPC客户端功能。
文件:json_rpc_client.py
import json
import socket
import itertools
class RPCClient(object):
def __init__(self, addr, codec=json):
self._socket = socket.create_connection(addr)
self._id_iter = itertools.count()
self._codec = codec
def _message(self, name, *params):
return dict(id=next(self._id_iter),
params=list(params),
method=name)
def call(self, name, *params):
req = self._message(name, *params)
id = req.get('id')
"""
Golang Rpc 返回的Json格式
type serverResponse struct {
Id *json.RawMessage `json:"id"`
Result interface{} `json:"result"`
Error interface{} `json:"error"`
}
"""
mesg = self._codec.dumps(req)
mesg = mesg.encode(encoding='utf-8')
self._socket.sendall(mesg)
# This will actually have to loop if resp is bigger
resp = self._socket.recv(4096)
resp = resp.decode(encoding='utf-8')
resp = self._codec.loads(resp)
if resp.get('id') != id:
raise Exception("expected id=%s, received id=%s: %s"
%(id, resp.get('id'), resp.get('error')))
if resp.get('error') is not None:
raise Exception(resp.get('error'))
return resp.get('result')
def close(self):
self._socket.close()
if __name__ == '__main__':
rpc = RPCClient(("127.0.0.1", 1234))
args = {'A':203, 'B':3}
print(rpc.call("Arith.Mulitply",args))
print(rpc.call("Arith.Plus",args))
运行场景一
- 启动Go服务器:
go run json_rpc_server.go
- 运行Python客户端:
python json_rpc_client.py
输出结果:
609
206
场景二:Python服务器 + Go客户端
Python服务器实现
Python服务器使用多线程处理并发连接,实现了完整的JSON-RPC协议。
文件:py_server_go_client/json_rpc_server.py
import json
import socket
import threading
# Define the arithmetic service
class ArithService:
def Multiply(self, args):
return args['A'] * args['B']
def Plus(self, args):
return args['A'] + args['B']
# JSON-RPC Server
class JSONRPCServer:
def __init__(self, host='0.0.0.0', port=1234):
self.host = host
self.port = port
self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
self.socket.bind((host, port))
self.socket.listen(5)
self.running = False
self.services = {}
def register_service(self, name, service):
self.services[name] = service
def handle_client(self, client_socket):
while True:
try:
# Read request
data = client_socket.recv(4096)
if not data:
break
# Parse request
request = json.loads(data.decode('utf-8'))
method_name = request.get('method')
params = request.get('params', [])
request_id = request.get('id')
# Process request
result = None
error = None
try:
if '.' in method_name:
service_name, method = method_name.split('.', 1)
service = self.services.get(service_name)
if service and hasattr(service, method):
method_func = getattr(service, method)
result = method_func(params[0])
else:
error = f"Method not found: {method_name}"
else:
error = f"Invalid method format: {method_name}"
except Exception as e:
error = str(e)
# Send response
response = {
'id': request_id,
'result': result,
'error': error
}
client_socket.sendall(json.dumps(response).encode('utf-8'))
except Exception as e:
print(f"Error handling client request: {e}")
break
client_socket.close()
def start(self):
self.running = True
print(f"JSON-RPC server listening on {self.host}:{self.port}")
try:
while self.running:
client_socket, _ = self.socket.accept()
client_thread = threading.Thread(target=self.handle_client, args=(client_socket,))
client_thread.daemon = True
client_thread.start()
except KeyboardInterrupt:
self.stop()
def stop(self):
self.running = False
self.socket.close()
print("Server stopped")
if __name__ == '__main__':
# Create server
server = JSONRPCServer()
# Register services
server.register_service('Arith', ArithService())
# Start server
try:
server.start()
except KeyboardInterrupt:
server.stop()
Go客户端实现
Go客户端使用标准库的net/rpc/jsonrpc
包连接Python服务器。
文件:py_server_go_client/json_rpc_client.go
package main
import (
"fmt"
"log"
"net/rpc/jsonrpc"
)
// Args represents the arguments for arithmetic operations
type Args struct {
A int `json:"A"`
B int `json:"B"`
}
func main() {
// Connect to the Python JSON-RPC server
client, err := jsonrpc.Dial("tcp", "127.0.0.1:1234")
if err != nil {
log.Fatal("dialing:", err)
}
defer client.Close()
// Prepare arguments
args := &Args{A: 203, B: 3}
// Call Multiply method
var multiplyResult int
err = client.Call("Arith.Multiply", args, &multiplyResult)
if err != nil {
log.Fatal("Multiply error:", err)
}
fmt.Printf("Multiply: %d * %d = %d\n", args.A, args.B, multiplyResult)
// Call Plus method
var plusResult int
err = client.Call("Arith.Plus", args, &plusResult)
if err != nil {
log.Fatal("Plus error:", err)
}
fmt.Printf("Plus: %d + %d = %d\n", args.A, args.B, plusResult)
}
运行场景二
- 启动Python服务器:
cd py_server_go_client
python json_rpc_server.py
- 运行Go客户端:
cd py_server_go_client
go run json_rpc_client.go
输出结果:
Multiply: 203 * 3 = 609
Plus: 203 + 3 = 206
技术要点
JSON-RPC协议格式
请求格式:
{
"id": 1,
"method": "Arith.Multiply",
"params": [{"A": 203, "B": 3}]
}
响应格式:
{
"id": 1,
"result": 609,
"error": null
}
实现特点
- Go服务器:使用官方RPC库,支持方法注册和自动路由
- Python服务器:手动实现JSON-RPC协议,支持服务注册和多线程处理
- 跨语言兼容:两种语言的客户端和服务器可以互相通信
- 错误处理:包含完整的错误处理机制
- 并发支持:服务器支持多客户端并发访问
总结
本项目展示了JSON-RPC在跨语言通信中的实用性。相比gRPC,JSON-RPC具有以下优势:
- 简单易用:协议简单,易于理解和实现
- 跨语言友好:基于JSON,几乎所有语言都有很好的支持
- 调试方便:可读的JSON格式便于调试
- 轻量级:协议开销小,适合简单的RPC场景
通过本demo,可以快速上手跨语言RPC开发,为分布式系统的不同语言组件提供通信基础。