Skip to content

Alipebt/geeRPC

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

17 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

go语言实现RPC框架

1. 服务端与消息编码

1.1 消息的序列化与反序列化

Header struct是经典RPC调用所需要的参数,分为服务名和方法名请求ID错误信息三个字段,其中请求ID用来区分不同的请求,之后需要编解码的数据即为Header和另一个为infterface的主体

Codec接口是处理Header struct的函数集合,其构造函数NewCodecFunc需传入io.ReadWriteCloser,由于Codec可能处理多个类型的编解码,所以将其存入NewCodecFuncMap,便可利用key来寻找合适的编解码函数,其中NewCodecFuncMap[GobType] = NewGobCodec就是将GobType类型的编解码交给NewGobCodec处理初始化

// 这是确保接口被实现常用的方式。即利用强制类型转换
// 确保 struct GobCodec 实现了接口 Codec
// 这样 IDE 和编译期间就可以检查,而不是等到使用的时候
var _ Codec = (*GobCodec)(nil)

1.2 通信过程

客户端与服务端通信一般需要协商一些内容,例如 HTTP 报文,分为 headerbody 两部分,body 的格式和长度通过 header 中的 Content-TypeContent-Length 指定,服务端通过解析 header 就能够知道如何从 body 中读取需要的信息。对于 RPC 协议来说,这部分协商是需要自主设计的。一般在报文的最开始会规划固定的字节,来协商相关的信息。比如第1个字节用来表示序列化方式,第2个字节表示压缩方式,第3-6字节表示 header 的长度,7-10 字节表示 body 的长度。

| Option{MagicNumber: xxx, CodecType: xxx} | Header{ServiceMethod ...} | Body interface{} |
| <------      固定 JSON 编码      ------> | <-------   编码方式由 CodeType 决定  ------->|

在一次连接中,Option 固定在报文的最开始,Header 和 Body 可以有多个,即报文可能是这样的:

| Option | Header1 | Body1 | Header2 | Body2 | ...

1.3 服务端的实现

首先需要一个server结构体,不需要任何成员字段,通过NewServer()创建一个默认server实例。

Accept()server的一个方法,需要net.Listener作为参数。内部通过for循环等待socket建立连接,并开启协程处理,处理过程交给了ServerConn()方法

启动方法很简单:

lis,_ := net.Listen("tcp",":9999")
geerpc.Accept(lis)

ServeConn 的实现就和之前讨论的通信过程紧密相关了,首先使用 json.NewDecoder 反序列化得到 Option 实例,检查 MagicNumberCodeType的值是否正确。然后根据CodeType得到对应的消息编解码器,接下来的处理交给 serverCodec

serveCodec 的过程非常简单。主要包含三个阶段

  • 读取请求 readRequest
  • 处理请求 handleRequest
  • 回复请求 sendResponse

在一次连接中,允许接收多个请求,即多个request headerrequest body,因此这里使用了 for 无限制地等待请求的到来,直到发生错误(例如连接被关闭,接收到的报文有问题等),这里需要注意的点有三个:

  • handleRequest 使用了协程并发执行请求。
  • 处理请求是并发的,但是回复请求的报文必须是逐个发送的,并发容易导致多个回复报文交织在一起,客户端无法解析。在这里使用锁(sending)保证。

New接口:

func New(typ Type ) value

New 返回一个 Value,表示指向指定类型的新零值的指针。也就是说,返回值的类型是 PointerTo(typ)

func (Value) Inetrface

接口将v 的当前值作为接口{}返回。它相当于:

var i interface{} = (v 的基础值)

如果 Value 是通过访问未导出的结构字段获得的,它会发生恐慌

func (Value) Elem

func (v Value) Elem() Value

Elem 返回接口 v 包含的值或指针 v 指向的值。如果 v 的种类不是接口或指针,它会发生恐慌。如果 v 为 nil,则返回零值

func Dial

func Dial(network, address string) (Conn, error)

拨号(Dial)连接到指定网络上的地址

About

Studying the geeRPC (github.com/geektutu/blog)

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages