rpc简介

是什么rpc

远程过程调用(Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。 该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。

rpc原理

rpc的调用流程

《rpc简介》 image

  1. 服务消费方(client)调用以本地调用方式调用服务;
  2. client stub接收到调用后负责将方法、参数等组装成能够1. 进行网络传输的消息体;
  3. client stub找到服务地址,并将消息发送到服务端;
  4. server stub收到消息后进行解码;
  5. server stub根据解码结果调用本地的服务;
  6. 本地服务执行并将结果返回给server stub;
  7. server stub将返回结果打包成消息并发送至消费方;
  8. client stub接收到消息,并进行解码;
  9. 服务消费方得到最终结果。

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

其中的技术细节包括:

  • 对象的序列化反序列化
  • 通信

rpc和Rest Api的区别

REST是一种设计风格,它的很多思维方式与RPC是不一样的。

RPC的思想是把本地函数映射到API,也就是说一个API对应的是一个function,我本地有一个getAllUsers,远程也能通过某种约定的协议来调用这个getAllUsers。至于这个协议是Socket、是HTTP还是别的什么并不重要;

RPC中的主体都是动作,是个动词,表示我要做什么。

而REST则不然,它的URL主体是资源,是个名词。而且也仅支持HTTP协议,规定了使用HTTP Method表达本次要做的动作,类型一般也不超过那四五种。这些动作表达了对资源仅有的几种转化方式。

go语言的rpc

Go官方提供了一个RPC库: net/rpc。包rpc提供了通过网络访问一个对象的方法的能力。

服务器需要注册对象, 通过对象的类型名暴露这个服务。注册后这个对象的输出方法就可以远程调用,这个库封装了底层传输的细节,包括序列化。

如果对象的方法要能远程访问,它们必须满足一定的条件,否则这个对象的方法会被忽略。

  • 方法的类型是可输出的 (the method’s type is exported)
  • 方法本身也是可输出的 (the method is exported)
  • 方法必须由两个参数,必须是输出类型或者是内建类型 (the method has two arguments, both exported or builtin types)
  • 方法的第二个参数是指针类型 (the method’s second argument is a pointer)
  • 方法返回类型为 error (the method has return type error)

输出的格式如下:

func (t *T) MethodName(argType T1, replyType *T2) error

这里的T、T1、T2需要能够被encoding/gob序列化。

这个方法的第一个参数代表调用者(client)提供的参数,
第二个参数代表要返回给调用者的计算结果

一个例子

定义传入参数和返回参数的数据结构

package server
type Args struct {
    A, B int
}
type Quotient struct {
    Quo, Rem int
}

定义服务对象

type Arith int

定义可被远程调用的方法

func (t *Arith) Multiply(args *Args, reply *int) error {
    *reply = args.A * args.B
    return nil
}

func (t *Arith) Divide(args *Args, quo *Quotient) error {
    if args.B == 0 {
        return errors.New("divide by zero")
    }
    quo.Quo = args.A / args.B
    quo.Rem = args.A % args.B
    return nil
}

实现rpc服务端调用

arith := new(Arith)
rpc.Register(arith)
rpc.HandleHTTP()
l, e := net.Listen("tcp", ":1234")
if e != nil {
    log.Fatal("listen error:", e)
}
go http.Serve(l, nil)

创建客户端

client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")
if err != nil {
    log.Fatal("dialing:", err)
}

调用客户端
同步的方式调用

args := &server.Args{7,8}
var reply int
err = client.Call("Arith.Multiply", args, &reply)
if err != nil {
    log.Fatal("arith error:", err)
}
fmt.Printf("Arith: %d*%d=%d", args.A, args.B, reply)

异步的方式调用:

quotient := new(Quotient)
divCall := client.Go("Arith.Divide", args, quotient, nil)
replyCall := <-divCall.Done // will be equal to divCall
// check errors, print, etc.

这个net/rpc已经不再更新了,推荐使用GRPC

其他

服务治理型的 RPC 框架有Alibab Dubbo、Motan 等,这类的 RPC 框架的特点是功能丰富,提供高性能的远程调用以及服务发现和治理功能,适用于大型服务的微服务化拆分以及管理,对于特定语言(Java)的项目可以十分友好的透明化接入。但缺点是语言耦合度较高,跨语言支持难度较大。

跨语言调用型的 RPC 框架有 Thrift、gRPC、Hessian、Finagle 等,这一类的 RPC 框架重点关注于服务的跨语言调用,能够支持大部分的语言进行语言无关的调用,非常适合于为不同语言提供通用远程服务的场景。但这类框架没有服务发现相关机制,实际使用时一般需要代理层进行请求转发和负载均衡策略控制。

参考文章:

    原文作者:ironman_
    原文地址: https://www.jianshu.com/p/fd0d4bf85c97
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞