1. 程式人生 > >動手實現一個簡單的 rpc 框架到入門 grpc (上)

動手實現一個簡單的 rpc 框架到入門 grpc (上)

rpc 全稱 `Remote Procedure Call` 遠端過程呼叫,即呼叫遠端方法。我們呼叫當前程序中的方法時很簡單,但是想要呼叫不同程序,甚至不同主機、不同語言中的方法時就需要藉助 rpc 來實現,下面我一步步實現一個簡單的 rpc 呼叫。 server 端註冊函式,執行並接收客戶端請求 ```go func main() { srv := NewServer() srv.Register("fn", fn) srv.Run() } //為了簡單,這裡只需要接收到訊息打印出就代表執行成功 func fn(args ...interface{}) { fmt.println(args) } ``` 定義請求格式 ```go type rpcData struct { Name string //函式名 Args []interface{} //引數 } ``` server 執行起來後,接收 socket 請求,解析訊息呼叫已註冊的函式 ```go //server結構體 type server struct { conn net.Conn //socket連線 maps map[string]reflect.Value //函式字典 } //建構函式 func NewServer() *server { return &server{ maps: make(map[string]reflect.Value), } } //註冊函式 func (s *server) Register(fname string, fun interface{}) { if _, ok := s.maps[fname]; !ok { s.maps[fname] = reflect.ValueOf(fun) } } //執行一個socket接收請求 func (s *server) Run() { listen, err := net.Listen("tcp4", ":3001") if err != nil { panic(err) } for { s.conn, err = listen.Accept() if err != nil { continue } go s.handleConnect() } } ``` 處理請求時,這裡為了簡單我使用 json 解析,同時需要定義一個簡單的協議:客戶端傳送時,前4個位元組放置訊息長度,這樣服務端接收到時就能知道訊息的長度,從而正常解碼訊息 ```go func (s *server) handleConnect() { for { header := make([]byte, 4) if _, err := s.conn.Read(header); err != nil { continue } bodyLen := binary.BigEndian.Uint32(header) body := make([]byte, int(bodyLen)) if _, err := s.conn.Read(body); err != nil { continue } var req rpcData if err := json.Unmarshal(body, &req); err != nil { continue } inArgs := make([]reflect.Value, len(req.Args)) for i := range req.Args { inArgs[i] = reflect.ValueOf(req.Args[i]) } fn := s.maps[req.Name] fn.Call(inArgs) } } ``` client 端只需呼叫函式,通過網路傳送請求 ```go func main() { var req = rpcData{"fn", []interface{}{1, "aaa"}} rpcCall(req) } func rpcCall(data rpcData) { conn, err := net.Dial("tcp4", "127.0.0.1:3001") if err != nil { panic(err) } req, err := json.Marshal(data) if err != nil { panic(err) } buf := make([]byte, 4+len(req)) binary.BigEndian.PutUint32(buf[:4], uint32(len(req))) copy(buf[4:], req) _, err = conn.Write(buf) if err != nil { panic(err) } } ``` 測試時,首先執行 server,然後執行 client,只要看到正確的列印就代表呼叫成功,這就是一個最簡單(簡陋)的 rpc 了。 當我們使用 grpc 這些 rpc 框架時,就可以不用自己實現訊息編碼解碼、socket連線這些細節,專注於業務邏輯,而且更為可靠。 > 參考: [https://github.com/ankur-anand/simple-go-rpc](https://github.com/ankur-anand/simple