Go簡單實現RPC和gRPC的調用

RPC

RPC(Remote Procedure Call Protocol)——遠程過程調用協議,它是一種通過網絡從遠程計算機程序上請求
服務,而不需要了解底層網絡技術的協議。
簡單來說,就是跟遠程訪問或者web請求差不多,都是一個client向遠端服務器請求服務返回結果,但是web請求
使用的網絡協議是http高層協議,而rpc所使用的協議多爲TCP,是網絡層協議,減少了信息的包裝,加快了處理速
度。
golang本身有rpc包,可以方便的使用,來構建自己的rpc服務,下邊是一個簡單是實例,可以加深我們的理解
在這裏插入圖片描述

1.調用客戶端句柄;執行傳送參數
2.調用本地系統內核發送網絡消息
3.消息傳送到遠程主機
4.服務器句柄得到消息並取得參數
5.執行遠程過程
6.執行的過程將結果返回服務器句柄
7.服務器句柄返回結果,調用遠程系統內核
8.消息傳回本地主機
9.客戶句柄由內核接收消息
10.客戶接收句柄返回的數據

服務端

package main
import (
"net/http"
"net/rpc"
"net"
"github.com/astaxie/beego"
"io"
) //- 方法是導出的
//- 方法有兩個參數,都是導出類型或內建類型
//- 方法的第二個參數是指針
//- 方法只有一個error接口類型的返回值
//
//func (t *T) MethodName(argType T1, replyType *T2) error
type Panda int;
func (this *Panda)Getinfo(argType int, replyType *int) error {
    beego.Info(argType)
   *replyType =1 +argType

return nil
}
 func main() {
//註冊1個頁面請求
   http.HandleFunc("/panda",pandatext)
//new 一個對象
   pd :=new(Panda)
//註冊服務
//Register在默認服務中註冊並公佈 接收服務 pd對象 的方法
   rpc.Register(pd)
   rpc.HandleHTTP()
//建立網絡監聽
  ln , err :=net.Listen("tcp","127.0.0.1:10086")
  if err != nil{
  beego.Info("網絡連接失敗")
} 
  beego.Info("正在監聽10086")
//service接受偵聽器l上傳入的HTTP連接,
   http.Serve(ln,nil)
} //用來現實網頁的web函數
func pandatext(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w,"panda")
}

客戶端

package main
import (
"net/rpc"
"github.com/astaxie/beego"
)
 func main() {
//rpc的與服務端建立網絡連接
cli,err := rpc.DialHTTP("tcp","127.0.0.1:10086")
if err !=nil {
   beego.Info("網絡連接失敗")
} 
   var val int
//遠程調用函數(被調用的方法,傳入的參數 ,返回的參數)
  err =cli.Call("Panda.Getinfo",123,&val)
 if err!=nil{
    beego.Info("打call失敗")
}
   beego.Info("返回結果",val)
}

GRPC

在 gRPC裏客戶端應用可以像調用本地對象一樣直接調用另一臺不同的機器上服務端應用的方法,使得您能夠更容易地創建分佈式應用和服務。與許多 RPC系統類似, gRPC也是基於以下理念:定義一個服務,指定其能夠被遠程調用的方法(包含參數和返回類型)。在服務端實現這個接口,並運行一個 gRPC服務器來處理客戶端調用。在客戶端擁有一個存根能夠像服務端一樣的方法。 gRPC客戶端和服務端可以在多種環境中運行和交互 -從 google內部的服務器到你自己的筆記本,並且可以用任何 gRPC支持的語言 來編寫。所以,你可以很容易地用 Java創建一個gRPC服務端,用 Go、 Python、Ruby來創建客戶端。此外, Google最新 API將有 gRPC版本的接口,使你很容易
地將 Google的功能集成到你的應用裏。

GRPC使用 protocol buffers

gRPC默認使用protoBuf,這是 Google開源的一套成熟的結構數據序列化機制(當然也可以使用其他數據格式如JSON)。正如你將在下方例子裏所看到的,你用 proto files創建 gRPC服務,用 protoBuf消息類型來定義方法參
數和返回類型。你可以在 Protocol Buffers文檔找到更多關於 protoBuf的資料。 雖然你可以使用 proto2 (當前默認的 protocol buffers版本 ),我們通常建議你在 gRPC裏使用 proto3,因爲這樣你可以使用 gRPC支持全部範圍的
的語言,並且能避免 proto2客戶端與 proto3服務端交互時出現的兼容性問題,反之亦然。

在這裏插入圖片描述

go語言實現GRPC遠程調用

protobuf協議定義

創建一個 protobuf package,如: my_rpc_proto;
在$GOPATH/src/下創建 /my_grpc_proto/文件夾裏面創建 protobuf協議文件 helloServer.proto

#到工作目錄
$ CD $GOPATH/src/
#創建目錄
$ mkdir grpc/myproto
#進入目錄
$ cd grpc/myproto
#創建proto文件
$ vim helloServer.proto
syntax = "proto3";
package my_grpc_proto;
service HelloServer{
// 創建第一個接口
rpc SayHello(HelloRequest)returns(HelloReplay){}
// 創建第二個接口
rpc GetHelloMsg(HelloRequest)returns(HelloMessage){}
}
 message HelloRequest{
   string name = 1 ;
} 
message HelloReplay{
    string message = 1;
} 
message HelloMessage{
   string msg = 1;
}

在當前文件下,編譯 helloServer.proto文件

$ protoc --go_out=./ *.proto #不加grpc插件
$ protoc --go_out=plugins=grpc:./ *.proto #添加grpc插件
#對比發現內容增加
#得到 helloServer.pb.go文件

gRPC-Server編寫

package main
import (
"net"
"fmt"
"google.golang.org/grpc"
pt "demo/grpc/proto"
"context"
)
 const (
    post = "127.0.0.1:18881"
)
 //對象要和proto內定義的服務一樣
type server struct{}
//實現RPC SayHello 接口
func(this *server)SayHello(ctx context.Context,in *pt.HelloRequest)(*pt.HelloReplay, error){
   return &pt.HelloReplay{Message:"hello"+in.Name},nil
} 

//實現RPC GetHelloMsg 接口
func (this *server) GetHelloMsg(ctx context.Context, in *pt.HelloRequest)(*pt.HelloMessage, error) {
   return &pt.HelloMessage{Msg: "this is from server HAHA!"}, nil
}

 func main() {
//監聽網絡
   ln ,err :=net.Listen("tcp",post)
if err!=nil {
   fmt.Println("網絡異常",err)
}

// 創建一個grpc的句柄
srv:= grpc.NewServer()
//將server結構體註冊到 grpc服務中
pt.RegisterHelloServerServer(srv,&server{})

//監聽grpc服務

err= srv.Serve(ln)
if err!=nil {
   fmt.Println("網絡啓動異常",err)
}
}

gRPC-Client編寫

package main
import (
"google.golang.org/grpc"
pt "demo/grpc/proto"
"fmt"
"context"
)
 const (
    post = "127.0.0.1:18881"
)
 func main() {
// 客戶端連接服務器
conn,err:=grpc.Dial(post,grpc.WithInsecure())
if err!=nil {
fmt.Println("連接服務器失敗",err)
} 


defer conn.Close()
//獲得grpc句柄
c:=pt.NewHelloServerClient(conn)

// 遠程調用 SayHello接口
//遠程調用 SayHello接口
r1, err := c.SayHello(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("cloud not get Hello server ..", err)
  return
}
 fmt.Println("HelloServer resp: ", r1.Message)
 
//遠程調用 GetHelloMsg接口
r2, err := c.GetHelloMsg(context.Background(), &pt.HelloRequest{Name: "panda"})
if err != nil {
fmt.Println("cloud not get hello msg ..", err)
  return
} 
fmt.Println("HelloServer resp: ", r2.Msg)
}

#先運行 server,後運行 client
#得到以下輸出結果
HelloServer resp: hellopanda
HelloServer resp: this is from server HAHA!
#如果反之則會報錯

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章