From d1c8357fdd2f6f4bfc5397f9fce243132e0ae02f Mon Sep 17 00:00:00 2001 From: chai2010 Date: Mon, 2 Jul 2018 10:37:17 +0800 Subject: [PATCH] =?UTF-8?q?ch4-03:=20=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- SUMMARY.md | 2 +- ch4-rpc/ch4-03-netrpc-hack.md | 151 ++++++++++++++++-- .../ch4-03-rpc-hack/rpc-auth/client/main.go | 37 +++++ .../ch4-03-rpc-hack/rpc-auth/server/main.go | 53 ++++++ .../rpc-context/client/main.go | 22 +++ examples/ch4-03-rpc-hack/rpc-context/main.go | 5 - .../rpc-context/server/main.go | 38 +++++ .../rpc-reverse/client/main.go | 39 +++++ examples/ch4-03-rpc-hack/rpc-reverse/main.go | 5 - .../rpc-reverse/server/main.go | 29 ++++ 10 files changed, 358 insertions(+), 23 deletions(-) create mode 100644 examples/ch4-03-rpc-hack/rpc-auth/client/main.go create mode 100644 examples/ch4-03-rpc-hack/rpc-auth/server/main.go create mode 100644 examples/ch4-03-rpc-hack/rpc-context/client/main.go delete mode 100644 examples/ch4-03-rpc-hack/rpc-context/main.go create mode 100644 examples/ch4-03-rpc-hack/rpc-context/server/main.go create mode 100644 examples/ch4-03-rpc-hack/rpc-reverse/client/main.go delete mode 100644 examples/ch4-03-rpc-hack/rpc-reverse/main.go create mode 100644 examples/ch4-03-rpc-hack/rpc-reverse/server/main.go diff --git a/SUMMARY.md b/SUMMARY.md index c94b1e5..e387f65 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -34,7 +34,7 @@ * [第四章 RPC和Protobuf](ch4-rpc/readme.md) * [4.1. RPC入门](ch4-rpc/ch4-01-rpc-intro.md) * [4.2. Protobuf](ch4-rpc/ch4-02-pb-intro.md) - * [4.3. 玩转RPC(TODO)](ch4-rpc/ch4-03-netrpc-hack.md) + * [4.3. 玩转RPC](ch4-rpc/ch4-03-netrpc-hack.md) * [4.4. GRPC入门(TODO)](ch4-rpc/ch4-04-grpc.md) * [4.5. GRPC进阶(TODO)](ch4-rpc/ch4-05-grpc-hack.md) * [4.6. Protobuf扩展语法和插件(TODO)](ch4-rpc/ch4-06-pb-option.md) diff --git a/ch4-rpc/ch4-03-netrpc-hack.md b/ch4-rpc/ch4-03-netrpc-hack.md index c394f09..817e2f2 100644 --- a/ch4-rpc/ch4-03-netrpc-hack.md +++ b/ch4-rpc/ch4-03-netrpc-hack.md @@ -1,24 +1,151 @@ # 4.3. 玩转RPC -TODO +在不同的场景中RPC有着不同的需求,因此开源的社区就诞生了各种RPC框架。本节我们将尝试Go内置RPC框架在一些比较特殊场景的用法。 - + clientChan := make(chan *rpc.Client) + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + clientChan <- rpc.NewClient(conn) + } + }() + + doClientWork(clientChan) +} +``` + +当接每个链接建立后,基于网络链接构造RPC客户端对象并发送到clientChan管道。 + +客户端执行RPC调用的操作在doClientWork函数完成: + +```go +func doClientWork(clientChan <-chan *rpc.Client) { + client := <-clientChan + defer client.Close() + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} +``` + +首先从管道去除一个RPC客户端对象,并且通过defer语句指定在函数退出钱关闭客户端。然后是执行正常的RPC调用。 + + +## 上下文信息 + +首先是上下文信息,基于上下文我们可以针对不同客户端提供定制化的RPC服务。我们可以通过为每个信道提供独立的RPC服务来实现对上下文特性的支持。 + +首先改造HelloService,里面增加了对应链接的conn成员: + +```go +type HelloService struct { + conn net.Conn +} +``` + +然后为每个信道启动独立的RPC服务: + +```go +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go func() { + defer conn.Close() + + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) + } () + } +} +``` + +Hello方法中就可以根据conn成员识别不同信道的RPC调用: + +```go +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} +``` + +基于上下文信息,我们可以方便地为RPC服务增加简单的登陆状态的验证: + +```go +type HelloService struct { + conn net.Conn + isLogin bool +} + +func (p *HelloService) Login(request string, reply *string) error { + if request != "user:password" { + return fmt.Errorf("auth failed") + } + log.Println("login ok") + p.isLogin = true + return nil +} + +func (p *HelloService) Hello(request string, reply *string) error { + if !p.isLogin { + return fmt.Errorf("please login") + } + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} +``` + +这样可以要求在客户端链接RPC服务时,首先要执行登陆操作,登陆成果后才能正常执行其他的服务。 diff --git a/examples/ch4-03-rpc-hack/rpc-auth/client/main.go b/examples/ch4-03-rpc-hack/rpc-auth/client/main.go new file mode 100644 index 0000000..07209b7 --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-auth/client/main.go @@ -0,0 +1,37 @@ +package main + +import ( + "fmt" + "log" + "net/rpc" +) + +func main() { + client, err := rpc.Dial("tcp", "localhost:1234") + if err != nil { + log.Fatal("dialing:", err) + } + + var reply string + + err = client.Call("HelloService.Login", "abc", &reply) + if err != nil { + log.Println(err) + } else { + log.Println("login ok") + } + + err = client.Call("HelloService.Login", "user:password", &reply) + if err != nil { + log.Println(err) + } else { + log.Println("login ok") + } + + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/examples/ch4-03-rpc-hack/rpc-auth/server/main.go b/examples/ch4-03-rpc-hack/rpc-auth/server/main.go new file mode 100644 index 0000000..281b1cf --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-auth/server/main.go @@ -0,0 +1,53 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/rpc" +) + +type HelloService struct { + conn net.Conn + isLogin bool +} + +func ServeHelloService(conn net.Conn) { + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) +} + +func (p *HelloService) Login(request string, reply *string) error { + if request != "user:password" { + log.Println("login failed") + return fmt.Errorf("auth failed") + } + log.Println("login ok") + p.isLogin = true + return nil +} + +func (p *HelloService) Hello(request string, reply *string) error { + if !p.isLogin { + return fmt.Errorf("please login") + } + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go ServeHelloService(conn) + } +} diff --git a/examples/ch4-03-rpc-hack/rpc-context/client/main.go b/examples/ch4-03-rpc-hack/rpc-context/client/main.go new file mode 100644 index 0000000..7e655ec --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-context/client/main.go @@ -0,0 +1,22 @@ +package main + +import ( + "fmt" + "log" + "net/rpc" +) + +func main() { + client, err := rpc.Dial("tcp", "localhost:1234") + if err != nil { + log.Fatal("dialing:", err) + } + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/examples/ch4-03-rpc-hack/rpc-context/main.go b/examples/ch4-03-rpc-hack/rpc-context/main.go deleted file mode 100644 index d8d44f3..0000000 --- a/examples/ch4-03-rpc-hack/rpc-context/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - // todo -} diff --git a/examples/ch4-03-rpc-hack/rpc-context/server/main.go b/examples/ch4-03-rpc-hack/rpc-context/server/main.go new file mode 100644 index 0000000..fc7b2f0 --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-context/server/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "log" + "net" + "net/rpc" +) + +type HelloService struct { + conn net.Conn +} + +func ServeHelloService(conn net.Conn) { + p := rpc.NewServer() + p.Register(&HelloService{conn: conn}) + p.ServeConn(conn) +} + +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + ", from" + p.conn.RemoteAddr().String() + return nil +} + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + go ServeHelloService(conn) + } +} diff --git a/examples/ch4-03-rpc-hack/rpc-reverse/client/main.go b/examples/ch4-03-rpc-hack/rpc-reverse/client/main.go new file mode 100644 index 0000000..c8cc94a --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-reverse/client/main.go @@ -0,0 +1,39 @@ +package main + +import ( + "fmt" + "log" + "net" + "net/rpc" +) + +func main() { + listener, err := net.Listen("tcp", ":1234") + if err != nil { + log.Fatal("ListenTCP error:", err) + } + + clientChan := make(chan *rpc.Client) + + go func() { + for { + conn, err := listener.Accept() + if err != nil { + log.Fatal("Accept error:", err) + } + + clientChan <- rpc.NewClient(conn) + } + }() + + client := <-clientChan + defer client.Close() + + var reply string + err = client.Call("HelloService.Hello", "hello", &reply) + if err != nil { + log.Fatal(err) + } + + fmt.Println(reply) +} diff --git a/examples/ch4-03-rpc-hack/rpc-reverse/main.go b/examples/ch4-03-rpc-hack/rpc-reverse/main.go deleted file mode 100644 index d8d44f3..0000000 --- a/examples/ch4-03-rpc-hack/rpc-reverse/main.go +++ /dev/null @@ -1,5 +0,0 @@ -package main - -func main() { - // todo -} diff --git a/examples/ch4-03-rpc-hack/rpc-reverse/server/main.go b/examples/ch4-03-rpc-hack/rpc-reverse/server/main.go new file mode 100644 index 0000000..b8c95b7 --- /dev/null +++ b/examples/ch4-03-rpc-hack/rpc-reverse/server/main.go @@ -0,0 +1,29 @@ +package main + +import ( + "net" + "net/rpc" + "time" +) + +type HelloService struct{} + +func (p *HelloService) Hello(request string, reply *string) error { + *reply = "hello:" + request + return nil +} + +func main() { + rpc.Register(new(HelloService)) + + for { + conn, _ := net.Dial("tcp", "localhost:1234") + if conn == nil { + time.Sleep(time.Second) + continue + } + + rpc.ServeConn(conn) + conn.Close() + } +}