从客户机主动连接到服务器,然后再由服务器向客户机提供服务。
通常的RPC是基于C/S结构,RPC的服务端对应网络的服务器,RPC的客户端也对应网络客户端。
但是对于一些特殊场景,比如在公司内网提供一个RPC服务,但是在外网无法链接到内网的服务器。
这种时候我们可以参考类似反向代理的技术,首先从内网主动链接到外网的TCP服务器,然后基于TCP链接向外网提供RPC服务。
服务端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
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 handleConn(conn net.Conn) { defer conn.Close() rpc.ServeConn(conn) } func main() { rpc.Register(new(HelloService)) for { //服务客户机地址 conn, _ := net.Dial("tcp", "192.168.1.102:1234") if conn == nil { time.Sleep(time.Second) continue } //因为只需要服务单个客户机,所以不需要异步 handleConn(conn) time.Sleep(time.Second) } } |
客户端代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package main import ( "fmt" "log" "net" "net/rpc" ) func handleConn(conn net.Conn) { defer close(conn) client := rpc.NewClient(conn) defer client.Close() var reply string err := client.Call("HelloService.Hello", "reserveClient hello", &reply) if err != nil { log.Fatal(err) } fmt.Println(reply) } func doClientWork(connChan <-chan net.Conn) { for { select { case conn := <-connChan: handleConn(conn) } } } func main() { listener, err := net.Listen("tcp", ":1234") if err != nil { log.Fatal("Listen TCP error: ", err) } connChan := make(chan net.Conn) go func() { for { conn, err := listener.Accept() if err != nil { log.Fatal("Accept error: ", err) } connChan <- conn } }() doClientWork(connChan) } |
说实话这段代码写的挺纠结的。
后来我就想通了,应该是服务端一直在尝试请求客户端,当客户端需要的时候就Listen,建立连接。
然后执行相应的操作,然后再释放链接。
【Go】反向RPC