编写Golang服务

这里主要和大家演示如何使用go-micro。

如果想先从更高的角度了解相关的工具集,可以查看博客https://micro.mu/blog/2016/03/20/micro.html

编写服务

顶级的服务接口Service,是构建服务所需的主要组件。它把所有Go-Micror的基础包打包成单一组件接口。

  1. type Service interface {
  2. Init(...Option)
  3. Options() Options
  4. Client() client.Client
  5. Server() server.Server
  6. Run() error
  7. String() string
  8. }

1. 初始化

可以使用micro.NewService创建服务

  1. import "github.com/micro/go-micro"
  2. service := micro.NewService()

初始化时,也可以传入相关选项

  1. service := micro.NewService(
  2. micro.Name("greeter"),
  3. micro.Version("latest"),
  4. )

所有的可选参数参考:配置项

Go Micro也提供通过命令行参数micro.Flags传递配置参数:

  1. import (
  2. "github.com/micro/cli"
  3. "github.com/micro/go-micro"
  4. )
  5. service := micro.NewService(
  6. micro.Flags(
  7. cli.StringFlag{
  8. Name: "environment",
  9. Usage: "The environment",
  10. },
  11. )
  12. )

解析命令行标识参数可以使用service.Init,增加标识参数可以使用micro.Action选项:

  1. service.Init(
  2. micro.Action(func(c *cli.Context) {
  3. env := c.StringFlag("environment")
  4. if len(env) > 0 {
  5. fmt.Println("Environment set to", env)
  6. }
  7. }),
  8. )

Go Micro提供预置的标识,service.Init执行时就会设置并解析这些参数。所有的标识参考.

2. 定义API

我们使用protobuf文件来定义服务的API接口。使用protobuf可以非常方便去严格定义API,提供服务端与客户端双边具体一致的类型。

下面是定义的示例

greeter.proto

  1. syntax = "proto3";
  2. service Greeter {
  3. rpc Hello(HelloRequest) returns (HelloResponse) {}
  4. }
  5. message HelloRequest {
  6. string name = 1;
  7. }
  8. message HelloResponse {
  9. string greeting = 2;
  10. }

我们定义了一个服务叫做Greeter的处理器,它有一个接收HelloRequest并返回HelloResponse的Hello方法。

3. 生成API接口

我们需要下面这个工具来生成protobuf代码文件,它们负责生成定义的go代码实现。

  1. go get github.com/golang/protobuf/{proto,protoc-gen-go}
  1. go get github.com/micro/protoc-gen-micro
  1. protoc --proto_path=$GOPATH/src:. --micro_out=. --go_out=. greeter.proto

生成的类现在可以引入handler中,在服务或客户端来创建请求了。

下面是部分生成的代码

  1. type HelloRequest struct {
  2. Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"`
  3. }
  4. type HelloResponse struct {
  5. Greeting string `protobuf:"bytes,2,opt,name=greeting" json:"greeting,omitempty"`
  6. }
  7. // Greeter service 客户端的API
  8. type GreeterClient interface {
  9. Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error)
  10. }
  11. type greeterClient struct {
  12. c client.Client
  13. serviceName string
  14. }
  15. func NewGreeterClient(serviceName string, c client.Client) GreeterClient {
  16. if c == nil {
  17. c = client.NewClient()
  18. }
  19. if len(serviceName) == 0 {
  20. serviceName = "greeter"
  21. }
  22. return &greeterClient{
  23. c: c,
  24. serviceName: serviceName,
  25. }
  26. }
  27. func (c *greeterClient) Hello(ctx context.Context, in *HelloRequest, opts ...client.CallOption) (*HelloResponse, error) {
  28. req := c.c.NewRequest(c.serviceName, "Greeter.Hello", in)
  29. out := new(HelloResponse)
  30. err := c.c.Call(ctx, req, out, opts...)
  31. if err != nil {
  32. return nil, err
  33. }
  34. return out, nil
  35. }
  36. // Greeter service 服务端
  37. type GreeterHandler interface {
  38. Hello(context.Context, *HelloRequest, *HelloResponse) error
  39. }
  40. func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler) {
  41. s.Handle(s.NewHandler(&Greeter{hdlr}))
  42. }

4. 实现handler处理器

服务端需要注册handlers,这样才能提供服务并接收请求。处理器相当于是一个拥有公共方法的公共类,它需要符合签名func(ctx context.Context, req interface{}, rsp interface{}) error

通过上面的内容,我们看到,Greeter interface的签名的看上去就是这样:

  1. type GreeterHandler interface {
  2. Hello(context.Context, *HelloRequest, *HelloResponse) error
  3. }

Greeter处理器实现:

  1. import proto "github.com/micro/examples/service/proto"
  2. type Greeter struct{}
  3. func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
  4. rsp.Greeting = "Hello " + req.Name
  5. return nil
  6. }

处理器会与服务一起被注册,就像http处理器一样。

  1. service := micro.NewService(
  2. micro.Name("greeter"),
  3. )
  4. proto.RegisterGreeterHandler(service.Server(), new(Greeter))

5. 运行服务

服务可以调用server.Run运行起来。这一步会让服务绑到配置中的地址(默认遵循RFC1918,分配随机的端口)接收请求。

另外,这一步会在服务启动时向注册中心注册,并在服务接收到关闭信号时卸载

  1. if err := service.Run(); err != nil {
  2. log.Fatal(err)
  3. }

6. 完整的服务代码

greeter.go

  1. package main
  2. import (
  3. "log"
  4. "github.com/micro/go-micro"
  5. proto "github.com/micro/examples/service/proto"
  6. "golang.org/x/net/context"
  7. )
  8. type Greeter struct{}
  9. func (g *Greeter) Hello(ctx context.Context, req *proto.HelloRequest, rsp *proto.HelloResponse) error {
  10. rsp.Greeting = "Hello " + req.Name
  11. return nil
  12. }
  13. func main() {
  14. service := micro.NewService(
  15. micro.Name("greeter"),
  16. micro.Version("latest"),
  17. )
  18. service.Init()
  19. proto.RegisterGreeterHandler(service.Server(), new(Greeter))
  20. if err := service.Run(); err != nil {
  21. log.Fatal(err)
  22. }
  23. }

需要注意的是,要保证服务发现机制运行起来,这样服务才能注册,其它服务或客户端才能发现它。快速启动可参考

编写客户端

客户端包用于查询服务,当创建服务时,也包含了一个客户端,这个客户端匹配服务所使用的初始化包。

查询上面的服务很简单:

  1. // 创建greate客户端,这需要传入服务名与服务的客户端方法构建的客户端对象
  2. greeter := proto.NewGreeterClient("greeter", service.Client())
  3. // 在Greeter handler上请求调用Hello方法
  4. rsp, err := greeter.Hello(context.TODO(), &proto.HelloRequest{
  5. Name: "John",
  6. })
  7. if err != nil {
  8. fmt.Println(err)
  9. return
  10. }
  11. fmt.Println(rsp.Greeter)

proto.NewGreeterClient 需要服务名与客户端来请求服务。

完整示例参考:go-micro/examples/service.