2022年的第一個rpc,比以往來的更早一些...
留杭過年...寫點東西
初始化專案gorpc
借助go module我們可以輕易創建一個新的專案
mkdir gorpc
go mod init github.com/taadis/gorpc
// output:
go: creating new go.mod: module github.com/taadis/gorpc
訊息約定
rpc 的客戶端和服務端之間通信需要傳輸資料訊息,典型的訊息結構一般有2部分組成
- header 訊息頭 - 用來承載約定內容,一般是相對固定的,
- body 訊息體 - 用來承載用戶資料,通常是不定長,也就是動態的
這里我們先定義更泛的訊息體,因為訊息體是動態的,所以我們可以直接用go中的 interface{} 來定義,就不用特別宣告一個結構體了,
然后我們來定義一個header結構體
// codec.go
type Header struct {
Sequence uint64 // sequence number chosen by client
ServiceMethod string // format "Service.Method"
Error error
}
Sequence 序列號是客戶端帶過來的,每個請求總要有點不一樣的地方,方便服務端根據序列號來區分不同的呼叫,可以理解是唯一ID,
ServiceMethod 是要遠程呼叫的服務下的方法名詞,這里對照為go語言中結構體的方法名,比如用戶創建方法"User.Create",
Error 是錯誤資訊,用來放置一端發生的錯誤,以便另一種接收到訊息時能根據錯誤進行處理,而不是直接丟失回應,
訊息的編碼解碼
rpc 的客戶端和服務端之間通信的訊息有其特有的格式,因此都需要涉及編碼和解碼這一關鍵步驟,也就是我們熟稱的序列化和反序列化,
以便抽象理解,我們定義一個統一的Codec介面
// codec.go
type Codec interface {
ReadHeader(*Header) error
ReadBody(interface{}) error
Write(*Header, interface{}) error
}
ReadHeader 讀取資訊頭,如果有錯誤回傳錯誤,
ReadBody 讀取訊息體,資料是動態的,所以使用interface{}作為引數,如果有錯誤回傳錯誤,
Write 訊息接收處理完成后,我們需要把結果告知給客戶端,需要一個寫入的操作,
這里我們借助標準庫內置的encoding/gob來提高作業效率,
當然你也可以用encoding/json,encoding/xml或者其他編解碼包,這里選擇encoding/gob,僅僅是因為這是go所特有的,JUST GO,
接下來我們基于encoding/gob實作一個gobCodec
rpc 請求是一種網路請求,本質還是I/O,所以我們可以用io.ReadWriteCloser來定義網路鏈接conn.
通過gob.Decoder解碼請求中的資料流至對應的結構體引數,
完成服務端呼叫之后,把回傳結果再用gob.Encoder編碼至資料流中,
最后通過bufio.Writer寫入資料完成回應,
// codec.go
type gobCodec struct {
conn io.ReadWriteCloser
decoder *gob.Decoder
encoder *gob.Encoder
writeBuf *bufio.Writer
}
封裝一個newGobCodec函式,方便后續呼叫,
func newGobCodec(conn io.ReadWriteCloser) Codec {
writeBuf := bufio.NewWriter(conn)
return &gobCodec{
conn: conn,
decoder: gob.NewDecoder(conn),
encoder: gob.NewEncoder(writeBuf),
writeBuf: writeBuf,
}
}
實作 Codec 介面中的 ReadHeader 方法
func (c *gobCodec) ReadHeader(header *Header) error {
return c.decoder.Decode(header)
}
實作 Codec 介面中的 ReadBody 方法
func (c *gobCodec) ReadBody(body interface{}) error {
return c.decoder.Decode(body)
}
實作 Codec 介面中的 Write 方法
func (c *gobCodec) Write(header *Header, body interface{}) error {
defer func() {
if c.writeBuf.Flush() != nil {
c.conn.Close()
}
}()
if err := c.encoder.Encode(header); err != nil {
return err
}
if err := c.encoder.Encode(body); err != nil {
return err
}
return nil
}
至此,rpc 中比較底層的資料編碼和解碼我們已經抽象出來了Codec介面,并借助encoding/gob實作了gobCodec.
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/423233.html
標籤:其他
上一篇:豐富Bean的配置
下一篇:[exaqp]STL
