Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

一、简介

关于服务注册和服务发现介绍,我前面的文章有介绍过 - 服务注册和发现的文章

作为服务中心的软件有很多,比如 etcd,consul,nacos,zookeeper 等都可以作为服务中心。

go-kratos 把这些服务中心的功能作为插件,集成进了 kratos 中。

下面就用 etcd 作为服务中心来说说 kratos 里服务注册和服务发现功能的使用。

Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

二、服务注册和服务发现

2.1 接口定义

从 go-kratos 服务注册和发现文档中,我们知道它的接口定义非常简单:

注册和反注册服务:

type Registrar interface {     // 注册实例     Register(ctx context.Context, service *ServiceInstance) error     // 反注册实例     Deregister(ctx context.Context, service *ServiceInstance) error } 

获取服务:

type Discovery interface {     // 根据 serviceName 直接拉取实例列表     GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error)     // 根据 serviceName 阻塞式订阅一个服务的实例列表信息     Watch(ctx context.Context, serviceName string) (Watcher, error) } 

2.2 简单使用

服务端注册服务

使用 etcd 作为服务中心。

1.新建 etcd连接client, etcdregitry.New(client)

2.把 regitry传入 kratos.Registrar(r)

3.传入服务名称 kratos.Name("helloworld")

看官方的示例代码,server/main.go

package main  import ( 	"context" 	"fmt" 	"log"  	etcdregitry "github.com/go-kratos/kratos/contrib/registry/etcd/v2" 	"github.com/go-kratos/kratos/v2" 	"github.com/go-kratos/kratos/v2/middleware/recovery" 	"github.com/go-kratos/kratos/v2/transport/grpc" 	"github.com/go-kratos/kratos/v2/transport/http"  	pb "github.com/go-kratos/examples/helloworld/helloworld" 	etcdclient "go.etcd.io/etcd/client/v3" )  type server struct { 	pb.UnimplementedGreeterServer }  func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) { 	return &pb.HelloReply{Message: fmt.Sprintf("welcome %+v!", in.Name)}, nil }  func main() { 	// 创建 etcd client 连接 	client, err := etcdclient.New(etcdclient.Config{ 		Endpoints: []string{"127.0.0.1:2379"}, 	}) 	if err != nil { 		log.Fatal(err) 	}  	// 初始化 http server 	httpSrv := http.NewServer( 		http.Address(":8080"), 		http.Middleware( 			recovery.Recovery(), 		), 	)  	// 初始化 grpc server 	grpcSrv := grpc.NewServer( 		grpc.Address(":9000"), 		grpc.Middleware( 			recovery.Recovery(), 		), 	)  	// 在服务器上注册服务 	s := &server{} 	pb.RegisterGreeterServer(grpcSrv, s) 	pb.RegisterGreeterHTTPServer(httpSrv, s)  	// 创建一个 registry 对象,就是对 ectd client 操作的一个包装 	r := etcdregitry.New(client)  	app := kratos.New( 		kratos.Name("helloworld"), // 服务名称 		kratos.Server( 			httpSrv, 			grpcSrv, 		), 		kratos.Registrar(r), // 填入etcd连接(etcd作为服务中心) 	) 	if err := app.Run(); err != nil { 		log.Fatal(err) 	} } 

etcd作为服务中心的使用步骤图解:
Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

客户端获取服务

客户端的服务发现,主要也是 3 个步骤.

1.新建 etcd连接, 传入到 etcdregitry.New(client)

2.将 registry 传入 WithDiscovery(r)

3.获取服务WithEndpoint("discovery:///helloworld")

步骤与服务没有多大区别。

官方的示例代码,client/main.go

package main  import ( 	"context" 	"log" 	"time"  	"github.com/go-kratos/examples/helloworld/helloworld" 	etcdregitry "github.com/go-kratos/kratos/contrib/registry/etcd/v2" 	"github.com/go-kratos/kratos/v2/transport/grpc" 	"github.com/go-kratos/kratos/v2/transport/http" 	etcdclient "go.etcd.io/etcd/client/v3" 	srcgrpc "google.golang.org/grpc" )  func main() { 	client, err := etcdclient.New(etcdclient.Config{ 		Endpoints: []string{"127.0.0.1:2379"}, 	}) 	if err != nil { 		log.Fatal(err) 	}  	r := etcdregitry.New(client) // 传入 etcd client,也就是选择 etcd 为服务中心  	connGRPC, err := grpc.DialInsecure( 		context.Background(), 		grpc.WithEndpoint("discovery:///helloworld"), // 服务发现 		grpc.WithDiscovery(r),                        // 传入etcd registry 	) 	if err != nil { 		log.Fatal(err) 	} 	defer connGRPC.Close()  	connHTTP, err := http.NewClient( 		context.Background(), 		http.WithEndpoint("discovery:///helloworld"), 		http.WithDiscovery(r), 		http.WithBlock(), 	) 	if err != nil { 		log.Fatal(err) 	} 	defer connHTTP.Close()  	for { 		callHTTP(connHTTP) 		callGRPC(connGRPC) 		time.Sleep(time.Second) 	} }  func callHTTP(conn *http.Client) { 	client := helloworld.NewGreeterHTTPClient(conn) 	reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "go-kratos"}) 	if err != nil { 		log.Fatal(err) 	} 	log.Printf("[http] SayHello %+vn", reply) }  func callGRPC(conn *srcgrpc.ClientConn) { 	client := helloworld.NewGreeterClient(conn) 	reply, err := client.SayHello(context.Background(), &helloworld.HelloRequest{Name: "go-kratos"}) 	if err != nil { 		log.Fatal(err) 	} 	log.Printf("[grpc] SayHello %+vn", reply) } 

运行程序

1.运行etcd,没有安装etcd的请自行百度或gg安装

2.运行服务端

$ cd ./etcd/server $ go run ./main.go INFO msg=[HTTP] server listening on: [::]:8080 INFO msg=[gRPC] server listening on: [::]:9000 

3.运行客户端

$ cd ./client $ go run .main.go INFO msg=[resolver] update instances: [{"id":"8fc08b88-e37b-11ec-bb6f-88d7f62323b4","name":"helloworld","version":"","metadata":null,"endpoints":["http://192.168.56.1:8080","grpc://192.168.56.1:9000"]}] 2022/06/04 04:28:21 [http] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:21 [grpc] SayHello message:"welcome go-kratos!" INFO msg=[resolver] update instances: [{"id":"8fc08b88-e37b-11ec-bb6f-88d7f62323b4","name":"helloworld","version":"","metadata":null,"endpoints":["http://192.168.56.1:8080","grpc://192.168.56.1:9000"]}] 2022/06/04 04:28:22 [http] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:22 [grpc] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:23 [http] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:23 [grpc] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:24 [http] SayHello message:"welcome go-kratos!" 2022/06/04 04:28:24 [grpc] SayHello message:"welcome go-kratos!"  ... ... 

程序运行成功

看看 etcd 运行日志:

2022-06-04 04:26:03.896230 W | wal: sync duration of 1.1565369s, expected less than 1s 2022-06-04 04:26:03.991356 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged! 2022-06-04 04:27:18.187663 W | etcdserver: request "header:<ID:7587862969930594823 > put:<key:"/microservices/helloworld/8fc08b88-e37b-11ec-bb6f-88d7f62323b4" value_size:162 lease:7587862969930594821 >" with result "size:4" took too long (113.4545ms) to execute 

2.3 简析服务注册程序

一图解千言:

Go微服务框架go-kratos实战04:kratos中服务注册和服务发现的使用

  • etcdregitry.New(client)

    这里是对 etcd client 的包装处理,那么选择的服务中心就是 etcd。也可以使用consul,zookeeper 等,kratos 对它们都有封装。

// 对 etcd client 的包装在处理 r := etcdregitry.New(client)  // https://github.com/go-kratos/kratos/contrib/registry/etcd/registry.go#L56 // New creates etcd registry func New(client *clientv3.Client, opts ...Option) (r *Registry) { 	op := &options{ 		ctx:       context.Background(), 		namespace: "/microservices", 		ttl:       time.Second * 15, 		maxRetry:  5, 	} 	for _, o := range opts { 		o(op) 	} 	return &Registry{ 		opts:   op, 		client: client, 		kv:     clientv3.NewKV(client), 	} } 
  • kratos.New()

    对应用程序初始化化,应用程序参数初始化 - 默认参数或接受传入的参数。

// https://github.com/go-kratos/kratos/blob/v2.3.1/app.go#L39 func New(opts ...Option) *App {     o := options{         ctx:              context.Background(),         sigs:             []os.Signal{syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT},         registrarTimeout: 10 * time.Second,         stopTimeout:      10 * time.Second,     }     ... ...     return &App{         ctx:    ctx,         cancel: cancel,         opts:   o,     } } 
  • kratos.Name("helloworld")

    处理应用的服务参数。这个参数传入到上面 func New(opts ...Option) *App

// https://github.com/go-kratos/kratos/options.go#L41 // Name with service name. func Name(name string) Option {      return func(o *options) { o.name = name } } 
  • kratos.Registrar(r)

    选择哪个服务中心(etcd,consul,zookeeper,nacos 等等)作为 kratos 的服务中心。
    这个参数传入到上面 func New(opts ...Option) *App

// https://github.com/go-kratos/kratos/blob/v2.3.1/options.go#L81 func Registrar(r registry.Registrar) Option { 	return func(o *options) { o.registrar = r } } 
  • registrar.Register()

真正把服务注册到服务中心的是 app.Run() 这个方法里的 a.opts.registrar.Register() 方法,Register() 方法把服务实例注册到服务中心。

// https://github.com/go-kratos/kratos/app.go#L84 if err := a.opts.registrar.Register(rctx, instance); err != nil {    return err } 

参数 instance 就是方法 buildInstance() 返回的服务实例 ServiceInstance,ServiceInstance struct 包含了一个服务实例所需的字段。

// https://github.com/go-kratos/kratos/app.go#L154 func (a *App) buildInstance() (*registry.ServiceInstance, error)  // https://github.com/go-kratos/kratos/registry/registry.go#L33 type ServiceInstance struct {     ID string `json:"id"`     Name string `json:"name"`     Version string `json:"version"`     Metadata map[string]string `json:"metadata"`     Endpoints []string `json:"endpoints"` } 

三、参考

发表评论

相关文章