一、介绍
Go 语言微服务框架 Kratos 服务注册与发现,支持多种注册中心,本文我们以 Consul 为例,介绍 Kratos 项目怎么实现服务注册与发现。
以 blog 项目作为 RPC 服务端,我们再创建一个 blog-client 项目作为 RPC 客户端。
二、服务注册
我们通过改造 blog 项目的代码,将 blog 服务作为 RPC 服务端,注册到 Consul 中。
1. 创建 Consul 注册中心
在 blog/internal 目录中,创建 registry 目录,并创建 consul.go 和 registry.go 文件。
编写 blog/internal/registry/consul.go 文件。
func NewConsulRegistry(c *conf.Registry) *consul.Registry {
client, err := api.NewClient(&api.Config{
Address: c.Consul.Addr,
Scheme: c.Consul.Schema,
})
if err != nil {
panic(err)
}
return consul.New(client)
}
编写 blog/internal/registry/registry.go 文件。
var ProviderSet = wire.NewSet(NewConsulRegistry)
编写 blog/cmd/blog/wire.go 文件。
func wireApp(*conf.Server, *conf.Data, *conf.Registry, log.Logger) (*kratos.App, func(), error) {
panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, registry.ProviderSet, newApp))
}
2. wire 生成文件
cd /root/go/src/blog/cmd/blog
wire
编写 blog/cmd/blog/main.go 文件。
func newApp(conf *conf.Server, logger log.Logger, gs *grpc.Server, hs *http.Server, registry *consul.Registry) *kratos.App {
return kratos.New(
kratos.ID(id),
kratos.Name(conf.Name),
kratos.Version(conf.Version),
kratos.Metadata(map[string]string{}),
kratos.Logger(logger),
kratos.Server(
gs,
hs,
),
kratos.Registrar(registry),
)
}
运行项目
cd /root/go/src/blog/cmd/blog
kratos run
3. 访问 consul UI
在浏览器中访问 http://IP:8500/ui/dc1/services,检查 blog 服务的注册状态。
三、服务发现
使用 kratos 创建一个客户端项目 blog-client。
1. 创建 Consul 注册中心
在 blog-client/internal 目录中,创建 registry 目录,并创建 consul.go 和 registry.go 文件。
编写 blog-client/internal/registry/consul.go 文件。
func NewConsulRegistry(c *conf.Registry) *consul.Registry {
client, err := api.NewClient(&api.Config{
Address: c.Consul.Addr,
Scheme: c.Consul.Schema,
})
if err != nil {
panic(err)
}
return consul.New(client)
}
编写 blog-client/internal/registry/registry.go 文件。
var ProviderSet = wire.NewSet(NewConsulRegistry)
2. 创建 RPC 客户端
在 blog-client/internal 目录中,创建 client 目录,并创建 blog_client.go 和 client.go 文件。
编写 blog-client/internal/client/blog_client.go 文件。
func NewBlogClient(registry *consul.Registry, logger log.Logger) (v1.UserClient, error) {
conn, err := grpc.DialInsecure(
context.Background(),
grpc.WithEndpoint("discovery:///blog"),
grpc.WithDiscovery(registry),
grpc.WithMiddleware(
recovery.Recovery(),
),
grpc.WithNodeFilter(
filter.Version("1.0.0")),
)
if err != nil {
log.NewHelper(logger).WithContext(context.Background()).Errorw("err", err)
return nil, err
}
return v1.NewUserClient(conn), nil
}
编写 blog-client/internal/client/client.go 文件。
var ProviderSet = wire.NewSet(NewBlogClient)
3. 拷贝 pb 文件
拷贝 /root/go/src/blog/api/user 目录,粘帖到 /root/go/src/blog-client/api 目录。
4. 创建 domain 和 usecase 层
创建 blog-client/internal/biz/user.go 文件。
type User struct {
Id int64 `xorm:"autoincr"`
Name string
Email string
Password string
Created int64 `xorm:"created"`
Updated int64 `xorm:"updated"`
}
type UserUsecase struct {
rpcClient v1.UserClient
}
func NewUserUsecase(client v1.UserClient) *UserUsecase {
return &UserUsecase{
rpcClient: client,
}
}
func (u *UserUsecase) GetUser(ctx context.Context, user *User) (reply *v1.GetUserReply, err error) {
req := &v1.GetUserRequest{
Id: user.Id,
}
reply, err = u.rpcClient.GetUser(ctx, req)
if err != nil {
return nil, err
}
return
}
编写 blog-client/internal/biz/biz.go 文件。
var ProviderSet = wire.NewSet(NewUserUsecase)
5. 生成 server 源码
cd /root/go/src/blog-client
kratos proto server api/user/v1/user.proto
编写生成文件 internal/service/user.go。
func (s *UserService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserReply, error) {
user := &biz.User{
Id: req.Id,
}
reply, err := s.userUcase.GetUser(ctx, user)
if err != nil {
return nil, err
}
return &v1.GetUserReply{
Name: reply.Name,
}, nil
}
编写 blog-client/internal/service/service.go 文件。
var ProviderSet = wire.NewSet(NewUserService)
6. 添加 wire 提供者
编写 blog-client/cmd/blog-client/wire.go 文件。
func wireApp(*conf.Server, *conf.Data, *conf.Registry, log.Logger) (*kratos.App, func(), error) {
panic(wire.Build(server.ProviderSet, biz.ProviderSet, registry.ProviderSet, service.ProviderSet, client.ProviderSet, newApp))
}
7. wire 生成文件
cd /root/go/src/blog-client/cmd/blog-client
wire
8. 运行项目
cd /root/go/src/blog-client/cmd/blog
kratos run
9. curl 请求示例
curl -H "Content-Type: application/json" -X GET http://192.168.110.209:8001/user/get/1
{"name":"frank"}
四、总结
本文我们通过示例代码,介绍 Kratos 项目怎么实现服务注册与发现。
需要注意的是,RPC 服务端和 RPC 客户端的端口不能相同。