初始go-micro微服务
go-micro 分为两大块,一块是Micro,相当于命令行工具集,一块是Go-Micro,两块构成了go-Micro,本章先主要了解Go-micro构造一个简单的微服务,然后逐步去讲解Micro命令行的作用
go-micro的使用
- go get github.com/micro/micro/v2 下载micro命令行
- micro new example 使用命生成示例代码
- 代码下载之后会提示我们下载protobuf的命令行工具,该工具主要是为了将protobuf自动化生成go-micro的代码
go get -u github.com/golang/protobuf/{proto,protoc-gen-go}
go get -u github.com/micro/protoc-gen-micro
- go mod tidy 下载go.mod中的包
- 生成protoc文件代码 protoc --proto_path=.:/d/gopath/src --go_out=. --micro_out=. proto/example/example.proto
代码解读
- 定义好接口文件
package go.micro.srv.example;
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;
}
////省略
- 使用protoc命令自动生成服务,使用自动生成服务的接口,即handler
### 实现定义的protoc文件
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
}
- 启动服务
//main.go
func main() {
// 生成微服务
service := micro.NewService(
micro.Name("go.micro.srv.example"),
micro.Version("latest"),
)
// 初始化服务
service.Init()
// 注册一个handler服务
example.RegisterExampleHandler(service.Server(), new(handler.Example))
// 注册一个订阅服务
micro.RegisterSubscriber("go.micro.srv.example", service.Server(), new(subscriber.Example))
// 启动服务
if err := service.Run(); err != nil {
log.Fatal(err)
}
}
- 启动main.go,服务就启动起来了,可以看到具体的输出
win不支持go自带的mdns的服务发现,建议替换为etcd
5.将服务替换为etcd
service := micro.NewService(
micro.Name("com.foo.srv.order"),
micro.Version("latest"),
micro.Registry(etcd.NewRegistry(func(op *registry.Options) {
op.Addrs = []string{
"http://127.0.0.1:2379",
}
})),)
通过上面的可以发现,micro内部很多实现都是使用interface的方式,我们可以通过传递不同的服务注册实现去替换到默认的,相当于可替换插件的方式,稍后的章节再理解这块的实现
- 服务启动了之后,可以使用web服务器去观察服务的状态 micro web 将启动一个web服务,这个服务可以去观察各个srv的状态
使用客户端去调取服务
func main() {
//生成一个客户端
service := micro.NewService(
micro.Name("com.foo.hello.client"),
micro.Registry(etcd.NewRegistry()),
)
//初始化
service.Init()
// 生成服务端调用,这里最最要的是服务端的名字,micro的服务发现是通过名字去处理的
client := example.NewExampleService("com.foo.srv.hello",service.Client())
d := example.Request{
Name:"xiaobai",
}
res,err := client.Call(context.Background(),&d)
fmt.Println(res,err)
}
通过客户端调用服务端,可以大致猜想服务发现应该是通过服务的名字去代理的
服务器的参数也可以使用自带参数传递进去,每个服务器使用自带的参数传递的话,每个服务必须使用一样的参数,才能保证每个服务使用的都是同一个服务发现服务器
go run main.go --registry=etcd --registry_address=127.0.0.1:2379
使用micro自带的web服务
micro也自带了一个简单的web服务,虽然没有gin提供的功能强大,简单的使用也是阔以的,下面使用micro自带的web服务,实现一个web服务,并且去调用服务端的接口
func main() {
//注册web服务
service := web.NewService(
web.Name("com.foo.hello.web"),
web.Registry(etcd.NewRegistry()),
)
service.Init()
//注册客户端调用服务
serviceClient := micro.NewService(
micro.Name("com.foo.hello.client"),
micro.Registry(etcd.NewRegistry()),
)
serviceClient.Init()
//构建客户端
srcClient = order.NewOrderService("com.foo.srv.order",serviceClient.Client())
//路由
service.HandleFunc("/abc", orderService)
//web服务启动
service.Run()
}
func orderService(writer http.ResponseWriter, request *http.Request) {
msg := request.URL.Query().Get("msg")
params := order.SayParam{
Msg:msg,
}
//调用第三方
r,err := srcClient.List(context.Background(),¶ms)
fmt.Fprint(writer,r)
fmt.Fprint(writer,err)
}
上面在浏览器发起请求之后,可以在服务端发现调用了,我们启动多个服务端,也是可以看到每个服务端基本都进行了调用,这就是服务发现的魅力,我们并不需要去关注哪个服务注册进来了,都是通过服务发现服务器统一去管理服务器。将服务杀死之后,发现调用方还是会调用已经死掉的服务,稍等一段时间,调用者才会发现这个服务已经死掉,说明micro的服务发现机制是服务端注册,然后使用心跳机制去检测服务端的状态,客户端并不关注服务端的状态
通过上面的粗略解决,我们搭建起了一个简单的服务,并且使用web方式去调取了远程服务,也对服务发现有个模凌两可的理解