go微服務架構go-micro深度學習(二) 入門例子

來源:互聯網
上載者:User

    上一篇文章簡單介紹了go-micro的整體架構結構,這一篇主要寫go-micro使用方式的例子,中間會穿插一些go-micro的源碼,和調用流程圖,幫大家更好的理解go-micro的底層。更詳細更具體的調用流程和細節,會在以後的文章裡詳細講解。

     例子的github地址: gomicrorpc   跑一遍例子,也就會明白個大概。

安裝所需要的環境

    go-micro服務發現預設使用的是consul,

brew install consulconsul agent -dev

   或者直接使用使用docker跑

docker run -p 8300:8300 -p 8301:8301 -p 8301:8301/udp -p 8302:8302/udp -p 8302:8302 -p 8400:8400 -p 8500:8500 -p 53:53/udp consul

    我個人更喜歡etcdv3原因我上一篇也有提到過,gomicro服務發現不支援consul叢集,我之前也寫過etcdv3 叢集的搭建和使用文章,有時間大家可以看一下

    安裝go-micro架構

go get github.com/micro/go-micro

    安裝protobuf和依賴 prtobuf的基礎知識我這裡就不講了,如果不瞭解的可以看一下官方文檔,就是一個跨平台,跨語言的資料序列化庫,簡單易學。

    是go-micro用於協助我們產生服務介面和一系列的調用代碼

brew install protobufgo get -u -v github.com/golang/protobuf/{proto,protoc-gen-go}go get -u -v github.com/micro/protoc-gen-micro

    protobuf也可以直接從源碼安裝

wget https://github.com/protocolbuffers/protobuf/releases/download/v3.6.1/protobuf-all-3.6.1.tar.gztar zxvf protobuf-all-3.6.1.tar.gzcd protobuf-3.6.1/./autogen.sh./configure makemake installprotoc -h

 

    安裝micro工具包,這個安裝是可選項,micro提供了一系列的工具來協助我們更好的使用go-micro。

go get github.com/micro/micro

 

例子1

建立proto檔案common.proto,這個檔案包含了傳入和返回的參數,參數包含了常用的基礎類型、數組、map等。還有一個Say 服務,這個服務裡有一個rpc方法。

syntax = "proto3";package model;message SayParam {    string msg = 1;}message Pair {    int32 key = 1;    string values = 2;}message SayResponse {    string msg = 1;    // 數組    repeated string values = 2;    // map    map<string, Pair> header = 3;    RespType type = 4;}enum RespType {    NONE = 0;    ASCEND = 1;    DESCEND = 2;}// 服務介面service Say {    rpc Hello(SayParam) returns (SayResponse) {}}

 

在根目錄下運行,產生兩個模板檔案

  protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. example1/proto/*.proto 

一個檔案是proto的go 結構檔案,還有一個go-micro rpc的介面檔案。

server 端:

type Say struct {}func (s *Say) Hello(ctx context.Context, req *model.SayParam, rsp *model.SayResponse) error {    fmt.Println("received", req.Msg)    rsp.Header = make(map[string]*model.Pair)    rsp.Header["name"] = &model.Pair{Key: 1, Values: "abc"}    rsp.Msg = "hello world"    rsp.Values = append(rsp.Values, "a", "b")    rsp.Type = model.RespType_DESCEND    return nil}func main() {    // 我這裡用的etcd 做為服務發現,如果使用consul可以去掉    reg := etcdv3.NewRegistry(func(op *registry.Options){        op.Addrs = []string{            "http://192.168.3.34:2379", "http://192.168.3.18:2379", "http://192.168.3.110:2379",        }    })    // 初始化服務    service := micro.NewService(        micro.Name("lp.srv.eg1"),        micro.Registry(reg),    )    // 註冊 Handler    model.RegisterSayHandler(service.Server(), new(Say))    // run server    if err := service.Run(); err != nil {        panic(err)    }}

服務發現我使用的是etcdv3  替換了預設的consul

micro.NewService 初始化服務,然後返回一個Service介面的執行個體,newService()方法的大概流程如下,

 

先是給各個介面初始化預設值,再使用傳入的值替換預設值,這也是go-micro可替換外掛程式的地方。

service有一個Init()可選方法,這是一個單例方法,

func (s *service) Init(opts ...Option) {    // process options    for _, o := range opts {        o(&s.opts)    }    s.once.Do(func() {        // save user action        action := s.opts.Cmd.App().Action        // set service action        s.opts.Cmd.App().Action = func(c *cli.Context) {                .........//這裡就不把代碼全顯示出來了                .........        }}

官方的例子中者有顯示調用,其實是不必的,因為在替換預設值的時候都會調用Init方法

比如micro.Name()方法,已經調用了Init()方法了

// Name of the servicefunc Name(n string) Option {    return func(o *Options) {        o.Server.Init(server.Name(n))    }}

 service.Run()方法 調用流程

   因為在初始化的時候沒有指定連接埠,系統會自動分配一個連接埠號碼分給Server,並把這個server的資訊註冊到Register。

   BeferStart和AfterStart也都是可以自訂的

client 端:

func main() {    // 我這裡用的etcd 做為服務發現,如果使用consul可以去掉    reg := etcdv3.NewRegistry(func(op *registry.Options){        op.Addrs = []string{            "http://192.168.3.34:2379", "http://192.168.3.18:2379", "http://192.168.3.110:2379",        }    })    // 初始化服務    service := micro.NewService(        micro.Registry(reg),    )    sayClent := model.NewSayService("lp.srv.eg1", service.Client())    rsp, err := sayClent.Hello(context.Background(), &model.SayParam{Msg: "hello server"})    if err != nil {        panic(err)    }    fmt.Println(rsp)}

 上面根據proto檔案的產生的兩個檔案中有一個是rpc的介面檔案,介面檔案已經幫我們把調用方法的整個流程封裝好了。

  只需要給出服務名稱和licent就可以。然後調用Hello方法

  源碼:

func (c *sayService) Hello(ctx context.Context, in *SayParam, opts ...client.CallOption) (*SayResponse, error) {    req := c.c.NewRequest(c.name, "Say.Hello", in)    out := new(SayResponse)    err := c.c.Call(ctx, req, out, opts...)    if err != nil {        return nil, err    }    return out, nil}

 主要的流程裡都在c.c.Call方法裡。簡單來說流程如下

 

就是得到節點資訊address,根據address去查詢 pool裡是否有串連,如果有則取出來,如果沒有則建立,然後進行資料轉送,傳輸完成後把client放回到pool內。pool的大小也是可以控制的,這部分的代碼讀起來特別爽,具體的細節和處理流程會在以後的文章裡詳細講解

 例子2

    例子1,做了一個簡單的服務,已經不能再簡單了,只是為了能讓大家熟悉一下go-micro。看完例子1後應該會有更多的想法,想使用更多的go-micro的功能,比如protobuf產生的類都在一起,如果想model和api分開怎麼處理,怎麼使用go-micro的雙向流,怎麼使用訊息推送,等等。所以我就雙做了一個小例子,這個例子裡包含了一些東西。

    

    這個例子我就只說一下組織圖,也沒有多少代碼,大家有時間看一下就ok了。

    proto下的兩個檔案夾,一個model一個rpcapi,是把資料和api分開,api引用了model

看一下rpcapi

syntax = "proto3";package rpcapi;import "github.com/lpxxn/gomicrorpc/example2/proto/model/common.proto";// 服務介面service Say {    rpc Hello(model.SayParam) returns (model.SayResponse) {}    rpc Stream(model.SRequest) returns (stream model.SResponse) {}}

    import了model裡的common.proto

在產生的時候一個只要go_out另一個只要micro_out就好了

  protoc --proto_path=$GOPATH/src:. --go_out=. example2/proto/model/*.proto     protoc --proto_path=$GOPATH/src:. --micro_out=. example2/proto/rpcapi/*.proto 

    訂閱一個資訊

    // Register Subscribers    if err := server.Subscribe(server.NewSubscriber(common.Topic1, subscriber.Handler)); err != nil {        panic(err)    }

   當有資訊發送時,所有訂閱了lp.srv.eg2.topic1這個資訊的服務都會收到資訊

   用戶端發送資訊

    p := micro.NewPublisher(common.Topic1, service.Client())    p.Publish(context.TODO(), &model.SayParam{Msg: lib.RandomStr(lib.Random(3, 10))})

   如果是生產環境一定不要用go-micro預設的資訊發布和訂閱處理方式,micro的外掛程式plugin裡是有很多成熟的外掛程式。

   使用雙向流的小功能

   這個方法只是每次向用戶端發送一些資料,每次只發送一部分。比如我們給用戶端推送的資料很大時,一次性全都推過去,是不太正確的做法,分批推送還是比較好的。

func (s *Say) Stream(ctx context.Context, req *model.SRequest, stream rpcapi.Say_StreamStream) error {    for i := 0; i < int(req.Count); i++ {        rsp := &model.SResponse{}        for j := lib.Random(3, 5); j < 10; j++ {            rsp.Value = append(rsp.Value, lib.RandomStr(lib.Random(3, 10)))        }        if err := stream.Send(rsp); err != nil {            return err        }        // 類比處理過程        time.Sleep(time.Microsecond * 50)    }    return nil    return nil}

 

    希望這個小例子能讓大家入門go-micro.

 

相關文章

聯繫我們

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