diff --git a/skills/rpc/hello_world/server_a/main.go b/skills/rpc/hello_world/server_a/main.go new file mode 100644 index 0000000..266c80c --- /dev/null +++ b/skills/rpc/hello_world/server_a/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "log" + "net" + "net/rpc" +) + +func main() { + // 把我们的对象注册成一个rpc的 receiver + // 其中rpc.Register函数调用会将对象类型中所有满足RPC规则的对象方法注册为RPC函数, + // 所有注册的方法会放在“HelloService”服务空间之下 + // err := HelloService.FnName(req, resp) + rpc.RegisterName("HelloService", new(HelloService)) + + // RPC服务器还没启动起来 + // 然后我们建立一个唯一的TCP链接, + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + // 通过rpc.ServeConn函数在该TCP链接上为对方提供RPC服务。 + // 没Accept一个请求,就创建一个goroutie进行处理 + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + // 前面都是tcp的知识, 到这个RPC就接管了 + // 因此 你可以认为 rpc 帮我们封装消息到函数调用的这个逻辑, + // 提升了工作效率, 逻辑比较简洁,可以看看他代码 + go rpc.ServeConn(conn) + } +} + +// 要通过 net/rpc 把一个对象的方法 暴露为rpc,需要符合 net/rpc的方法签名: Hello(request string, reply *string) error +// FnName(req any, resp *any) error + +type HelloService struct{} + +// HTTP Framwork Handler Hello(request string, reply *string) error +func (h *HelloService) Hello(request string, reply *string) error { + *reply = "hello " + request + return nil +} diff --git a/skills/rpc/hello_world/server_b/main.go b/skills/rpc/hello_world/server_b/main.go new file mode 100644 index 0000000..d80b701 --- /dev/null +++ b/skills/rpc/hello_world/server_b/main.go @@ -0,0 +1,23 @@ +package main + +import ( + "context" + "fmt" + "log" + + "122.51.31.227/go-course/go18/skills/rpc/hello_world/service" +) + +func main() { + client, err := service.NewHelloServiceClient("localhost:1234") + if err != nil { + log.Fatal("dialing:", err) + } + + // SDK 提供的方法调用 + reply, err := client.Hello(context.Background(), "bob") + if err != nil { + log.Fatal(err) + } + fmt.Println(reply) +} diff --git a/skills/rpc/hello_world/service/client.go b/skills/rpc/hello_world/service/client.go new file mode 100644 index 0000000..92e0db0 --- /dev/null +++ b/skills/rpc/hello_world/service/client.go @@ -0,0 +1,45 @@ +package service + +import ( + "context" + "net/rpc" +) + +// 首先是通过rpc.Dial拨号RPC服务, 建立连接 +// client, err := rpc.Dial("tcp", "localhost:1234") +// if err != nil { +// log.Fatal("dialing:", err) +// } + +// // 然后通过client.Call调用具体的RPC方法 +// // 在调用client.Call时: +// // 第一个参数是用点号链接的RPC服务名字和方法名字, +// // 第二个参数是 请求参数 +// // 第三个是请求响应, 必须是一个指针, 有底层rpc服务帮你赋值 +// var reply string +// // HelloServiceClient.Hello(ctx, req) (resp, error) +// err = client.Call("HelloService.Hello", "bob", &reply) +// if err != nil { +// log.Fatal(err) +// } + +func NewHelloServiceClient(address string) (HelloService, error) { + client, err := rpc.Dial("tcp", address) + if err != nil { + return nil, err + } + return &HelloServiceClient{client: client}, nil +} + +type HelloServiceClient struct { + client *rpc.Client +} + +func (h *HelloServiceClient) Hello(ctx context.Context, request string) (string, error) { + var reply string + err := h.client.Call("HelloService.Hello", request, &reply) + if err != nil { + return "", err + } + return reply, nil +} diff --git a/skills/rpc/hello_world/service/interface.go b/skills/rpc/hello_world/service/interface.go new file mode 100644 index 0000000..a1a172a --- /dev/null +++ b/skills/rpc/hello_world/service/interface.go @@ -0,0 +1,8 @@ +package service + +import "context" + +// RPC 协议,约束服务端的实现,约束 客户端的调用 +type HelloService interface { + Hello(ctx context.Context, request string) (string, error) +}