小程序容器助力企业在金融与物联网领域实现高效合规运营,带来的新机遇与挑战如何管理?
908
2022-11-18
Go语言从入门到规范-6.6、Go语言rpc包
Go语言从入门到规范-6.6、Go语言rpc包
文章目录
Go语言从入门到规范-6.6、Go语言rpc包
1. 前言2. 概述
(1). rpc概述(2). 流程(3). Go rpc包概述
3. 常量4. 变量5. func [Accept](func [HandleHTTP](func [Register](func [RegisterName](func [ServeCodec](func [ServeConn](func [ServeRequest](type [Call](type [Client](func [Dial](func [DialHTTP](func [DialHTTPPath](func [NewClient](func [NewClientWithCodec](func (*Client) [Call](func (*Client) [Close](func (*Client) [Go](type [ClientCodec](type [Request](type [Response](type [Server](func [NewServer](func (*Server) [Accept](func (*Server) [HandleHTTP](func (*Server) [Register](func (*Server) [RegisterName](func (*Server) [ServeCodec](func (*Server) [ServeConn](func (*Server) [ServeHTTP](func (*Server) [ServeRequest](type [ServerCodec](type [ServerError](func (ServerError) [Error](前言
在分布式计算、微服务大行其道的当下,我们需要对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连接)。客户端获得到的对象有两个方法,Call和Go,指定的参数有:服务和方法指向参数的指针,接受返回结果的指针。
call方法等待远程调用完成,但Go方法是异步调用call方法,使用Call通道来标志调用完成。
除非有明确制定编解码器,否则默认使用encoding/gob来传输数据。
这是个简单的例子,服务器希望对外服务出Arith对象:
package servertype Args struct { A, B int}type Quotient struct { Quo, Rem int}type Arith intfunc (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 nil)
在这个时候,客户端可以看见服务“Arith”,并且有“Arith.Multiply”方法和“Arith.Divide”方法。 调用其中一个,客户端首先连接服务:
client, err := rpc.DialHTTP("tcp", serverAddress + ":1234")if err != nil { log.Fatal("dialing:", err)}
当它要调用远程服务的时候:
// Synchronous callargs := &server.Args{7,8}var reply interr = 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 callquotient := 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处理器。 它仍然需要调用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处理器。 它仍然需要调用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 req *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
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~