Go语言从入门到规范-6.6、Go语言rpc包

Go语言从入门到规范-6.6、Go语言rpc包


文章目录

1. 前言

在分布式计算、微服务大行其道的当下,我们需要对rpc进行了解学习,而且很有必要掌握该技术,我们先对rpc的概念做简单了解,然后总结一下go的rpc包,然后写一个示例来熟悉rpc协议。

2. 概述

(1). rpc概述

在分布式计算,远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一个地址空间(通常为一个开放网络的一台计算机)的子程序,而程序员就像调用本地程序一样,无需额外地为这个交互作用编程(无需关注细节)。RPC是一种服务器-客户端(Client/Server)模式,经典实现是一个通过发送请求-接受回应进行信息交互的系统。

如果涉及的软件采用面向对象编程,那么远程过程调用亦可称作远程调用或远程方法调用,例:Java RMI。

RPC是一种进程间通信的模式,程序分布在不同的地址空间里。如果在同一主机里,RPC可以通过不同的虚拟地址空间(即便使用相同的物理地址)进行通讯,而在不同的主机间,则通过不同的物理地址进行交互。许多技术(常常是不兼容)都是基于这种概念而实现的。

(2). 流程

  • 1、客户端调用客户端stub(client stub)。这个调用是在本地,并将调用参数push到栈(stack)中。

  • 2、客户端stub(client stub)将这些参数包装,并通过系统调用发送到服务端机器。打包的过程叫 marshalling。(常见方式:XML、JSON、二进制编码)

  • 3、客户端本地操作系统发送信息至服务器。(可通过自定义TCP协议或HTTP传输)
    服务器系统将信息传送至服务端stub(server stub)。

  • 4、服务端stub(server stub)解析信息。该过程叫 unmarshalling。

  • 5、服务端stub(server stub)调用程序,并通过类似的方式返回给客户端。

为了允许不同的客户端均能访问服务器,许多标准化的 RPC 系统应运而生了。其中大部分采用接口描述语言(Interface Description Language,IDL),方便跨平台的远程过程调用。

(3). Go rpc包概述

rpc 包提供了一个方法来通过网络或者其他的I/O连接进入对象的外部方法. 一个server注册一个对象, 标记它成为可见对象类型名字的服务。注册后,对象的外部方法就可以远程调用了。一个server可以注册多个 不同类型的对象,但是却不可以注册多个相同类型的对象。

只有满足这些标准的方法才会被远程调用视为可见;其他的方法都会被忽略:

- 方法是外部可见的。
- 方法有两个参数,参数的类型都是外部可见的。
- 方法的第二个参数是一个指针。
- 方法有返回类型错误

事实上,方法必须看起来类似这样

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

T,T1和T2可以被encoding/gob序列化。 不管使用什么编解码,这些要求都要满足。(在未来,这些要求可能对自定义的编解码会放宽)

方法的第一个参数代表调用者提供的参数;第二个参数代表返回给调用者的参数。方法的返回值,如果是非空的话 就会被作为一个string返回,客户端会error像是被errors.New调用返回的一样。如果error返回的话, 返回的参数将会被送回给客户端。

服务断可以使用ServeConn来处理单个连接上的请求。更通用的方法,服务器可以制造一个网络监听,然后调用 Accept,或者对一个HTTP监听,处理HandleHTTP和http.Serve。

客户端希望使用服务来建立连接,然后在连接上调用NewClient来建立连接。更方便的方法就是调用Dial(DialHTTP) 来建立一个新的网络连接(一个HTTP连接)。客户端获得到的对象有两个方法,Call和Go,指定的参数有:服务和方法指向参数的指针,接受返回结果的指针。

call方法等待远程调用完成,但Go方法是异步调用call方法,使用Call通道来标志调用完成。

除非有明确制定编解码器,否则默认使用encoding/gob来传输数据。

这是个简单的例子,服务器希望对外服务出Arith对象:

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
}

服务端调用(使用HTTP服务):

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)

在这个时候,客户端可以看见服务“Arith”,并且有“Arith.Multiply”方法和“Arith.Divide”方法。 调用其中一个,客户端首先连接服务:

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

当它要调用远程服务的时候:

// Synchronous call
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)

or

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

服务端的实现需要为客户端提供一个简单的,类型安全服务。

3. 常量

const (
    // Defaults used by HandleHTTP  //默认被HandleHTPP使用
    DefaultRPCPath   = "/_goRPC_"
    DefaultDebugPath = "/debug/rpc"
)

4. 变量

var DefaultServer = NewServer()

DefaultServer是默认的*Server实例。

var ErrShutdown = errors.New("connection is shut down")

5. func Accept

func Accept(lis net.Listener)

Accept在连接上监听和服务请求,为每个连接调用DefaultServer。 Accept是阻塞的,调用者一般是在go语句中调用。

6. func HandleHTTP

func HandleHTTP()

HandleHTTP在DefaultRPCPath上为RPC消息注册了一个HTTP的处理器到DefaultServer上,并且在 DefaultDebugPath上注册了一个debuggin处理器。 它仍然需要调用http.Serve(),一般是在go语句中。

7. func Register

func Register(rcvr interface{}) error

Register在DefaultServer中发布接收者的方法

8. func RegisterName

func RegisterName(name string, rcvr interface{}) error

RegisterName就像Register,但是为类型使用自定义的名字而不是接收者定义的名字。

9. func ServeCodec

func ServeCodec(codec ServerCodec)

ServeCodec和ServeConn一样,但是使用特定的codec来解码请求,编码回复。

10. func ServeConn

func ServeConn(conn io.ReadWriteCloser)

ServeConn在单个连接上调用DefaultServer。 ServeConn阻塞,服务连接,直到客户端关闭。 调用者一般在go语句中调用ServeConn。ServeConn在连接上使用gob格式(参考gob包)。 要使用自定义的编解码,使用ServeCodec.

11. func ServeRequest

func ServeRequest(codec ServerCodec) error

ServeRequest和ServeCodec相似,但是同步地服务单个请求。 它直到完成了才关闭codec。

12. type Call

type Call struct {
    ServiceMethod string      // The name of the service and method to call.  // 要调用的服务名字和方法
    Args          interface{} // The argument to the function (*struct).  // 方法的参数(*struct)
    Reply         interface{} // The reply from the function (*struct).  // 方法的返回值(*struct)
    Error         error       // After completion, the error status.  // 完成之后返回的error
    Done          chan *Call  // Strobes when call is complete.  // 调用完成的信号
}

Call 代表一个活跃的RPC

13. type Client

type Client struct {
    // contains filtered or unexported fields
}

Client代表一个RPC客户端。 一个客户端可以有多个调用,并且一个客户端可以被多个goroutine同时使用

(1). func Dial

func Dial(network, address string) (*Client, error)

Dial根据指定的网络地址连接到一个RPC服务。

(2). func DialHTTP

func DialHTTP(network, address string) (*Client, error)

DialHttp根据制定的网络地址,连接到一个HTTP RPC服务。并且在默认的HTTP RPC路径进行监听。

(3). func DialHTTPPath

func DialHTTPPath(network, address, path string) (*Client, error)

DialHTTPPATH根据制定的网络地址和路径连接到一个HTTP RPC服务。

(4). func NewClient

func NewClient(conn io.ReadWriteCloser) *Client

NewClient返回一个新的客户端来处理对连接另一端的一组服务的请求。它在连接的写端添加一个缓冲区,以便将报头和有效负载作为一个单元发送

(5). func NewClientWithCodec

func NewClientWithCodec(codec ClientCodec) *Client

NewClientWithCodec与NewClient类似,但使用指定的编解码器对请求进行编码并解码响应。

(6). func (*Client) Call

func (client *Client) Call(serviceMethod string, args interface{}, reply interface{}) error

Call调用方法的名字,等待它完成,然后返回成功或失败的error状态。

(7). func (*Client) Close

func (client *Client) Close() error

(8). func (*Client) Go

func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call

Go能异步调用功能。它返回Call结构来代表回调。当调用完成,返回相同的Call对象,done channel就会获取到 信息。如果done是空的话,Go就会分配一个新的channel。如果非空的话,done必须缓冲起来,或者Go会立即崩溃。

14. type ClientCodec

type ClientCodec interface {
    // WriteRequest must be safe for concurrent use by multiple goroutines.
    WriteRequest(*Request, interface{}) error
    ReadResponseHeader(*Response) error
    ReadResponseBody(interface{}) error

    Close() error
}

ClientCodec实现了客户端一方对RPC会话的写RPC请求,和读RPC回复功能。客户端调用WriterRequest 往连接中写RPC请求,同时调用ReadResponseHeader和ReadResponseBody来读取RPC返回。当结束连接的时候 客户端调用Close。ReadResponseBody可以使用一个nil参数,来读取RPC回复,然后丢弃信息。

15. type Request

type Request struct {
    ServiceMethod string // format: "Service.Method"  // 格式:“Service.Method”
    Seq           uint64 // sequence number chosen by client  // 客户端序列化的数
    // contains filtered or unexported fields
}

Request是在每个RPC调用之前使用的header。它是内部使用的,写在这里是为了调试用,例如分析网络的流量等。

16. type Response

type Response struct {
    ServiceMethod string // echoes that of the Request  // 每个Request的相应方法
    Seq           uint64 // echoes that of the request  // 每个request的相应
    Error         string // error, if any.  // 如果有的话,表示错误
    // contains filtered or unexported fields
}

Response是在每个RPC回复之前被写在头里面的。它是内部使用的,写在这里是为了调试用,例如分析网络的流量等。

17. type Server

type Server struct {
    // contains filtered or unexported fields
}

Server代表一个RPC服务。

(1). func NewServer

func NewServer() *Server

NewServer返回一个新的Server

(2). func (*Server) Accept

func (server *Server) Accept(lis net.Listener)

Accept接收连接,为每个连接监听和服务请求。Accept是阻塞的,调用者一般在go语句中使用它。

(3). func (*Server) HandleHTTP

func (server *Server) HandleHTTP(rpcPath, debugPath string)

HandleHTTP在rpcPath上为RPC消息注册一个HTTP处理器,并在debugPath注册一个debugging处理器。 它仍然需要调用http.Serve(),一般是在go语句中使用。

(4). func (*Server) Register

func (server *Server) Register(rcvr interface{}) error

Register发布服务器的一系列方法,接受器必须满足这几个条件:

- 对外可见的方法
- 两个参数,都指向对外可见的结构
- 一个error类型返回值

如果接收者不是一个对外可见的类型,或者没有任何方法,或者没有满足条件的方法,都会返回error。 它也会使用log包来记录错误。客户端进入每个方法使用字符串格式形如“Type.Method”, 这里Type是接收者的具体的类型。

(5). func (*Server) RegisterName

func (server *Server) RegisterName(name string, rcvr interface{}) error

RegisterName像Register,但是为type使用提供的名字,而不是使用receivers的具体类型。

(6). func (*Server) ServeCodec

func (server *Server) ServeCodec(codec ServerCodec)

ServerCodec和ServeConn相似,但是使用自定义的编解码器来解码请求和编码回复。

(7). func (*Server) ServeConn

func (server *Server) ServeConn(conn io.ReadWriteCloser)

ServeConn在单个连接上跑server。 ServeConn阻塞,知道客户端关闭之后才继续服务其他连接。 调用者一般在go语句中调用ServeConn。 ServeConn在连接传输的时候使用gob格式(参考gob包)。可以使用自定义编码器,ServeCodec。

(8). func (*Server) ServeHTTP

func (server *Server) ServeHTTP(w http.ResponseWriter, req *http.Request)

ServeHTTP实现了http.Handler,并且回复RPC请求。

(9). func (*Server) ServeRequest

func (server *Server) ServeRequest(codec ServerCodec) error

ServerRequest和ServeCodec相似,但是是同步地服务单个请求。 它在结束的时候不会关闭编解码器。

18. type ServerCodec

type ServerCodec interface {
    ReadRequestHeader(*Request) error
    ReadRequestBody(interface{}) error
    // WriteResponse must be safe for concurrent use by multiple goroutines.
    WriteResponse(*Response, interface{}) error

    Close() error
}

ServerCodec实现了为RPC会话提供读RPC请求和写PRC回复的服务端的方法。服务端调用 ReadRequestHeader和ReadRequestBody来读取连接上的请求,然后调用WriteResponse来 写回复。服务端当结束连接的时候调用Close。ReadRequestBody可能会调用一个nil参数来强迫 读取请求内容并忽略。

19. type ServerError

type ServerError string

ServerError 代表从远程RPC连接另一端返回的错误

(1). func (ServerError) Error

func (e ServerError) Error() string
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

昵称系统有问题

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值