多客户端整合

默认情况下,我们会为每个服务生成如下结构的客户端:

  1. client
  2. ├── client.go
  3. ├── v1
  4. ├── client.go
  5. └── types.go
  6. └── v2

在微服务的场景下,会生成大量的客户端,直接导致用户在使用客户端时引用各种各样的项目。为了解决这个问题,可以建立一个综合项目,然后将所有服务的客户端生成到该项目中:

  1. clientset
  2. ├── svca
  3. ├── client.go
  4. ├── v1
  5. ├── client.go
  6. └── types.go
  7. └── v2
  8. └── svcb
  9. ├── client.go
  10. ├── v1
  11. ├── client.go
  12. └── types.go
  13. └── v2

整合客户端

为了演示这个过程,我们逐步构建这个项目。

创建项目

首先创建 clientset 项目,用于保存所有服务的客户端:

  1. $ cd $GOPATH/src/
  2. $ mkdir clientset

然后创建两个服务项目(仅用于演示):

  1. $ nirvana init svca
  2. $ nirvana init svcb

即创建了 svca 和 svcb 两个服务项目。

生成客户端

生成 svca 的客户端:

  1. $ cd $GOPATH/src/svca
  2. $ nirvana client --output ../clientset/svca

生成 svcb 的客户端:

  1. $ cd $GOPATH/src/svcb
  2. $ nirvana client --output ../clientset/svcb

此时 clientset 的项目结构如下:

  1. clientset
  2. ├── svca
  3. ├── client.go
  4. └── v1
  5. ├── client.go
  6. └── types.go
  7. └── svcb
  8. ├── client.go
  9. └── v1
  10. ├── client.go
  11. └── types.go

这样所有客户端都在一个项目中,不需要依赖其他服务项目。

统一网关访问

在某些场景下,所有的微服务会通过一个公共的网关进行暴露。这样就需要再进一步对客户端进行整合。

创建 ClientSet

clientset 中创建 clientset.go

  1. $ cd $GOPATH/src/clientset
  2. $ touch clientset.go

clientset.go 代码如下:

  1. package clientset
  2. import (
  3. svca "clientset/svca"
  4. svcb "clientset/svcb"
  5. rest "github.com/caicloud/nirvana/rest"
  6. )
  7. // Interface describes a clientset.
  8. type Interface interface {
  9. // SvcA returns a client for svc a.
  10. SvcA() svca.Interface
  11. // SvcB returns a client for svc b.
  12. SvcB() svcb.Interface
  13. }
  14. // ClientSet contains multiple clients.
  15. type ClientSet struct {
  16. svcA svca.Interface
  17. svcB svcb.Interface
  18. }
  19. // NewClientSet creates a new client set.
  20. func NewClientSet(cfg *rest.Config) (Interface, error) {
  21. c := &ClientSet{}
  22. var err error
  23. c.svcA, err = svca.NewClient(cfg)
  24. if err != nil {
  25. return nil, err
  26. }
  27. c.svcB, err = svcb.NewClient(cfg)
  28. if err != nil {
  29. return nil, err
  30. }
  31. return c, nil
  32. }
  33. // MustNewClientSet creates a new client set or panic if an error occurs.
  34. func MustNewClientSet(cfg *rest.Config) Interface {
  35. c, err := NewClientSet(cfg)
  36. if err != nil {
  37. panic(err)
  38. }
  39. return c
  40. }
  41. // SvcA returns a client for svc a.
  42. func (c *ClientSet) SvcA() svca.Interface {
  43. return c.svcA
  44. }
  45. // SvcB returns a client for svc b.
  46. func (c *ClientSet) SvcB() svcb.Interface {
  47. return c.svcB
  48. }

使用 ClientSet

ClientSet 的使用方法和普通 Client 没有太大区别:

  1. func main() {
  2. cli := clientset.MustNewClientSet(&rest.Config{
  3. Scheme: "http",
  4. Host: "localhost:8080",
  5. })
  6. msgs, err := cli.SvcA().V1().ListMessages(context.Background(), 10)
  7. if err != nil {
  8. log.Fatal(err)
  9. }
  10. log.Info(msgs)
  11. }