學習微服務框架是為了更了解各種架構對不同專案上的適應性
雖然並不是每個專案都適合為服務,但是也需要先熟練各種兵器好應對更種情況

介紹

go-micro 是一個使用 go 實現的微服務框架,可以方便幫助 go 進行微服務的開發

主要功能

  1. 服務發現
  2. 平衡負載
  3. Client/Server
  4. Pub/Sub
  5. gRPC

Require

Install

安裝工具

1
2
3
4
go mod init .
go get github.com/micro/micro/v2@v2.4.0
go get -u github.com/golang/protobuf/protoc-gen-go@v1.3.0
go get -u github.com/micro/protoc-gen-micro/v2@v2.3.0

mod version

這邊遇到了一些麻煩的狀況,各個版本的相依性很高,不同版本搭配可能造成錯誤

所以在這邊提供的以下的版本是確定可運行的

1
2
3
4
5
6
7
8
go 1.14

require (
	github.com/golang/protobuf v1.4.2
	github.com/micro/go-micro/v2 v2.9.1
	github.com/micro/protoc-gen-micro/v2 v2.3.0 // indirect
	google.golang.org/grpc v1.26.0
)

Simple

我試著使用各種寫法來實現簡單的範例

使用 go-micro 進行服務發現,再透過 grpc 進行傳輸

Proto

一個簡單的 Request/Respone

syntax = "proto3";

option go_package= ".;rpc";

service Greeter {
    rpc Hello (Request) returns (Response){};
}

message Request {
    string name = 1;
}

message Response {
    string greeting = 2;
}

然後透過以下語法進行轉換,會得到除了 grpc 的 go file 以外,還會得到給 go-micro 使用的 go file

1
2
3
4
protoc proto/*.proto --go_out=plugins=grpc:rpc --micro_out=rpc

# hello.pb.go
# hello.pb.micro.go

Server

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package main

import (
	"context"
	"fmt"

	pb "github.com/cody0704/grpc_example01/rpc"
	micro "github.com/micro/go-micro/v2"
	"github.com/micro/go-micro/v2/server/grpc"
)

type Greeter struct {
}

func (g *Greeter) Hello(context context.Context, req *pb.Request, rsp *pb.Response) error {
	rsp.Greeting = "Hello " + req.Name
	return nil
}

func main() {
	service := micro.NewService(
		micro.Name("cody.micro.srv.hello"),
		micro.Version("latest"),
	)

	sServer := service.Server()
	sServer.Init(
		grpc.MaxMsgSize(2 * 1024 * 1024 * 1024),
	)

	err := pb.RegisterGreeterHandler(sServer, new(Greeter))
	if err != nil {
		fmt.Println(err)
	}

	if err := service.Run(); err != nil {
		fmt.Println(err)
	}
}

Client

會在範例中使用那麼多參數

主要是因為沒有找到有人教學如何使用相關參數的使用

官方的文檔貌似不是那麼齊全

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package main

import (
	"context"
	"log"
	"time"

	pb "github.com/cody0704/micro_example01/rpc"

	micro "github.com/micro/go-micro/v2"
	clientOpt "github.com/micro/go-micro/v2/client"
	"github.com/micro/go-micro/v2/client/grpc"
)

func main() {
	// Create a new service
	service := micro.NewService(
		micro.Name("hello.client"),
	)

	service.Init()
	client := service.Client()
	client.Init(
		clientOpt.Retries(3),
		clientOpt.DialTimeout(60*time.Second),
		clientOpt.RequestTimeout(60*time.Second),
		clientOpt.Retry(func(ctx context.Context, req clientOpt.Request, retryCount int, err error) (bool, error) {
			log.Println(req.Method(), retryCount, " client retry")
			return true, nil
		}),
		grpc.MaxRecvMsgSize(2*1024*1024*1024),
		grpc.MaxRecvMsgSize(2*1024*1024*1204),
	)

	helloClient := pb.NewGreeterService("cody.micro.srv.hello", client)

	rsp, err := helloClient.Hello(context.TODO(), &pb.Request{Name: "Cody"})
	if err != nil {
		log.Println(err)
	}

	log.Println(rsp.GetGreeting())
}

Run

然後進行運行

1
2
3
4
5
6
7
8
9
# terminal 1
go run server/main.go
> 2020-09-18 11:16:20  file=v2@v2.9.1/service.go:200 level=info Starting [service] cody.micro.srv.hello
> 2020-09-18 11:16:20  file=grpc/grpc.go:864 level=info Server [grpc] Listening on [::]:52355
> 2020-09-18 11:16:20  file=grpc/grpc.go:697 level=info Registry [mdns] Registering node: cody.micro.srv.hello-d7e523ea-c574-400b-8bac-b664d4f30b9c

# terminal 2
go run client/main.go
2020-09-18 11:16:32.070476 I | Hello Cody

結論

由於只有一對一,所以運行起來感覺會跟一般的 gRPC 相同,不過底層是透過 multicast 進行呼叫進行呼叫 所以不用指定 IP 位址,取而代之的是一個定義的名稱進行呼叫

Ref

  1. 使用 go-micro 的时候 etcd,grpc-gateway 产生冲突
  2. Go Micro 中文文档 - 发布订阅
  3. Micro In Action