Golang中的RPC和gRPC

來源:互聯網
上載者:User

一、RPC編程

  • 參考資料

    <<Go語言編程>> --許式偉
    gRPC介紹與安裝
    gRPC官方文檔
    gRPC中文文檔
    protocol-buffers

  • 介紹
RPC(Remote Procedure Call,遠端程序呼叫)是一種通過網路從遠端電腦程式上請求服務,而不需要瞭解底層網路細節的應用程式通訊協定。RPC協議構建於TCP或UDP,或者是HTTP上。允許開發人員直接調用另一台伺服器上的程式,而開發人員無需另外的為這個調用過程編寫網路通訊相關代碼,使得開發網路分布式程式在內的應用程式更加容易

RPC採用用戶端-伺服器端的工作模式,請求程式就是一個用戶端,而服務提供者就是一個伺服器端。當執行一個遠端程序呼叫時,用戶端程式首先先發送一個帶有參數的調用資訊到服務端,然後等待服務端響應。在服務端,服務進程保持睡眠狀態直到用戶端的調用資訊到達。當一個調用資訊到達時,服務端獲得進程參數,計算出結果,並向用戶端發送應答資訊。然後等待下一個調用。

  • Go語言中的RPC支援與處理
在Go中,標準庫提供的net/rpc包實現了RPC協議需要的相關細節,開發人員可以很方便的使用該包編寫RPC的服務端和用戶端程式。這使得用Go語言開發的多個進程之間的通訊變得非常簡單
net/rpc包允許PRC用戶端程式通過網路或者其他IO串連調用一個遠程對象的公開方法(該方法必須是外部可訪問即首字母大寫)。在PRC服務端,可將一個對象註冊為可訪問的服務,之後該對象的公開方法就能夠以遠端方式提供訪問。一個RPC服務端可以註冊多個不通類型的對象,但<font color="Brown">不允許註冊同一類型的多個對象。</font>
  • 一個對象中只有滿足如下條件的方法,才能被PRC服務端設定為可供遠端存取
    1.<font color="Brown">必須是在對象外部可公開調用的方法(首字母大寫)</font>
    2.<font color="Brown">必須有兩個參數,且參數的類型都必須是包外部可以訪問的類型或者是Go內建支援的類型</font>
    3.<font color="Brown">第二個參數必須是一個指標</font>
    4.<font color="Brown">方法必須返回一個error類型的值</font>
    用代碼錶示

    func (t T*)MethodName(argType T1,replyType *T2)error  

    在上面這行代碼中,類型 T T1 T2 預設會使用Go內建的encoding/gob包進行編碼和解碼
    改方法的第一個參數表示由PRC用戶端傳入的參數,第二個參數表示要返回給PRC用戶端的結果。改方法最後返回一個error類型

  • RPC用戶端和伺服器端的使用
RPC服務端可以通過調用 ```rpc.ServerConn```處理單個串連請求。多數情況下,通過tcp或是http在某個網路地址上監聽然後再建立該服務是個不錯的選擇  在RPC用戶端,Go的net/rpc包提供了便利的```rpc.Dial()```和```rpc.DialHTTP()```方法來與指定的RPC服務建立串連。在建立串連之後,Go的net/rpc包允許我們使用通過或者非同步方式接受RPC服務端的結果。調用RPC用戶端的```Call()```方法則進行同步處理。這個時候用戶端程式按照順序執行。當調用RPC用戶端的```Go()```方法時,則進行非同步處理。用戶端無需等待服務端的結果即可執行後面的程式,當接收到服務端響應時,再對其進行相應的處理。 無論是哪個方法,都必須要指定要調用的服務及其方法名稱,以及一個用戶端傳入參數的引用,還有一個用於接收處理結果參數的指標  如果沒有指定RPC傳輸過程中使用何種編碼解碼器,預設使用Go標準庫提供的eccoding/gob包進行資料轉送   
  • 程式碼範例
伺服器端代碼
package mainimport (    "errors"    "log"    "net"    "net/http"    "net/rpc"    "os"    "time")type Args struct {    A, B int}type Quotient struct {    Quo, Rem int}type Arith int//計算乘積func (t *Arith) Multiply(args *Args, reply *int) error {    time.Sleep(time.Second * 3) //睡三秒,同步調用會等待,非同步會先往下執行    *reply = args.A * args.B    return nil}//計算商和餘數func (t *Arith) Divide(args *Args, quo *Quotient) error {    time.Sleep(time.Second * 3)    if args.B == 0 {        return errors.New("divide by zero")    }    quo.Quo = args.A / args.B    quo.Rem = args.A % args.B    return nil}func main() {    //建立對象    arith := new(Arith)    //rpc服務註冊了一個arith對象 公開方法供用戶端調用    rpc.Register(arith)    //指定rpc的傳輸協議 這裡採用http協議作為rpc調用的載體 也可以用rpc.ServeConn處理單個串連請求    rpc.HandleHTTP()    l, e := net.Listen("tcp", ":1234")    if e != nil {        log.Fatal("listen error", e)    }    go http.Serve(l, nil)    os.Stdin.Read(make([]byte, 1))}
用戶端代碼
package mainimport (    "fmt"    "log"    "net/rpc"    "time")type Args struct {    A, B int}type Quotient struct {    Quo, Rem int}func main() {    //調用rpc服務端提供的方法之前,先與rpc服務端建立串連    client, err := rpc.DialHTTP("tcp", "127.0.0.1:1234")    if err != nil {        log.Fatal("dialHttp error", err)        return    }    //同步調用服務端提供的方法    args := &Args{7, 8}    var reply int    //可以查看源碼 其實Call同步調用是用非同步呼叫實現的。後續再詳細學習    err = client.Call("Arith.Multiply", args, &reply) //這裡會阻塞三秒    if err != nil {        log.Fatal("call arith.Multiply error", err)    }    fmt.Printf("Arith:%d*%d=%d\n", args.A, args.B, reply)    //非同步呼叫    quo := Quotient{}    divCall := client.Go("Arith.Divide", args, &quo, nil)    //使用select模型監聽通道有資料時執行,否則執行後續程式    for {        select {        case <-divCall.Done:            fmt.Printf("商是%d,餘數是%d\n", quo.Quo, quo.Rem)        default:            fmt.Println("繼續向下執行....")            time.Sleep(time.Second * 1)        }    }}

說明

//Go函數的原型,注意其最後一個參數是一個channel 也就是調用結果存到了這個channel裡 裡面的類型資料是*Call類型//如果你不傳遞這個channel這個參數,Go函數內部會預設建立一個*Call類型的10個長度的channel來緩衝結果資料。channel的名字叫做//Done也就是傳回值*Calll倆面的最後一個值  //好吧,其實我就是想說,cient.Go的傳回值包含了最後一個參數(channel),想擷取調用結果,可以從參數管道中直接擷取,也可以從傳回值//Done中擷取// Call represents an active RPC.type Call struct {    ServiceMethod string      // The name of the service and method to call.    Args          interface{} // The argument to the function (*struct).    Reply         interface{} // The reply from the function (*struct).    Error         error       // After completion, the error status.    Done          chan *Call  // Strobes when call is complete.}func (client *Client) Go(serviceMethod string, args interface{}, reply interface{}, done chan *Call) *Call {

二 gRpc

  • gRPC是什麼
gRPC中文文檔

gRPC是一個高效能、開源、通用的RPC架構。基於HTTP/2協議標準設計開發,預設採用Protocol Buffers資料序列化協議([Protocol Buffers基本文法]()),支援多種開發語言。gRPC提供了一種簡單的方法來精確的定義服務,並且為用戶端和服務端自動產生可靠的功能庫

  • gRPC應用情境
在gRPC用戶端可以直接調用不通伺服器上的遠程程式,就想調用本地程式一樣,很容易構建分布式應用和服務。和很多RPC系統一樣,服務負責實現定義好的介面並處理用戶端請求,用戶端根據介面描述直接調用需要的服務。用戶端和伺服器可以分別使用gRPC支援的不同語言實現

  • 安裝

protobuf Golang外掛程式

執行完成後會在GOPATH/bin目錄下產生 protoc-gen-go工具,在編譯.proto檔案時,protoc命令需要用到此外掛程式

[關於protobuf文法和基本使用]()

go get -u github.com/golang/protobuf/{proto,protoc-gen-go}

grpc-go golang第三方庫下載(需要翻牆 用ss配置http代理完成終端翻牆))

go get -u google.golang.org/grpc
  • gPRC列子 hello gRPC

流程

1.編寫.proto描述檔案  2.編譯產生.pb.go檔案  3.用戶端實現約定的介面並提供服務  4.用戶端按照約定調用方法請求服務  

目錄結構

$GOPATH/src/go_demo/23gPRC/|—— hello/    |—— client/        |—— main.go   // 用戶端    |—— server/        |—— main.go   // 服務端|—— proto/    |—— hello.proto   // proto描述檔案    |—— hello.pb.go   // proto編譯後檔案
  • 程式碼範例
proto rpc服務描述檔案
syntax = "proto3"; //指定proto版本package proto;//定義請求結構message HelloRequest{    string name=1;}//定義響應結構message HelloReply{    string message=1;}//定義Hello服務service Hello{    //定義服務中的方法    rpc SayHello(HelloRequest)returns (HelloReply){}}
protoc -I . --go_out=plugins=grpc:. ./hello.proto
hello.proto檔案中定義了一個Hello Service 該服務包含了一個SayHello方法 同時聲明了HelloRequest和HelloReply訊息結構
用於請求和響應。用戶端使用HelloRequest參數調用SayHello方法請求服務端 服務端響應HelloReply訊息
根據hello.proto檔案編譯產生Golang源檔案 hello.pb.go
源檔案中包含訊息傳遞的請求和響應結構。服務端註冊對象的方法 建立用戶端 以及調用服務端方法
伺服器端代碼
package mainimport (    "fmt"    pb "go_demo/23gRPC/proto"    "net"    "golang.org/x/net/context"    "google.golang.org/grpc")const (    //gRPC服務地址    Address = "127.0.0.1:50052")//定義一個helloServer並實現約定的介面type helloService struct{}func (h helloService) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {    resp := new(pb.HelloReply)    resp.Message = "hello" + in.Name + "."    return resp, nil}var HelloServer = helloService{}func main() {    listen, err := net.Listen("tcp", Address)    if err != nil {        fmt.Printf("failed to listen:%v", err)    }    //實現gRPC Server    s := grpc.NewServer()    //註冊helloServer為用戶端提供服務    pb.RegisterHelloServer(s, HelloServer) //內部調用了s.RegisterServer()    fmt.Println("Listen on" + Address)    s.Serve(listen)}
go run main.go
Listen on127.0.0.1:50052
用戶端代碼
package mainimport (    "fmt"    pb "go_demo/23gRPC/proto"    "golang.org/x/net/context"    "google.golang.org/grpc")const (    Address = "127.0.0.1:50052")func main() {    //串連gRPC伺服器    conn, err := grpc.Dial(Address, grpc.WithInsecure())    if err != nil {        fmt.Println(err)    }    defer conn.Close()    //初始化用戶端    c := pb.NewHelloClient(conn)    //調用方法    reqBody := new(pb.HelloRequest)    reqBody.Name = "gRPC"    r, err := c.SayHello(context.Background(), reqBody)    if err != nil {        fmt.Println(err)    }    fmt.Println(r.Message)}
go run main.go
hello gRpc
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.