RPC与Protobuf(五)
RPC实现上下文信息
基于上下文可以针对不同的客户端提供定制化的RPC服务,我们可以通过为每个连接提供独立的RPC服务来实现对上下文的特性的支持,下面我们将针对每个RPC服务进行登陆验证,如果通过服务登陆就可以调用RPC,首先是proto目录下的proto文件:
syntax = "proto3"; package proto; message String { string value = 1; }
当然我们需要使用特殊的命令生成相应的接口文件: protoc --go_out=. hello.proto ;紧接着是服务端代码, ctx_service.go:
package main import ( "fmt" "log" "net" "net/rpc" "rpc_protobuf/proto" ) type HelloServiceCtx struct { conn net.Conn isLogin bool } func (p *HelloServiceCtx) Login(request *proto.String, reply *proto.String) error { if request.GetValue() != "user:password" { return fmt.Errorf("auth failed") } log.Println("login OK") p.isLogin = true return nil } func (p *HelloServiceCtx) Hello(request *proto.String, reply *proto.String) error { if !p.isLogin { return fmt.Errorf("please login") } reply.Value = "hello :"+ reply.GetValue() + ", from " + p.conn.LocalAddr().String() return nil } func main() { listener, err := net.Listen("tcp",":1234") if err != nil { log.Fatal("tcp is error") } // 基于每个连接创建一个RPC服务 for { conn, err:= listener.Accept() if err != nil { log.Fatal("accept error") } go func() { defer conn.Close() p := rpc.NewServer() p.Register(&HelloServiceCtx{conn:conn}) p.ServeConn(conn) }() } }
然后是客户端代码ctx_client.go:
package main import ( "fmt" "log" "net/rpc" "rpc_protobuf/proto" ) func main() { // 拨号本端1234端口服务 client, err := rpc.Dial("tcp", "localhost:1234") if err != nil { log.Fatal("TCP error:", err) } // 统一采用接口提供的参数,和请求参数 var reply = &proto.String{} var param = &proto.String{ Value: "user:password", } // 先登陆后在调服务 err = client.Call("HelloServiceCtx.Login", ¶m, &reply) if err != nil { log.Fatal(err) } // 调用远程服务 err = client.Call("HelloServiceCtx.Hello", ¶m, &reply) if err != nil { log.Fatal(err) } fmt.Println(reply) }
到处RPC和Protobuf相关知识就介绍到这了, 如果想深度研究可以访问官网,之后我们可能会介绍gRPC相关知识。