佳木斯湛栽影视文化发展公司

主頁 > 知識庫 > Go實(shí)現(xiàn)簡易RPC框架的方法步驟

Go實(shí)現(xiàn)簡易RPC框架的方法步驟

熱門標(biāo)簽:網(wǎng)站排名優(yōu)化 Linux服務(wù)器 AI電銷 地方門戶網(wǎng)站 鐵路電話系統(tǒng) 服務(wù)外包 百度競價排名 呼叫中心市場需求

本文旨在講述 RPC 框架設(shè)計(jì)中的幾個核心問題及其解決方法,并基于 Golang 反射技術(shù),構(gòu)建了一個簡易的 RPC 框架。

項(xiàng)目地址:Tiny-RPC

RPC

RPC(Remote Procedure Call),即遠(yuǎn)程過程調(diào)用,可以理解成,服務(wù) A 想調(diào)用不在同一內(nèi)存空間的服務(wù) B 的函數(shù),由于不在一個內(nèi)存空間,不能直接調(diào)用,需要通過網(wǎng)絡(luò)來表達(dá)調(diào)用的語義和傳達(dá)調(diào)用的數(shù)據(jù)。

服務(wù)端

RPC 服務(wù)端需要解決 2 個問題:

  • 由于客戶端傳送的是 RPC 函數(shù)名,服務(wù)端如何維護(hù) 函數(shù)名 與 函數(shù)實(shí)體 之間的映射
  • 服務(wù)端如何根據(jù) 函數(shù)名 實(shí)現(xiàn)對應(yīng)的 函數(shù)實(shí)體 的調(diào)用

核心流程

  • 維護(hù)函數(shù)名到函數(shù)的映射
  • 在接收到來自客戶端的函數(shù)名、參數(shù)列表后,解析參數(shù)列表為反射值,并執(zhí)行對應(yīng)函數(shù)
  • 對函數(shù)執(zhí)行結(jié)果進(jìn)行編碼,并返回給客戶端

方法注冊

服務(wù)端需要維護(hù) RPC 函數(shù)名到 RPC 函數(shù)實(shí)體的映射,我們可以使用 map 數(shù)據(jù)結(jié)構(gòu)來維護(hù)映射關(guān)系。

type Server struct {
 addr string
 funcs map[string]reflect.Value
}

// Register a method via name
func (s *Server) Register(name string, f interface{}) {
 if _, ok := s.funcs[name]; ok {
 return
 }
 s.funcs[name] = reflect.ValueOf(f)
}

執(zhí)行調(diào)用

一般來說,客戶端在調(diào)用 RPC 時,會將 函數(shù)名 和 參數(shù)列表 作為請求數(shù)據(jù),發(fā)送給服務(wù)端。

由于我們使用了 map[string]reflect.Value 來維護(hù)函數(shù)名與函數(shù)實(shí)體之間的映射,則我們可以通過 Value.Call() 來調(diào)用與函數(shù)名相對應(yīng)的函數(shù)。

package main

import (
 "fmt"
 "reflect"
)

func main() {
 // Register methods
 funcs := make(map[string]reflect.Value)
 funcs["add"] = reflect.ValueOf(add)

 // When receives client's request
 req := []reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)}
 vals := funcs["add"].Call(req)
 var rsp []interface{}
 for _, val := range vals {
 rsp = append(rsp, val.Interface())
 }

 fmt.Println(rsp)
}

func add(a, b int) (int, error) {
 return a + b, nil
}

具體實(shí)現(xiàn)

由于篇幅的限制,此處沒有貼出服務(wù)端實(shí)現(xiàn)的具體代碼,細(xì)節(jié)請查看項(xiàng)目地址。

客戶端

RPC 客戶端需要解決 1 個問題:

  • 由于函數(shù)的具體實(shí)現(xiàn)在服務(wù)端,客戶端只有函數(shù)的原型,客戶端如何通過 函數(shù)原型 調(diào)用其 函數(shù)實(shí)體

核心流程

  • 對調(diào)用者傳入的函數(shù)參數(shù)進(jìn)行編碼,并傳送給服務(wù)端
  • 對服務(wù)端響應(yīng)數(shù)據(jù)進(jìn)行解碼,并返回給調(diào)用者

生成調(diào)用

我們可以通過 reflect.MakeFunc 為指定的函數(shù)原型綁定一個函數(shù)實(shí)體。

package main

import (
 "fmt"
 "reflect"
)

func main() {
 add := func(args []reflect.Value) []reflect.Value {
 result := args[0].Interface().(int) + args[1].Interface().(int)
 return []reflect.Value{reflect.ValueOf(result)}
 }

 var addptr func(int, int) int
 container := reflect.ValueOf(addptr).Elem()
 v := reflect.MakeFunc(container.Type(), add)
 container.Set(v)

 fmt.Println(addptr(1, 2))
}

具體實(shí)現(xiàn)

由于篇幅的限制,此處沒有貼出客戶端實(shí)現(xiàn)的具體代碼,細(xì)節(jié)請查看項(xiàng)目地址。

數(shù)據(jù)傳輸格式

我們需要定義服務(wù)端與客戶端交互的數(shù)據(jù)格式。

type Data struct {
 Name string    // service name
 Args []interface{} // request's or response's body except error
 Err string    // remote server error
}

與交互數(shù)據(jù)相對應(yīng)的編碼與解碼函數(shù)。

func encode(data Data) ([]byte, error) {
 var buf bytes.Buffer
 encoder := gob.NewEncoder(buf)
 if err := encoder.Encode(data); err != nil {
 return nil, err
 }
 return buf.Bytes(), nil
}

func decode(b []byte) (Data, error) {
 buf := bytes.NewBuffer(b)
 decoder := gob.NewDecoder(buf)
 var data Data
 if err := decoder.Decode(data); err != nil {
 return Data{}, err
 }
 return data, nil
}

同時,我們需要定義簡單的 TLV 協(xié)議(固定長度消息頭 + 變長消息體),規(guī)范數(shù)據(jù)的傳輸。

// Transport struct
type Transport struct {
 conn net.Conn
}

// NewTransport creates a transport
func NewTransport(conn net.Conn) *Transport {
 return Transport{conn}
}

// Send data
func (t *Transport) Send(req Data) error {
 b, err := encode(req) // Encode req into bytes
 if err != nil {
 return err
 }
 buf := make([]byte, 4+len(b))
 binary.BigEndian.PutUint32(buf[:4], uint32(len(b))) // Set Header field
 copy(buf[4:], b)                  // Set Data field
 _, err = t.conn.Write(buf)
 return err
}

// Receive data
func (t *Transport) Receive() (Data, error) {
 header := make([]byte, 4)
 _, err := io.ReadFull(t.conn, header)
 if err != nil {
 return Data{}, err
 }
 dataLen := binary.BigEndian.Uint32(header) // Read Header filed
 data := make([]byte, dataLen)       // Read Data Field
 _, err = io.ReadFull(t.conn, data)
 if err != nil {
 return Data{}, err
 }
 rsp, err := decode(data) // Decode rsp from bytes
 return rsp, err
}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

您可能感興趣的文章:
  • python使用rpc框架gRPC的方法
  • Java如何實(shí)現(xiàn)簡單的RPC框架
  • Java RPC框架過濾器機(jī)制原理解析
  • Java RPC框架如何實(shí)現(xiàn)客戶端限流配置
  • Java RPC框架熔斷降級機(jī)制原理解析
  • SpringBoot2.0 整合 Dubbo框架實(shí)現(xiàn)RPC服務(wù)遠(yuǎn)程調(diào)用方法
  • 分析JAVA中幾種常用的RPC框架
  • Java實(shí)現(xiàn)簡單的RPC框架的示例代碼
  • Java利用Sping框架編寫RPC遠(yuǎn)程過程調(diào)用服務(wù)的教程
  • php實(shí)現(xiàn)的一個簡單json rpc框架實(shí)例
  • python實(shí)現(xiàn)一個簡單RPC框架的示例

標(biāo)簽:蘭州 崇左 仙桃 衡水 銅川 湘潭 黃山 湖南

巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《Go實(shí)現(xiàn)簡易RPC框架的方法步驟》,本文關(guān)鍵詞  ;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問題,煩請?zhí)峁┫嚓P(guān)信息告之我們,我們將及時溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無關(guān)。
  • 相關(guān)文章
  • 收縮
    • 微信客服
    • 微信二維碼
    • 電話咨詢

    • 400-1100-266
    阜康市| 石狮市| 山西省| 安西县| 惠州市| 浏阳市| 绵竹市| 大化| 韩城市| 来宾市| 安义县| 广南县| 博客| 万山特区| 梁河县| 洪江市| 马公市| 江西省| 巴中市| 宜兰县| 车致| 新竹市| 祥云县| 寻甸| 安远县| 堆龙德庆县| 长葛市| 南昌县| 封丘县| 丰都县| 内江市| 都江堰市| 济宁市| 剑阁县| 铁力市| 乌鲁木齐县| 屏南县| 西藏| 抚宁县| 南靖县| 抚松县|