在Go語言中使用 Protobuf-RPC

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Go語言版本的Protobuf-RPC基本算完成了. 現在簡單說下使用方法.

安裝測試環境

先下載代碼(不支援go get):

hg clone https://bitbucket.org/chai2010/gopath

然後下載後的目錄設定為GOPATH, 並添加$GOPATH/binPATH環境變數.

$GOPATH/bin中已經包含了Windows下的2.4.1版本的protoc.exe. 如果是Linux等系統, 請自行下載並安裝protoc程式.

安裝protoc.exe的Go語言外掛程式:

go install encoding/protobuf/protoc-gen-go

該外掛程式是基於code.google.com/p/goprotobuf/protoc-gen-go實現, 主要增加了encoding/protobuf/protoc-gen-go/generator/service.go檔案, 用於RPC的代碼產生. 產生的RPC代碼依賴net/rpc/protorpc, 這個包是Protobuf-RPC的底層實現, 可以單獨使用.

現在可以運行一下測試程式:

C:\>go test net/rpc/protorpc/service.pbok      net/rpc/protorpc/service.pb     2.123s

測試通過, 繼續.

編譯 proto 檔案

建立一個名為pbrpc的工作目錄, 再建立pbrpc/arith.pb的子目錄.
net/rpc/protorpc/service.pb/service.proto檔案複製到pbrpc/arith.pb的子目錄.

包名字改為arith, 檔案arith.proto的內容如下:

package arith;option cc_generic_services = true;option java_generic_services = true;option py_generic_services = true;message ArithRequest {    optional int32 a = 1;    optional int32 b = 2;}message ArithResponse {    optional int32 c = 1;}service ArithService {    rpc add (ArithRequest) returns (ArithResponse);    rpc mul (ArithRequest) returns (ArithResponse);    rpc div (ArithRequest) returns (ArithResponse);    rpc error (ArithRequest) returns (ArithResponse);}

主要是定義了一個ArithService介面. 要注意的是cc_generic_services/java_generic_services, py_generic_services幾個選項.
我前提提到的protoc-gen-go在產生代碼的時候, 這3個選項至少要有一個為true, 才會產生RPC的代碼.

當然, 如果不產生RPC代碼的話, 也是可以單獨使用net/rpc/protorpc包的. 不過protoc-gen-go產生的程式碼會簡便很多.

進入pbrpc/arith.pb的子目錄, 編譯arith.proto檔案:

protoc --go_out=. arith.proto

產生 arith.pb.go 檔案, 其中RPC的代碼主要是下面這些:

type ArithService interface {    Add(in *ArithRequest, out *ArithResponse) error    Mul(in *ArithRequest, out *ArithResponse) error    Div(in *ArithRequest, out *ArithResponse) error    Error(in *ArithRequest, out *ArithResponse) error}// RegisterArithService publish the given ArithService implementation on the server.func RegisterArithService(srv *rpc.Server, x ArithService) error {    if err := srv.RegisterName("ArithService", x); err != nil {        return err    }    return nil}// ServeArithService serves the given ArithService implementation on conn.func ServeArithService(conn io.ReadWriteCloser, x ArithService) error {    srv := rpc.NewServer()    if err := srv.RegisterName("ArithService", x); err != nil {        return err    }    srv.ServeCodec(protorpc.NewServerCodec(conn))    return nil}// ListenAndServeArithService listen announces on the local network address laddr// and serves the given ArithService implementation.func ListenAndServeArithService(network, addr string, x ArithService) error {    clients, err := net.Listen(network, addr)    if err != nil {        return err    }    srv := rpc.NewServer()    if err := srv.RegisterName("ArithService", x); err != nil {        return err    }    for {        conn, err := clients.Accept()        if err != nil {            return err        }        go srv.ServeCodec(protorpc.NewServerCodec(conn))    }    panic("unreachable")}type rpcArithServiceStub struct {    *rpc.Client}func (c *rpcArithServiceStub) Add(in *ArithRequest, out *ArithResponse) error {    return c.Call("ArithService.Add", in, out)}func (c *rpcArithServiceStub) Mul(in *ArithRequest, out *ArithResponse) error {    return c.Call("ArithService.Mul", in, out)}func (c *rpcArithServiceStub) Div(in *ArithRequest, out *ArithResponse) error {    return c.Call("ArithService.Div", in, out)}func (c *rpcArithServiceStub) Error(in *ArithRequest, out *ArithResponse) error {    return c.Call("ArithService.Error", in, out)}// DialArithService connects to an ArithService at the specified network address.func DialArithService(network, addr string) (*rpc.Client, ArithService, error) {    conn, err := net.Dial(network, addr)    if err != nil {        return nil, nil, err    }    c, srv := NewArithServiceClient(conn)    return c, srv, nil}// NewArithServiceClient returns a ArithService rpc.Client and stub to handle// requests to the set of ArithService at the other end of the connection.func NewArithServiceClient(conn io.ReadWriteCloser) (*rpc.Client, ArithService) {    c := rpc.NewClientWithCodec(protorpc.NewClientCodec(conn))    return c, &rpcArithServiceStub{c}}// NewArithServiceStub returns a ArithService stub to handle rpc.Client.func NewArithServiceStub(c *rpc.Client) ArithService {    return &rpcArithServiceStub{c}}

其中產生的伺服器端的代碼有: ListenAndServeArithService, ServeArithService, RegisterArithService.
產生的用戶端的介面有: DialArithService, NewArithServiceClient, NewArithServiceStub.
其中RPC介面對應ArithService介面.

編寫測試代碼

pbrpc目錄建立rpc_server.go檔案, 代碼如下:

package mainimport (    "encoding/protobuf/proto"    "errors"    "./arith.pb")type Arith intfunc (t *Arith) Add(args *arith.ArithRequest, reply *arith.ArithResponse) error {    reply.C = proto.Int32(args.GetA() + args.GetB())    return nil}func (t *Arith) Mul(args *arith.ArithRequest, reply *arith.ArithResponse) error {    reply.C = proto.Int32(args.GetA() * args.GetB())    return nil}func (t *Arith) Div(args *arith.ArithRequest, reply *arith.ArithResponse) error {    if args.GetB() == 0 {        return errors.New("divide by zero")    }    reply.C = proto.Int32(args.GetA() / args.GetB())    return nil}func (t *Arith) Error(args *arith.ArithRequest, reply *arith.ArithResponse) error {    return errors.New("ArithError")}func main() {    arith.ListenAndServeArithService("tcp", ":1234", new(Arith))}

最關鍵的是arith.ListenAndServeArithService("tcp", ":1234", new(Arith)). 當然, 也可以使用RegisterArithServiceServeArithService等介面進行定製.

然後在pbrpc建立rpc_client.go對應用戶端, 代碼如下:

package mainimport (    "encoding/protobuf/proto"    "log"    "./arith.pb")func main() {    // client    client, stub, err := arith.DialArithService("tcp", "127.0.0.1:1234")    if err != nil {        log.Fatalf(`arith.DialArithService("tcp", "127.0.0.1:1234"): %v`, err)    }    defer client.Close()    var args arith.ArithRequest    var reply arith.ArithResponse    // Add    args.A = proto.Int32(1)    args.B = proto.Int32(2)    if err = stub.Add(&args, &reply); err != nil {        log.Fatalf(`arith.Add: %v`, err)    }    if reply.GetC() != 3 {        log.Fatalf(`arith.Add: expected = %d, got = %d`, 3, reply.GetC())    }    // Mul    args.A = proto.Int32(2)    args.B = proto.Int32(3)    if err = stub.Mul(&args, &reply); err != nil {        log.Fatalf(`arith.Mul: %v`, err)    }    if reply.GetC() != 6 {        log.Fatalf(`arith.Mul: expected = %d, got = %d`, 6, reply.GetC())    }    // Div    args.A = proto.Int32(13)    args.B = proto.Int32(5)    if err = stub.Div(&args, &reply); err != nil {        log.Fatalf(`arith.Div: %v`, err)    }    if reply.GetC() != 2 {        log.Fatalf(`arith.Div: expected = %d, got = %d`, 2, reply.GetC())    }    // Div zero    args.A = proto.Int32(1)    args.B = proto.Int32(0)    if err = stub.Div(&args, &reply); err.Error() != "divide by zero" {        log.Fatalf(`arith.Error: expected = %s, got = %s`, "divide by zero", err.Error())    }    // Error    args.A = proto.Int32(1)    args.B = proto.Int32(2)    if err = stub.Error(&args, &reply); err.Error() != "ArithError" {        log.Fatalf(`arith.Error: expected = %s, got = %s`, "ArithError", err.Error())    }    log.Printf("Done")}

然後就可以啟動服務, 並測試用戶端了.

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.