Go-kratos学习(1)

Kratos是一个Go语言实现的微服务框架,说得更准确一点,它更类似于一个使用Go构建微服务的工具箱,开发者可以按照自己的习惯选用或定制其中的组件,来打造自己的微服务。

kratos结构
  .
├── Dockerfile  
├── LICENSE
├── Makefile  
├── README.md
├── api // 下面维护了微服务使用的proto文件以及根据它们所生成的go文件
│   └── helloworld
│       └── v1
│           ├── error_reason.pb.go
│           ├── error_reason.proto
│           ├── error_reason.swagger.json
│           ├── greeter.pb.go
│           ├── greeter.proto
│           ├── greeter.swagger.json
│           ├── greeter_grpc.pb.go
│           └── greeter_http.pb.go
├── cmd  // 整个项目启动的入口文件
│   └── server
│       ├── main.go
│       ├── wire.go  // 我们使用wire来维护依赖注入
│       └── wire_gen.go
├── configs  // 这里通常维护一些本地调试用的样例配置文件
│   └── config.yaml
├── generate.go
├── go.mod
├── go.sum
├── internal  // 该服务所有不对外暴露的代码,通常的业务逻辑都在这下面,使用internal避免错误引用
│   ├── biz   // 业务逻辑的组装层,类似 DDD 的 domain 层,data 类似 DDD 的 repo,而 repo 接口在这里定义,使用依赖倒置的原则。
│   │   ├── README.md
│   │   ├── biz.go
│   │   └── greeter.go
│   ├── conf  // 内部使用的config的结构定义,使用proto格式生成
│   │   ├── conf.pb.go
│   │   └── conf.proto
│   ├── data  // 业务数据访问,包含 cache、db 等封装,实现了 biz 的 repo 接口。我们可能会把 data 与 dao 混淆在一起,data 偏重业务的含义,它所要做的是将领域对象重新拿出来,我们去掉了 DDD 的 infra层。
│   │   ├── README.md
│   │   ├── data.go
│   │   └── greeter.go
│   ├── server  // http和grpc实例的创建和配置
│   │   ├── grpc.go
│   │   ├── http.go
│   │   └── server.go
│   └── service  // 实现了 api 定义的服务层,类似 DDD 的 application 层,处理 DTO 到 biz 领域实体的转换(DTO -> DO),同时协同各类 biz 交互,但是不应处理复杂逻辑
│       ├── README.md
│       ├── greeter.go
│       └── service.go
└── third_party  // api 依赖的第三方proto
    ├── README.md
    ├── google
    │   └── api
    │       ├── annotations.proto
    │       ├── http.proto
    │       └── httpbody.proto
    └── validate
        ├── README.md
        └── validate.proto

kratos 是与 Kratos 框架配套的脚手架工具,kratos 能够

  • 通过模板快速创建项目
  • 快速创建与生成 protoc 文件
  • 使用开发过程中常用的命令
  • 极大提高开发效率,减轻心智负担

因此我们可以通过kratos创建模板后在以此修改protoc,生成我们需要的代码。

当我们创建一个新的项目,此时可以

kratos new bubble

在这之后我们创建自己的protobuf文件,

kratos proto add api/bubble/v1/todo.proto

之后根据自己的业务修改todo.proto

syntax = "proto3";

package api.bubble.v1;
import "google/api/annotations.proto"; //添加HTTP注解需要导⼊入 google/api/annotations.proto

option go_package = "bubble/api/bubble/v1;v1";
option java_multiple_files = true;
option java_package = "api.bubble.v1";

service Todo {
    rpc CreateTodo (CreateTodoRequest) returns (CreateTodoReply){
        option (google.api.http) = {
            post: "v1/todo",
            body :"*"
        };
    };
    rpc UpdateTodo (UpdateTodoRequest) returns (UpdateTodoReply){
        option (google.api.http) = {
            put: "v1/todo/{id}",
            body :"*"
        };
    };
    rpc DeleteTodo (DeleteTodoRequest) returns (DeleteTodoReply){
        option (google.api.http) = {
            delete: "v1/todo{id}",

        };
    };
    rpc GetTodo (GetTodoRequest) returns (GetTodoReply){
        option (google.api.http) = {
            get: "v1/todo/{id}",
        };
    };
    rpc ListTodo (ListTodoRequest) returns (ListTodoReply){
        option (google.api.http) = {
            get: "v1/todos",

        };
    };
}

message todo{
    int64 id = 1;
    string title = 2;
    bool status = 3;
}

message CreateTodoRequest {
        string title = 1;
}
message CreateTodoReply {
    int64 id = 1;
    string title = 2;
    bool status = 3;
}

message UpdateTodoRequest {
    int64 id = 1;
    string title = 2;
    bool status = 3;
}
message UpdateTodoReply {
}

message DeleteTodoRequest {
    int64 id = 1;

}
message DeleteTodoReply {}

message GetTodoRequest {
    int64 id = 1;
}
message GetTodoReply {
    todo todo = 1;
}

message ListTodoRequest {}
message ListTodoReply {
    repeated todo data = 1;
}

之后生成代码

//生成客户端代码
kratos proto client api/bubble/v1/todo.proto
//生成服务端代码
kratos proto server api/bubble/v1/todo.proto -t internal/service

生成的服务端代码位于internal/service文件夹下,客户端代码位于api/bubble/v1之下。

// 实现了 api 定义的服务层,但是不应处理复杂逻辑
//可以理解为接收请求,并将调用diz包来处理请求。
//具体的请求都在diz中
type TodoService struct {
    pb.UnimplementedTodoServer
        
}

func NewTodoService() *TodoService {
    return &TodoService{}
}

func (s *TodoService) CreateTodo(ctx context.Context, req *pb.CreateTodoRequest) (*pb.CreateTodoReply, error) {
    return &pb.CreateTodoReply{}, nil
}
func (s *TodoService) UpdateTodo(ctx context.Context, req *pb.UpdateTodoRequest) (*pb.UpdateTodoReply, error) {
    return &pb.UpdateTodoReply{}, nil
}
func (s *TodoService) DeleteTodo(ctx context.Context, req *pb.DeleteTodoRequest) (*pb.DeleteTodoReply, error) {
    return &pb.DeleteTodoReply{}, nil
}
func (s *TodoService) GetTodo(ctx context.Context, req *pb.GetTodoRequest) (*pb.GetTodoReply, error) {
    return &pb.GetTodoReply{}, nil
}
func (s *TodoService) ListTodo(ctx context.Context, req *pb.ListTodoRequest) (*pb.ListTodoReply, error) {
    return &pb.ListTodoReply{}, nil
}

我们从模板生成的示例中可以看到

// GreeterService is a greeter service.
type GreeterService struct {
    v1.UnimplementedGreeterServer

    uc *biz.GreeterUsecase
}

// NewGreeterService new a greeter service.
func NewGreeterService(uc *biz.GreeterUsecase) *GreeterService {
    return &GreeterService{uc: uc}
}

// SayHello implements helloworld.GreeterServer.
func (s *GreeterService) SayHello(ctx context.Context, in *v1.HelloRequest) (*v1.HelloReply, error) {
    g, err := s.uc.CreateGreeter(ctx, &biz.Greeter{Hello: in.Name})
    if err != nil {
        return nil, err
    }
    return &v1.HelloReply{Message: "Hello " + g.Hello}, nil
}
// CreateGreeter creates a Greeter, and returns the new Greeter.
func (uc *GreeterUsecase) CreateGreeter(ctx context.Context, g *Greeter) (*Greeter, error) {
    uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Hello)
    return uc.repo.Save(ctx, g)
}


于是我们类似模板,对项目进行改造。从service->diz->data

//service/todo.go
//service需要调用diz,因此在TodoService 嵌入TodoUsecase
type TodoService struct {
    pb.UnimplementedTodoServer
    uc *biz.TodoUsecase
}

func NewTodoService(uc *biz.TodoUsecase) *TodoService {
    return &TodoService{uc:uc}
}

func (s *TodoService) CreateTodo(ctx context.Context, req *pb.CreateTodoRequest) (*pb.CreateTodoReply, error) {
    //请求验证
    if len(req.Title) == 0 {
        return &pb.CreateTodoReply{}, errors.New("无效title")
    }
    data, err := s.uc.CreateTodo(ctx, &biz.Todo{Title: req.Title})
    if err != nil {
        return nil, err
    }

    return &pb.CreateTodoReply{Id: data.ID, Title: data.Title, Status: data.Status}, nil
}
func (s *TodoService) UpdateTodo(ctx context.Context, req *pb.UpdateTodoRequest) (*pb.UpdateTodoReply, error) {
    return &pb.UpdateTodoReply{}, nil
}
func (s *TodoService) DeleteTodo(ctx context.Context, req *pb.DeleteTodoRequest) (*pb.DeleteTodoReply, error) {
    return &pb.DeleteTodoReply{}, nil
}
func (s *TodoService) GetTodo(ctx context.Context, req *pb.GetTodoRequest) (*pb.GetTodoReply, error) {
    return &pb.GetTodoReply{}, nil
}
func (s *TodoService) ListTodo(ctx context.Context, req *pb.ListTodoRequest) (*pb.ListTodoReply, error) {
    return &pb.ListTodoReply{}, nil
}

//diz
// Greeter is a Greeter model.
type Todo struct {
    ID     int64
    Title  string
    Status bool
}

// TodoRepo is a Todo repo.
type TodoRepo interface {
    Save(context.Context, *Todo) (*Todo, error)
    Update(context.Context, *Todo) error
    FindByID(context.Context, int64) (*Todo, error)
    Delete(context.Context, int64) error
    ListAll(context.Context) ([]*Todo, error)
}

// TodoUsecase is a Todo usecase.
type TodoUsecase struct {
    repo TodoRepo
    log  *log.Helper
}

// NewGreeterUsecase new a Greeter usecase.
func NewTodoUsecase(repo TodoRepo, logger log.Logger) *TodoUsecase {
    return &TodoUsecase{repo: repo, log: log.NewHelper(logger)}
}

// CreateGreeter creates a Greeter, and returns the new Greeter.
func (uc *TodoUsecase) CreateTodo(ctx context.Context, g *Todo) (*Todo, error) {
    uc.log.WithContext(ctx).Infof("CreateGreeter: %v", g.Title)
    return uc.repo.Save(ctx, g)
}

//data

type todoRepo struct {
    data *Data
    log  *log.Helper
}

// NewGreeterRepo .
func NewTodoRepo(data *Data, logger log.Logger) biz.TodoRepo {
    return &todoRepo{
        data: data,
        log:  log.NewHelper(logger),
    }
}

func (r *todoRepo) Save(ctx context.Context, t *biz.Todo) (*biz.Todo, error) {
    // 实现数据库的操作
    fmt.Printf("save: t:%#v\n", t)
    return t, nil
}

func (r *todoRepo) Update(ctx context.Context, t *biz.Todo) error {
    return nil
}

func (r *todoRepo) FindByID(context.Context, int64) (*biz.Todo, error) {
    return nil, nil
}

func (r *todoRepo) Delete(context.Context, int64) error {
    return nil
}

func (r *todoRepo) ListAll(context.Context) ([]*biz.Todo, error) {
    return nil, nil
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容