golang RPC包原理和使用詳細介紹
本篇文章旨在通過學習rpc包和github上的一個rpc小項目,熟悉和學習golang中各個包的使用
工作流程
通過閱讀官方文檔,瞭解瞭rpc的基本工作模式
- 第一步,建立一個用於遠程調用的包,存放僅供遠程調用使用的方法和類型-
- 第二步,實例化包的對象,並在rpc中註冊該包,以便之後的調用
- 第三步,建立一個服務端,接收客戶端的請求,使用編碼器解析請求後,根據請求中的方法和參數,調用第二步註冊的實例的方法,然後使用編碼器把返回值加密後,返回給客戶端
- 第四步,建立一個客戶端,連接服務端,成功後,向連接發送使用編碼器加密後的數據,然後等待服務端響應(同步或異步)。響應成功後,使用編碼器解析服務端返回的數據
第三步第四步中,多次用到的編碼器,是rpc包的關鍵。默認情況下,rpc包使用的是go特有的encoding/gob包進行數據的編碼和解碼。但是當我們服務端和客戶端使用瞭不同的語言時,若加密方法無法兼容,就會出現問題,所以rpc包支持自定義編碼器。
工作模式
go的rpc除瞭支持常規常規的tpc+端口的遠程調用方式,也支持基於http的遠程調用實現。但是,我都用rpc瞭,還用個毛的http形式。不過作為一種形式,我們出於禮貌的簡單瞭解下。
http模式
官方文檔的例子,就是使用的http形式的rpc,如下
服務端
//實例化rpc遠程調用的方法所屬對象 arith := new(Arith) //註冊對象 rpc.Register(arith) //把rpc監聽 對應到http處理器。即指定http請求addr+port時,調用的方法 rpc.HandleHTTP() //獲取監聽地址 l, e := net.Listen("tcp", ":1234") if e != nil { log.Fatal("listen error:", e) } //開啟一個go程,持續處理監聽數據 go http.Serve(l, nil)
客戶端
//連接 rpc的http服務端 client, err := rpc.DialHTTP("tcp", serverAddress + ":1234") if err != nil { log.Fatal("dialing:", err) } //同步調用 // 實例化rpc傳入參數 args := &server.Args{7,8} //聲明rpc 回復參數。傳入和回復參數,必須與調用方法中的參入類型一致 var reply int //調用rpc註冊的方法 err = 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) //或:異步調用 // Asynchronous call quotient := new(Quotient) divCall := client.Go("Arith.Divide", args, quotient, nil) replyCall := <-divCall.Done // will be equal to divCall
從上面的代碼可以看到,請求服務端時,需要客戶端在服務器發起http請求,如果直接在瀏覽器或者其他工具發起http請求則報錯,因為此時rpc.HandleHTTP方法指定的默認方法,使用的是默認gob編碼器,且隻接收connect類型的請求。查看源代碼,如下
直接發起的http請求,無法使用go獨有的gob包編碼,rpc服務端也就無法使用默認gob包解碼。
所以我們要寫一個新的方法代替rpc.HandleHTTp,把http請求綁定到一個使用其他解碼器的方法上,如下
//註冊路由和對應的handler http.HandleFunc("/json", func(rw http.ResponseWriter, r *http.Request) { //聲明一個客戶端連接對象 var conn io.ReadWriteCloser = struct { io.Writer io.ReadCloser }{ ReadCloser: r.Body, Writer: rw, } //也可以使用如下方法獲取接管客戶端連接,http處理器不在管理該鏈接,使用完畢後需要自行關閉鏈接 // conn, _, err := rw.(http.Hijacker).Hijack() // if err != nil { // log.Print("rpc hijacking fail: ", err.Error()) // return // } io.WriteString(conn, "HTTP/1.0 rpc-ok\n\n") //server.ServeConn(conn) //rpc.ServeRequest,指定編碼器,以同步的方式處理請求一次,編碼器內不關閉鏈接,由http服務處理。適用於http形式的請求,因為http是無狀態的,每次請求都是一個新的鏈接 //rpc.ServeCodec,指定編碼器,for循環接收客戶端鏈接的消息,每次消息處理開啟一個go程,相當於異步處理,直到客戶端關閉或解碼錯誤,跳出循環,關閉連接。適用客戶端,一次連接多次發送數據 rpc.ServeRequest(jsonrpc.NewServerCodec(conn))}) //監聽http請求 http.ListenAndServe("127.0.0.1:1234", nil)
上面代碼中的==jsonrpc.NewServerCodec(conn)==是一個官方定義好的json格式的編碼器。我們在http中發送數據時,隻要使用json格式,就能被服務端解析
執行go文件,在post模擬url請求,如下
服務器模式
服務端
//聲明和註冊rpc方法對象 //獲取監聽信息 lis, err := net.Listen("tcp", ":8082") if err != nil { log.Fatal(err) } //循環讀取監聽到的數據 for { //獲取一個客戶端連接 conn, err := lis.Accept() if err != nil { continue } //開啟一個go程序,使用自定義編碼器處理當前獲取連接 go s.Server.ServeCodec(jsonrpc.NewServerCodec(conn)) }
客戶端
//連接服務端 conn, err := net.Dial("tcp", ":8082")if err != nil { log.Fatal(err) } defer conn.Close() //使用json編碼器新建客戶端 client := &Client{rpc.NewClientWithCodec(jsonrpc.NewServerCodec(conn))} //聲明rpc方法中傳入和輸出的參數 resq := message.ArithRequest{A: 20, B: 5} resp := message.ArithResponse{} //調用rpc方法 err = client.Call("ArithService.Add", &resq, &resp) log.Printf("Arith.Add(%v, %v): %v ,Error: %v", resq.A, resq.B, resp.C, err)
到此這篇關於golang RPC包原理和使用詳細介紹的文章就介紹到這瞭,更多相關golang RPC包內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!
推薦閱讀:
- Go語言net包RPC遠程調用三種方式http與json-rpc及tcp
- golang實現一個簡單的websocket聊天室功能
- Go語言七篇入門教程六網絡編程
- Golang簡單實現http的server端和client端
- Golang實現簡易的rpc調用