go-micro提供了一个命令行工具micro
dotzdeMacBook-Pro-2:foo dotz$ micro
NAME:
micro - A microservice toolkit
USAGE:
micro [global options] command [command options] [arguments...]
VERSION:
1.0.0
COMMANDS:
api Run the api gateway
bot Run the chatops bot
cli Run the interactive CLI
registry Query registry
call Call a service
stream Create a service stream
publish Publish a message to a topic
health Query the health of a service
stats Query the stats of a service
list List items in registry
register Register an item in the registry
deregister Deregister an item in the registry
get Get item from registry
proxy Run the service proxy
service Run a micro service
new Create a service template
web Run the web dashboard
GLOBAL OPTIONS:
--client Client for go-micro; rpc [$MICRO_CLIENT]
--client_request_timeo
使用该工具的new命令我们快速生成一个微服务项目
dotzdeMacBook-Pro-2:foo dotz$ micro new foo
生成模板后,我们需要将Protobuf原型文件编译下,需要注意的是插件要使用micro(如果使用过gRPC可以类比)
dotzdeMacBook-Pro-2:foo dotz$ protoc --go_out=plugins=micro:. proto/example/*.proto
可以直接运行make build,这样直接生成接口文件,并完成项目编译
dotzdeMacBook-Pro-2:foo dotz$ make build
protoc --proto_path=/opt/goHome/src:. --micro_out=. --go_out=. proto/example/example.proto
go build -o foo-srv main.go plugin.go
然后运行项目,现在go-micro默认使用mdns插件进行服务注册和发现,可以传入参数,使用consul作为服务发现和注册插件
dotzdeMacBook-Pro-2:foo dotz$ ./foo-srv --registry=consul
2019/03/27 16:16:25 Transport [http] Listening on [::]:64352
2019/03/27 16:16:25 Broker [http] Connected to [::]:64353
2019/03/27 16:16:25 Registry [consul] Registering node: go.micro.srv.foo-d9ceb8a5-9a3c-4e5e-9533-124402568a3b
我们访问https://127.0.0.1:8500,可以发现我们的服务已经注册到consul
// 初始化服务
service := micro.NewService(
micro.Name("go.micro.srv.foo"),
micro.Version("latest"),
)
// 包含默认选项,和参数解析
service.Init()
初始化服务可以传入选项,如服务的名字,版本等,所有的选项定义在一个接口中:
type Options struct {
Broker broker.Broker
Cmd cmd.Cmd
Client client.Client
Server server.Server
Registry registry.Registry
Transport transport.Transport
// Before and After funcs
BeforeStart []func() error
BeforeStop []func() error
AfterStart []func() error
AfterStop []func() error
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
}
定义了服务之后,需要调用服务的Init函数初始化,主要是设置一些默认选项,和解析参数,如传入--registry=consul,会解析到consul注册服务选项。
交互基于Protobuf协议,方法跟gRPC是类似的,首先要定义接口原型(包含方法约定和消息约定),这里的示例定义了三个方法Call,Stream和PingPong
syntax = "proto3";
package go.micro.srv.foo;
service Example {
rpc Call(Request) returns (Response) {}
rpc Stream(StreamingRequest) returns (stream StreamingResponse) {}
rpc PingPong(stream Ping) returns (stream Pong) {}
}
message Message {
string say = 1;
}
message Request {
string name = 1;
}
message Response {
string msg = 1;
}
message StreamingRequest {
int64 count = 1;
}
message StreamingResponse {
int64 count = 1;
}
message Ping {
int64 stroke = 1;
}
message Pong {
int64 stroke = 1;
}
经过命令操作,从原型文件生成我们所需的接口代码文件
dotzdeMacBook-Pro-2:foo dotz$ protoc --go_out=plugins=micro:. proto/example/*.proto
接着我们需要实现预先定义的接口(protoc命令不是帮我们实现方法,只是将我们的接口规范信息转换为Protobuf格式),形式跟gRPC是一样的
func(ctx context.Context, req interface{}, rsp interface{}) error
下面是具体实现
package handler
import (
"context"
"github.com/micro/go-log"
example "foo/proto/example"
)
type Example struct{}
// Call is a single request handler called via client.Call or the generated client code
func (e *Example) Call(ctx context.Context, req *example.Request, rsp *example.Response) error {
log.Log("Received Example.Call request")
rsp.Msg = "Hello " + req.Name
return nil
}
// Stream is a server side stream handler called via client.Stream or the generated client code
func (e *Example) Stream(ctx context.Context, req *example.StreamingRequest, stream example.Example_StreamStream) error {
log.Logf("Received Example.Stream request with count: %d", req.Count)
for i := 0; i < int(req.Count); i++ {
log.Logf("Responding: %d", i)
if err := stream.Send(&example.StreamingResponse{
Count: int64(i),
}); err != nil {
return err
}
}
return nil
}
// PingPong is a bidirectional stream handler called via client.Stream or the generated client code
func (e *Example) PingPong(ctx context.Context, stream example.Example_PingPongStream) error {
for {
req, err := stream.Recv()
if err != nil {
return err
}
log.Logf("Got ping %v", req.Stroke)
if err := stream.Send(&example.Pong{Stroke: req.Stroke}); err != nil {
return err
}
}
}
接着就是将实现的接口注册为RPC方法
//注册RPC方法
example.RegisterExampleHandler(service.Server(), new(handler.Example))
// 运行服务
if err := service.Run(); err != nil {
log.Fatal(err)
}
package main
import (
"context"
"fmt"
micro "github.com/micro/go-micro"
example "foo/proto/example"
)
func main() {
// Create a new service. Optionally include some options here.
service := micro.NewService(micro.Name("greeter.client"))
service.Init()
// Create new greeter client
client := example.NewExampleService("go.micro.srv.foo", service.Client())
// Call the greeter
rsp, err := client.Call(context.TODO(), &example.Request{Name: "John"})
if err != nil {
fmt.Println(err)
}
// Print response
fmt.Println(rsp.GetMsg())
}
运行输出
dotzdeMacBook-Pro-2:client dotz$ go run client.go --registry=consul
Hello John