mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-24 04:22:22 +00:00
ch4-03: 完成
This commit is contained in:
parent
ca1ed4da49
commit
d1c8357fdd
@ -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)
|
||||
|
@ -1,24 +1,151 @@
|
||||
# 4.3. 玩转RPC
|
||||
|
||||
TODO
|
||||
在不同的场景中RPC有着不同的需求,因此开源的社区就诞生了各种RPC框架。本节我们将尝试Go内置RPC框架在一些比较特殊场景的用法。
|
||||
|
||||
<!--
|
||||
## 反向RPC
|
||||
|
||||
认证/反向/gls?,能够拿到req吗,基于req的gls
|
||||
通常的RPC是基于C/S结构,RPC的的服务端对应网络的服务器,RPC的客户端也对应网络客户端。但是对于一些特殊场景,比如在公司内网提供一个RPC服务,但是在外网无法链接到内网的服务器。这种时候我们可以参考类似反向代理的技术,首先从内网主动链接到外网的TCP服务器,然后基于TCP链接向外网提供RPC服务。
|
||||
|
||||
--
|
||||
以下是启动反向RPC服务的代码:
|
||||
|
||||
pb 和 json 是类似的,
|
||||
```go
|
||||
func main() {
|
||||
rpc.Register(new(HelloService))
|
||||
|
||||
唯一的差异的 protoc 工具
|
||||
for {
|
||||
conn, _ := net.Dial("tcp", "localhost:1234")
|
||||
if conn == nil {
|
||||
time.Sleep(time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
有了代码生成,一切就发生变化了
|
||||
rpc.ServeConn(conn)
|
||||
conn.Close()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
pb 的重要性不再时底层的编码,二是 api 的通用语言!!!
|
||||
反向RPC的内网服务将不再主导提供TCP监听服务,而是首先主动链接到对方的TCP服务器。然后基于每个建立的TCP链接向对方提供RPC服务。
|
||||
|
||||
但是 pb 个 rest 是无法一一等价的
|
||||
而RPC客户端则需要在一个公共的地址提供一个TCP服务,用于接受RPC服务器的链接请求:
|
||||
|
||||
生成简单的代码,增加接口约束
|
||||
```go
|
||||
func main() {
|
||||
listener, err := net.Listen("tcp", ":1234")
|
||||
if err != nil {
|
||||
log.Fatal("ListenTCP error:", err)
|
||||
}
|
||||
|
||||
甚至最终回归到 gob 或 json 编码
|
||||
-->
|
||||
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服务时,首先要执行登陆操作,登陆成果后才能正常执行其他的服务。
|
||||
|
37
examples/ch4-03-rpc-hack/rpc-auth/client/main.go
Normal file
37
examples/ch4-03-rpc-hack/rpc-auth/client/main.go
Normal file
@ -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)
|
||||
}
|
53
examples/ch4-03-rpc-hack/rpc-auth/server/main.go
Normal file
53
examples/ch4-03-rpc-hack/rpc-auth/server/main.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
22
examples/ch4-03-rpc-hack/rpc-context/client/main.go
Normal file
22
examples/ch4-03-rpc-hack/rpc-context/client/main.go
Normal file
@ -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)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
// todo
|
||||
}
|
38
examples/ch4-03-rpc-hack/rpc-context/server/main.go
Normal file
38
examples/ch4-03-rpc-hack/rpc-context/server/main.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
39
examples/ch4-03-rpc-hack/rpc-reverse/client/main.go
Normal file
39
examples/ch4-03-rpc-hack/rpc-reverse/client/main.go
Normal file
@ -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)
|
||||
}
|
@ -1,5 +0,0 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
// todo
|
||||
}
|
29
examples/ch4-03-rpc-hack/rpc-reverse/server/main.go
Normal file
29
examples/ch4-03-rpc-hack/rpc-reverse/server/main.go
Normal file
@ -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()
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user