這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
簡單介紹
Micro是一個用來簡化微服務開發的架構,提供了如下功能:
- Go Micro - 基於Golang的外掛程式式RPC架構,提供服務發現,用戶端負載平衡,編碼,同步和非同步通訊功能。
- API - API Gateway(API Gateway), 用來提供處理http請求。可以作為一個http的反向 Proxy或者翻譯相關的http請求到RPC服務。
- Sidecar - 用來接入其他語言編寫的應用到Micro中。
- Web - 提供一個web dashboard,並且可以為Micro應用提供反向 Proxy。
- CLI - 用來跟Micro服務互動的命令列工具。
Bot - 用它我們可以在我們的服務中與Slack, HipChat, XMPP通訊。
架構如所示:
Micro架構圖
安裝
- 由於Micro的服務發現並沒有自己實現,僅僅是提供Plugin來接入第三方服務發現(consul, etcd), 預設使用的是consule
安裝參考: consul installation doc
- 安裝protobuf
go get github.com/micro/protobuf/{proto,protoc-gen-go}
- 安裝go-micro, 參考github go-micro
go get github.com/micro/go-micro
- 安裝micro, 參考github micro
go get github.com/micro/micro
接下來我們簡單使用micro來構建一個hello world 應用。
- 建立一個proto檔案, proto文法請參考google protobuf
# hello_world.protosyntax = "proto3";service HelloWorld { rpc Hello(HelloRequest) returns (HelloResponse) {}}message HelloRequest { string name = 1;}message HelloResponse { string greeting = 2;}
- 通常我們需要將上述hello_world.proto檔案編譯輸出相關語言的代碼實現檔案。
protoc --go_out=plugins=micro:. hello_world.proto
組建檔案hello_world.pb.go
// Code generated by protoc-gen-go. DO NOT EDIT.// source: hello_world.proto/*Package hello_world is a generated protocol buffer package.It is generated from these files: hello_world.protoIt has these top-level messages: HelloWorldRequest HelloWorldResponse*/package hello_worldimport proto "github.com/golang/protobuf/proto"import fmt "fmt"import math "math"import ( client "github.com/micro/go-micro/client" server "github.com/micro/go-micro/server" context "golang.org/x/net/context")// Reference imports to suppress errors if they are not otherwise used.var _ = proto.Marshalvar _ = fmt.Errorfvar _ = math.Inf// This is a compile-time assertion to ensure that this generated file// is compatible with the proto package it is being compiled against.// A compilation error at this line likely means your copy of the// proto package needs to be updated.const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto packagetype HelloWorldRequest struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`}func (m *HelloWorldRequest) Reset() { *m = HelloWorldRequest{} }func (m *HelloWorldRequest) String() string { return proto.CompactTextString(m) }func (*HelloWorldRequest) ProtoMessage() {}func (*HelloWorldRequest) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }func (m *HelloWorldRequest) GetName() string { if m != nil { return m.Name } return ""}type HelloWorldResponse struct { Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"`}func (m *HelloWorldResponse) Reset() { *m = HelloWorldResponse{} }func (m *HelloWorldResponse) String() string { return proto.CompactTextString(m) }func (*HelloWorldResponse) ProtoMessage() {}func (*HelloWorldResponse) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }func (m *HelloWorldResponse) GetGreeting() string { if m != nil { return m.Greeting } return ""}func init() { proto.RegisterType((*HelloWorldRequest)(nil), "HelloWorldRequest") proto.RegisterType((*HelloWorldResponse)(nil), "HelloWorldResponse")}// Reference imports to suppress errors if they are not otherwise used.var _ context.Contextvar _ client.Optionvar _ server.Option// Client API for HelloWorld servicetype HelloWorldClient interface { Hello(ctx context.Context, in *HelloWorldRequest, opts ...client.CallOption) (*HelloWorldResponse, error)}type helloWorldClient struct { c client.Client serviceName string}func NewHelloWorldClient(serviceName string, c client.Client) HelloWorldClient { if c == nil { c = client.NewClient() } if len(serviceName) == 0 { serviceName = "helloworld" } return &helloWorldClient{ c: c, serviceName: serviceName, }}func (c *helloWorldClient) Hello(ctx context.Context, in *HelloWorldRequest, opts ...client.CallOption) (*HelloWorldResponse, error) { req := c.c.NewRequest(c.serviceName, "HelloWorld.Hello", in) out := new(HelloWorldResponse) err := c.c.Call(ctx, req, out, opts...) if err != nil { return nil, err } return out, nil}// Server API for HelloWorld servicetype HelloWorldHandler interface { Hello(context.Context, *HelloWorldRequest, *HelloWorldResponse) error}func RegisterHelloWorldHandler(s server.Server, hdlr HelloWorldHandler, opts ...server.HandlerOption) { s.Handle(s.NewHandler(&HelloWorld{hdlr}, opts...))}type HelloWorld struct { HelloWorldHandler}func (h *HelloWorld) Hello(ctx context.Context, in *HelloWorldRequest, out *HelloWorldResponse) error { return h.HelloWorldHandler.Hello(ctx, in, out)}func init() { proto.RegisterFile("hello_world.proto", fileDescriptor0) }var fileDescriptor0 = []byte{ // 136 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0xcc, 0x48, 0xcd, 0xc9, 0xc9, 0x8f, 0x2f, 0xcf, 0x2f, 0xca, 0x49, 0xd1, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x57, 0x52, 0xe7, 0x12, 0xf4, 0x00, 0x09, 0x86, 0x83, 0xc4, 0x82, 0x52, 0x0b, 0x4b, 0x53, 0x8b, 0x4b, 0x84, 0x84, 0xb8, 0x58, 0xf2, 0x12, 0x73, 0x53, 0x25, 0x18, 0x15, 0x18, 0x35, 0x38, 0x83, 0xc0, 0x6c, 0x25, 0x03, 0x2e, 0x21, 0x64, 0x85, 0xc5, 0x05, 0xf9, 0x79, 0xc5, 0xa9, 0x42, 0x52, 0x5c, 0x1c, 0xe9, 0x45, 0xa9, 0xa9, 0x25, 0x99, 0x79, 0xe9, 0x12, 0x4c, 0x60, 0xd5, 0x70, 0xbe, 0x91, 0x03, 0x17, 0x17, 0x42, 0x87, 0x90, 0x11, 0x17, 0x2b, 0x98, 0x27, 0x24, 0xa4, 0x87, 0x61, 0xa1, 0x94, 0xb0, 0x1e, 0xa6, 0xd9, 0x4a, 0x0c, 0x49, 0x6c, 0x60, 0x37, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x80, 0x8c, 0xf7, 0x9b, 0xb8, 0x00, 0x00, 0x00,}
下面簡述其中幾個關鍵輸出:
package hello_world // 訊息定義所在packagetype HelloWorldRequest struct { Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`} //請求訊息類type HelloWorldResponse struct { Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"`} // 響應訊息類type HelloWorldClient interface { Hello(ctx context.Context, in *HelloWorldRequest, opts ...client.CallOption) (*HelloWorldResponse, error)} // 服務要求介面, 定義用戶端可調用的要求方法'Hello', 用戶端編程使用type HelloWorldHandler interface { Hello(context.Context, *HelloWorldRequest, *HelloWorldResponse) error} // 服務端interface, 定義服務端需要實現的方法'Hello', 服務端編程使用func RegisterHelloWorldHandler(s server.Server, hdlr HelloWorldHandler, opts ...server.HandlerOption) { s.Handle(s.NewHandler(&HelloWorld{hdlr}, opts...))} // 服務端使用該方法註冊定義的Service, 服務端編程使用type HelloWorld struct { HelloWorldHandler}func (h *HelloWorld) Hello(ctx context.Context, in *HelloWorldRequest, out *HelloWorldResponse) error { return h.HelloWorldHandler.Hello(ctx, in, out)}// 定義HelloWorld類,需要服務端實現其中的Handle的方法'Hello', 服務端編程使用
服務端實現
package mainimport ( "fmt" "./hello_world" // import proto產生的類 "github.com/micro/go-micro" "golang.org/x/net/context")type HelloWorld struct{}func (g *HelloWorld) Hello(ctx context.Context, req *hello_world.HelloWorldRequest, rsp *hello_world.HelloWorldResponse) error { rsp.Greeting = "Hello World: " + req.Name return nil} // 實現hello_world service中Hello方法func main() { service := micro.NewService( micro.Name("hello_world"), // 定義service的名稱為hello_world micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), ) service.Init() // 初始化service hello_world.RegisterHelloWorldHandler(service.Server(), new(HelloWorld)) // 註冊服務 if err := service.Run(); err != nil { fmt.Println(err) } // 運行服務}
用戶端實現
package mainimport ( "fmt" "./hello_world" "github.com/micro/go-micro" "golang.org/x/net/context")func main() { service := micro.NewService( micro.Name("hello_world"), micro.Version("latest"), micro.Metadata(map[string]string{ "type": "helloworld", }), ) service.Init() greeter := hello_world.NewHelloWorldClient("hello_world", service.Client()) // 建立服務hello_world的client對象, 以便調用其中定義的RPC方法'Hello' rsp, err := greeter.Hello(context.TODO(), &hello_world.HelloWorldRequest{Name: "Alice"}) // 傳入HelloWorldRequest對象作為調用RPC方法的參數'Hello' if err != nil { fmt.Println(err) return } fmt.Println(rsp.Greeting)}
總結開發流程
- 公用開發模組
- 建立訊息protobuf檔案, 在其中定義訊息
- 根據定義的protobuf檔案,產生基於該語言的實作類別
- 服務端
- 實現在訊息中定義的RPC方法
- 初始化一個新的Service對象
- 註冊服務
- 運行服務
- 用戶端
- 初始化一個新的Service對象
- 定義請求訊息
- 傳入請求訊息調用服務端定義RPC方法
- 解釋響應訊息
代碼請參考https://github.com/zouqilin/micro_demo/tree/master/hello_world