golang rpc

2021-10-30  本文已影响0人  夜空中乄最亮的星

rpc是什么

RPC(Remote Procedure Call)是远程过程调用,通俗的说就是调用远处的一个函数。

入门

一. RPC版 HelloWorld

服务端代码

type HelloWorldService struct {
}

func (hello *HelloWorldService) Hello(request string, reply *string) error {
    *reply = "recieved msg :" + request + "\n"
    return nil
}
func Run() {
    rpc.RegisterName("HelloService", new(HelloWorldService))
    listen, err := net.Listen("tcp", "127.0.0.1:9090")
    if err != nil {
        log.Fatalln(err)
    }
    conn, err := listen.Accept()
    if err != nil {
        log.Fatalln(err)
    }
    rpc.ServeConn(conn)
}

说明:rpc.RegisterName会将HelloWorldService对象中所有满足rpc规则的方法注册为rpc函数,并放在HelloService服务空间之下。其中Hello方法必须满足以下规则:

  1. 访问限制必须公开
  2. 只能有2个序列化参数,第二个参数必须是指针类型
  3. 返回error类型

客户端代码:

func main()  {
    client, err := rpc.Dial("tcp", "127.0.0.1:9090")
    if err !=nil {
        log.Fatalln(err)
    }
    var reply string
    err = client.Call("HelloService.Hello", "Hello World", &reply)
    if err !=nil {
        log.Fatalln(err)
    }
    fmt.Println(reply)
}

首先通过rpc.Dial拨号rpc服务,cient.Call()可理解为呼叫,其第一个参数是rpc的服务服务名和方法名,第二和第三个参数是该方法的2个参数。

二. 更安全的RPC接口

在设计RPC的应用中,作为开发人员至少有3中角色:

  1. 服务器端实现RPC方法的开发人员
  2. 客户端调用RPC方法的开发人员
  3. 制定服务端和客户端RPC接口规范的设计人员

上面HelloWorld的例子虽然看似简单,但是不利于后期的维护和工作的切割,其中存在一个较大的问题是:
客户端中,Call()方法的参数无法得到编译器安全检查,只有运行时才能发现错误。

服务端代码:

const HelloServiceName = "study.rpc.HelloService"

type HelloServiceInterface  interface {
    Hello(request string,reply *string) error
}

func RegisterHelloService(srv HelloServiceInterface) error {
    return rpc.RegisterName(HelloServiceName,srv)
}
type HelloWorldService struct {
}

func (hello *HelloWorldService) Hello(request string, reply *string) error {
    *reply = "recieved msg :" + request + "\n"
    return nil
}

func main()  {
    RegisterHelloService(new(HelloWorldService))
    listen, err := net.Listen("tcp", "127.0.0.1:9090")
    if err != nil {
        log.Fatalln(err)
    }
    for  {
        conn, err := listen.Accept()
        if err != nil {
            log.Fatalln(err)
        }
        go rpc.ServeConn(conn)
    }
}

type HelloServiceClient struct {
    *rpc.Client
}

//检测HelloServiceClient 是否实现了 HelloServiceInterface 
var _ HelloServiceInterface = (*HelloServiceClient)(nil)

func (client *HelloServiceClient) Hello(request string, reply *string) error {
    return client.Call(HelloServiceName+".Hello",request,reply)
}

func DialHelloService(network string,addr string) (*HelloServiceClient,error) {
    client, err := rpc.Dial(network, addr)
    if err !=nil {
        return nil,err
    }
    return &HelloServiceClient{client},nil
}

func main()  {
    service, err := DialHelloService("tcp", "127.0.0.1:9090")
    if err !=nil {
        log.Fatalln(err)
    }
    var reply string
    err = service.Hello("Hello world", &reply)
    if err !=nil {
        log.Fatalln(err)
    }
    fmt.Println(reply)
}
上一篇 下一篇

猜你喜欢

热点阅读