什么是RPC

RPC是Remote Procedure CallProtocol的缩写,即—远程过程调用协议。

RPC是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用。通过它可以使函数调用模式网络化。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

包net/rpc

Go语言标准库能够自带一个rpc框架还是非常给力的,这可以很大程度的降低写后端网络通信服务的门槛,特别是在大规模的分布式系统中,rpc基本是跨机器通信的标配。rpc能够最大程度屏蔽网络细节,让开发者专注在服务功能的开发上面。

包rpc提供对通过网络或其他I / O连接的对象的导出方法的访问。服务器注册一个对象,使其作为具有对象类型名称的服务可见。注册后,可以远程访问对象的导出方法。服务器可以注册不同类型的多个对象(服务),但是注册相同类型的多个对象是错误的。

Go RPC的函数只有符合下面的条件才能被远程访问:

  1. 函数必须是导出的(首字母大写)
  2. 必须有两个导出类型的参数,
  3. 第一个参数是接收的参数,第二个参数是返回给客户端的参数,第二个参数必须是指针类型的
  4. 函数还要有一个返回值error

下面介绍几个方法:

Register
func Register(rcvr interface{}) error 
Register在DefaultServer中发布接收者的方法。

NEWSERVER
func NewServer() *Server 
NewServer返回一个新的服务器。

HandleHTTP 
func (server *Server) HandleHTTP(rpcPath, debugPath string) 
HandleHTTP在rpcPath上注册RPC消息的HTTP处理程序,在debugPath上注册调试处理程序。仍然需要调用http.Serve()

Accept
func Accept(lis net.Listener)
Accept接受侦听器上的连接,并为每个传入连接向DefaultServer提供请求。 接受块; 调用者通常在go语句中调用它。

Dial
func Dial(network, address string) (*Client, error)
拨号连接到指定网络地址的RPC服务器。

RPC与tcp、http

RPC可以利用TCP和HTTP进行传递数据,下面我们来比较一下.

这个stackoverlow上的回答,觉得很好,搬过来:
HTTP is a layer built ontop of the TCP layer to some what standardize data transmission. So naturally using TCP sockets will be less heavy than using HTTP. If performance is the only thing you care about then plain TCP is the best solution for you.
You may want to consider HTTP because of its ease of use and simplicity which ultimately reduces development time. If you are doing something that might be directly consumed by a browser (through an AJAX call) then you should use HTTP. For a non-modern browser to directly consume TCP connections without HTTP you would have to use Flash or Silverlight and this normally happens for rich content such as video and/or audio. However, many modern browsers now (as of 2013) support API’s to access network, audio, and video resources directly via JavaScript. The only thing to consider is the usage rate of modern web browsers among your users; see caniuse.com for the latest info regarding browser compatibility.

As for benchmarks, this is the only thing I found. See page 5, it has the performance graph. Note that it doesn’t really compare apples to apples since it compares the TCP/Binary data option with the HTTP/XML data option. Which begs the question: what kind of data are your services outputting? binary (video, audio, files) or text (JSON, XML, HTML)?

In general performance oriented system like those in the military or financial sectors will probably use plain TCP connections. Where as general web focused companies will opt to use HTTP and use IIS or Apache to host their services.

HTTP是在TCP层的顶部构建的层,用于标准化数据传输的一些层。所以使用TCP套接字自然会比使用HTTP更重。如果性能是您唯一关心的,那么普通TCP是最适合您的解决方案。
您可能需要考虑HTTP,因为它易于使用且简单,最终缩短了开发时间。如果您正在做一些可能被浏览器直接使用的东西(通过AJAX调用),那么您应该使用HTTP。对于非现代浏览器直接使用没有HTTP的TCP连接,您必须使用Flash或Silverlight,这通常适用于视频和/或音频等丰富内容。但是,现在许多现代浏览器(截至2013年)都支持API直接通过JavaScript访问网络,音频和视频资源。唯一要考虑的是用户之间现代Web浏览器的使用率;有关浏览器兼容性的最新信息,请访问caniuse.com。
至于基准测试,这是我发现的唯一的东西。见第5页,它有性能图表。请注意,它并不真正比较苹果和苹果,因为它将TCP /二进制数据选项与HTTP / XML数据选项进行比较。这引出了一个问题:您的服务输出的是哪种数据?二进制(视频,音频,文件)或文本(JSON,XML,HTML)?
一般来说,与军事或金融部门相似的性能导向系统可能会使用普通的TCP连接。一般网络公司将选择使用HTTP并使用IIS或Apache来托管其服务。

基于HTTP的RPC
服务端:

package main

import (
    "errors"
    "fmt"
    "net/http"
    "net/rpc"
)

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
}

func main() {

    arith := new(Arith)
    rpc.Register(arith)
    rpc.HandleHTTP()

    err := http.ListenAndServe(":8989", nil)
    if err != nil {
        fmt.Println(err.Error())
    }
}

客户端

package main

import (
    "fmt"
    "log"
    "net/rpc"
)

type Args struct {
    A, B int
}

type Quotient struct {
    Quo, Rem int
}

func main() {

    client, err := rpc.DialHTTP("tcp", "127.0.0.1:8989")
    if err != nil {
        log.Fatal("dialing:", err)
    }
    // Synchronous call
    args := Args{17, 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\n", args.A, args.B, reply)

    var quot Quotient
    err = client.Call("Arith.Divide", args, &quot)
    if err != nil {
        log.Fatal("arith error:", err)
    }
    fmt.Printf("Arith: %d/%d=%d remainder %d\n", args.A, args.B, quot.Quo, quot.Rem)

}

运行效果:

rpc.png

over~

Last modification:September 9th, 2019 at 07:12 pm
如果觉得我的文章对你有用,请随意赞赏