Go语言的rpc包的路径为net/rpc:
package main import ( "net" "net/rpc" ) type HelloWorldService struct { } func (s *HelloWorldService) HelloWorld(request string, response *string) error { *response = "hello" + request return nil } func main() { _ = rpc.RegisterName("HelloWorldService", &HelloWorldService{}) listener, err := net.Listen("tcp", ":8000") if err != nil { panic("监听端口失败!") } conn, err := listener.Accept() if err != nil { panic("建立连接失败!") } rpc.ServeConn(conn) }
package main import ( "fmt" "log" "net/rpc" ) func main() { client, err := rpc.Dial("tcp", "127.0.0.1:8000") if err != nil { log.Fatal("dial", err) } var response string err = client.Call("HelloWorldService.HelloWorld", "world", &response) if err != nil { log.Fatal("caller", err) } fmt.Println(response) }
上述RPC默认采用的是Go语言特有的gob编码,所以其它语言调用Go语言实现的RPC服务相对困难。比较常见的有基于json实现的RPC服务,所以如果使用json来替换gob将会使其通用性变的更强。在Go语言中可以通过net/rpc/jsonrpc来基于json实现RPC,实现跨语言调用。
package main import ( "net" "net/rpc" "net/rpc/jsonrpc" ) type HelloWorldService struct { } func (s *HelloWorldService) HelloWorld(request string, response *string) error { *response = "hello" + request return nil } func main() { _ = rpc.RegisterName("HelloWorldService", &HelloWorldService{}) listener, err := net.Listen("tcp", ":8000") if err != nil { panic("监听端口失败!") } // 不断的接收新的请求 for { conn, err := listener.Accept() if err != nil { panic("建立连接失败!") } go rpc.ServeCodec(jsonrpc.NewServerCodec(conn)) // 启动协程处理请求 } }
这与之前相比使用rpc.ServeCodec函数替代了rpc.ServeConn函数,传入符合json编码的参数。
package main import ( "fmt" "log" "net" "net/rpc" "net/rpc/jsonrpc" ) func main() { conn, err := net.Dial("tcp", "127.0.0.1:8000") if err != nil { log.Fatal("dial", err) } client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn)) var response string err = client.Call("HelloWorldService.HelloWorld", "world", &response) if err != nil { log.Fatal("caller", err) } fmt.Println(response) }
这与之前相比通过net进行拨号建立连接,然后通过NewClientWithCodec函数传入符合json编码的参数。
既然使用的是json进行编、解码,那么就具备一定的通用性,可以使用其它语言来进行调用,比如使用Python客户端进行调用,但是Go的RPC监听的是TCP连接,如果使用Python中的requests包是行不通的,它使用的是HTTP协议,会携带很多比如请求头之类的多余信息,所以使用socket编程发送请求:
import json import socket # 发送的数据格式必须满足这样的 """ method: 服务名称、方法名称 result:返回的结果 id:随意指定一个值,如果不指定,返回值id为None """ request = { "method": "HelloWorldService.HelloWorld", "params": ["bily"], "id": 0 } client = socket.create_connection(("127.0.0.1", 8000)) client.sendall(json.dumps(request).encode()) # 设置一次性接收的数据大小 response = client.recv(4096) response = json.loads(response.decode()) print(response) # 关闭连接 client.close()
请求结果:
{'id': 0, 'result': 'hellobily', 'error': None}
package main import ( "io" "net/http" "net/rpc" "net/rpc/jsonrpc" ) type HelloWorldService struct { } func (s *HelloWorldService) HelloWorld(request string, response *string) error { *response = "hello" + request return nil } func main() { _ = rpc.RegisterName("HelloWorldService", new(HelloWorldService)) http.HandleFunc("/jsonrpc", func(writer http.ResponseWriter, request *http.Request) { var conn io.ReadWriteCloser = struct { io.Writer io.ReadCloser }{ ReadCloser: request.Body, Writer: writer, } _ = rpc.ServeRequest(jsonrpc.NewServerCodec(conn)) }) _ = http.ListenAndServe(":8000", nil) }
import requests request = { "method": "HelloWorldService.HelloWorld", "params": ["karry"], "id": 0 } res = requests.post("http://127.0.0.1:8000/jsonrpc", json=request) print(res.text)
在之前的调用中,存在一些明显的问题:
这样就引出服务端代理Server Stub和客户端代理Client Stub:
├─client │ client.go │ ├─client_stub │ client_stub.go │ ├─handler │ handler.go │ ├─server │ server.go │ └─server_stub server_stub.go
package main import ( "fmt" "go_rpc_project/stub_rpc/client_stub" ) func main() { // 建立连接 client := client_stub.NewHelloWorldServiceClient("tcp", "127.0.0.1:8000") // 调用业务函数HelloWorld var response string _ = client.HelloWorld("harry", &response) fmt.Println(response) }
package client_stub import ( "go_rpc_project/stub_rpc/handler" "log" "net/rpc" ) type HelloWorldServiceStub struct { *rpc.Client } func NewHelloWorldServiceClient(protcol string, address string) HelloWorldServiceStub { conn, err := rpc.Dial(protcol, address) if err != nil { log.Fatal("拨号错误", err) } return HelloWorldServiceStub{conn} } func (c *HelloWorldServiceStub) HelloWorld(request string, response *string) error { err := c.Call(handler.HelloWorldServiceName+".HelloWorld", request, response) if err != nil { log.Fatal("调用服务失败", err) } return nil }
package handler const HelloWorldServiceName = "handler/HelloWorldService" type HelloWorldService struct { } func (s *HelloWorldService) HelloWorld(request string, response *string) error { *response = "hello" + request return nil }
package main import ( "go_rpc_project/stub_rpc/handler" "go_rpc_project/stub_rpc/server_stub" "net" "net/rpc" ) func main() { // 实例化一个server listener, _ := net.Listen("tcp", ":8000") // 注册,将业务逻辑代码注册到代理中 _ = server_stub.RegisterServicer(&handler.HelloWorldService{}) // 启动服务 for { conn, _ := listener.Accept() go rpc.ServeConn(conn) } }
package server_stub import ( "go_rpc_project/stub_rpc/handler" "net/rpc" ) type HelloWorldServicer interface { HelloWorld(request string, response *string) error } func RegisterServicer(srv HelloWorldServicer) error { return rpc.RegisterName(handler.HelloWorldServiceName, srv) }