实现目标
- 原生rpc使用是直接写死方法名进行调用, 但实际中我们使用其实是想像调用本地方法一样直接调用的
- 以下我们就参考grpc的大致流程对go原生rpc进行一定的封装: grpc的大致流程主要分为, 客户端, 客户端stub(类似于代理), 服务端, 服务端stub, socket通信
原生rpc写法和我们要实现的目标写法
// 原生rpc调用方式, 使用call方法进行写死调用
var reply string
_ = dial.Call("HelloService.Hello", "rpc2", &reply)
fmt.Println(reply)
// 要实现的目标调用, 直接调用hello
if err := hello.Hello("rpc", &reply); err != nil {
fmt.Println(err)
}
client和server公共部分
// 增加handler/handler.go HelloService名称
package handler
var HelloServiceName = "handler/HelloService"
客户端实现
- 客户端代理部分
- 增加client_stub/client.sub.go 实现一个client 调用的代理
package client_stub
import (
"net/rpc"
"new_helloword/handler"
)
type HelloServiceStub struct {
*rpc.Client
}
func NewHelloServiceStub(network, address string) *HelloServiceStub {
dial, err := rpc.Dial(network, address)
if err != nil {
panic(err)
}
return &HelloServiceStub{
dial,
}
}
func (s *HelloServiceStub) Hello(request string, reply *string) (err error) {
// 调用原方法
err = s.Call(handler.HelloServiceName+".Hello", request, reply)
return
}
- 客户端调用部分
func main() {
helloStub := client_stub.NewHelloServiceStub("tcp", "127.0.0.1:1234")
var reply string
if err := helloStub.Hello("rpc", &reply); err != nil {
fmt.Println(err)
}
fmt.Println(reply)
}
服务端实现
- handler/handler.go 增加helloService 实现逻辑
type HelloService struct {
}
func (receiver *HelloService) Hello(arg string, reply *string) error {
*reply = "hello " + arg
return nil
}
- 增加服务端stub, server_stub/server_sub.go ,并定义接口进行解耦 (因为调用的实际是方法, 跟哪个无关)
type HelloServicer interface {
Hello(arg string, reply *string) error
}
// 使用接口进行解耦
func RegisterHelloService(rcvr HelloServicer) {
// 注册rpc
_ = rpc.RegisterName(handler.HelloServiceName, rcvr)
}
-服务端注册service
func main() {
// 创建一个server实例
listen, _ := net.Listen("tcp", ":1234")
// 注册rpc服务
server_stub.RegisterHelloService(&handler.HelloService{})
// 启动server
for {
accept, _ := listen.Accept()
go rpc.ServeConn(accept) // 进入的链接让rpc来执行
}
}
- 拓展, 目前封装的stub都是我们自己写的, 实际使用过程中这些代码应该是自动生成的,且可以生成多种语言的代码, 可参考grpc