1、简介

Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具

2、示例

2.1、准备条件

mac 为例
go : 安装go1.23.6
protoc : 直接 brew install protobuf 

2.2、官网示例

https://go-kratos.dev/docs/getting-started/start

2.2.1、安装kratos

go install github.com/go-kratos/kratos/cmd/kratos/v2@latest

2.2.2、下载helloworld示例

kratos new helloworld

2.2.3、安装依赖

make init

2.2.4、生成代码

make all

2.2.5、IDEA运行

Clipboard_Screenshot_1745832590.png

-conf /Users/xxx/IdeaProjects/helloworld/configs/config.yaml

2.2.6、命令行运行

kratos run

2.3、访问

curl 'http://127.0.0.1:8000/helloworld/kratos'
{"message":"Hello kratos"}

3、开发一个aritcle示例

3.1、Protobuf 定义(api/hellworld/v1/demo.proto)

syntax = "proto3";

package helloworld.v1;

import "google/api/annotations.proto";

option go_package = "helloworld/api/helloworld/v1;v1";
option java_multiple_files = true;
option java_package = "dev.kratos.api.helloworld.v1";
option java_outer_classname = "HelloworldProtoV1";

service ArticleService {
   rpc GetArticle(GetArticleRequest) returns (GetArticleReply) {
       option (google.api.http) = {
           get: "/v1/article/{id}"
       };
   }
}

message GetArticleRequest {
   int64 id = 1;
}

message GetArticleReply {
   int64 id = 1;
   string title = 2;
   string content = 3;
}

然后执行

# 生成以下文件(文件内容自动生成):
# - demo.pb.go        : 消息结构体
# - demo_http.pb.go   : HTTP 路由和 Handler 接口
# - demo_grpc.pb.go   : gRPC 服务接口
make api

3.2、数据访问层对象定义

internal/data/article.go

package data

import (
    "context"
    "github.com/go-kratos/kratos/v2/log"
    "helloworld/internal/biz"
)

type ArticleDO struct {
    ID      int64
    Title   string
    Content string
}

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

// 这个其实就是对数据库的操作,实现了ArticleRepo接口的实现
func NewArticleRepo(data *Data, logger log.Logger) biz.ArticleRepo {
    return &ArticleRepo{
        data: data,
        log:  log.NewHelper(logger),
    }
}

func (r *ArticleRepo) GetArticle(ctx context.Context, id int64) (*biz.Article, error) {
    var article ArticleDO
    if err := r.data.db.WithContext(ctx).First(&article, id).Error; err != nil {
        return nil, err
    }

    // 将数据库对象转换为业务对象
    return &biz.Article{
        ID:      article.ID,
        Title:   article.Title,
        Content: article.Content,
    }, nil
}

internal/data/data.go

package data

import (
    "gorm.io/gorm"
    "helloworld/internal/conf"

    "github.com/go-kratos/kratos/v2/log"
    "github.com/google/wire"
    "gorm.io/driver/mysql"
)

// ProviderSet is data providers. ProviderSet 中加入NewArticleRepo
var ProviderSet = wire.NewSet(NewData, NewGreeterRepo, NewArticleRepo)

// Data .
type Data struct {
    db *gorm.DB
}

// NewData .
func NewData(c *conf.Data, logger log.Logger) (*Data, func(), error) {

    db, err := gorm.Open(mysql.Open(c.Database.Source), &gorm.Config{})
    if err != nil {
        return nil, nil, err
    }

    // 自动迁移数据库表结构
    db.AutoMigrate(&ArticleDO{})

    return &Data{
            db: db,
        }, func() {
            sqlDB, _ := db.DB()
            sqlDB.Close()
        }, nil
}

3.3、业务逻辑层

internal/biz/article.go

package biz

import (
    "context"
    "github.com/go-kratos/kratos/v2/log"
)

type Article struct {
    ID      int64
    Title   string
    Content string
}

type ArticleRepo interface {
    GetArticle(ctx context.Context, id int64) (*Article, error)
}

type ArticleUsecase struct {
    repo ArticleRepo
    log  *log.Helper
}

func NewArticleUsecase(repo ArticleRepo, logger log.Logger) *ArticleUsecase {
    return &ArticleUsecase{repo: repo, log: log.NewHelper(logger)}
}

func (uc *ArticleUsecase) GetArticle(ctx context.Context, id int64) (*Article, error) {
    return uc.repo.GetArticle(ctx, id)
}

修改 internal/biz/biz.go,增加Provider

var ProviderSet = wire.NewSet(NewGreeterUsecase, NewArticleUsecase)

3.3、服务层

internal/service/article.go

package service

import (
    "context"
    pb "helloworld/api/helloworld/v1"
    "helloworld/internal/biz"
)

type ArticleService struct {
    pb.UnimplementedArticleServiceServer
    uc *biz.ArticleUsecase
}

func NewArticleService(uc *biz.ArticleUsecase) *ArticleService {
    return &ArticleService{uc: uc}
}

func (s *ArticleService) GetArticle(ctx context.Context, req *pb.GetArticleRequest) (*pb.GetArticleReply, error) {
    article, err := s.uc.GetArticle(ctx, req.Id)
    if err != nil {
        return nil, err
    }
    return &pb.GetArticleReply{
        Id:      article.ID,
        Title:   article.Title,
        Content: article.Content,
    }, nil
}

修改 internal/service/service.go,增加Provider

var ProviderSet = wire.NewSet(NewGreeterService, NewArticleService)

3.4、修改server层

internal/server/http.go

package server

import (
    "github.com/go-kratos/kratos/v2/log"
    v1 "helloworld/api/helloworld/v1"
    "helloworld/internal/conf"
    "helloworld/internal/service"

    "github.com/go-kratos/kratos/v2/transport/http"
)

// NewHTTPServer new an HTTP server.
func NewHTTPServer(
    c *conf.Server,
    greeterSvc *service.GreeterService,
    articleSvc *service.ArticleService, // 新增参数
    logger log.Logger,
) *http.Server {
    svr := http.NewServer(http.Address(c.Http.Addr))

    // 注册多个服务
    v1.RegisterGreeterHTTPServer(svr, greeterSvc)
    v1.RegisterArticleServiceHTTPServer(svr, articleSvc) // 新增注册

    return svr
}

internal/server/grpc.go

package server

import (
    v1 "helloworld/api/helloworld/v1"
    "helloworld/internal/conf"
    "helloworld/internal/service"

    "github.com/go-kratos/kratos/v2/log"
    "github.com/go-kratos/kratos/v2/middleware/recovery"
    "github.com/go-kratos/kratos/v2/transport/grpc"
)

// NewGRPCServer new a gRPC server.
func NewGRPCServer(c *conf.Server,
    greeterSvc *service.GreeterService,
    articleSvc *service.ArticleService, // 新增参数
    logger log.Logger,
) *grpc.Server {
    var opts = []grpc.ServerOption{
        grpc.Middleware(
            recovery.Recovery(),
        ),
    }
    if c.Grpc.Network != "" {
        opts = append(opts, grpc.Network(c.Grpc.Network))
    }
    if c.Grpc.Addr != "" {
        opts = append(opts, grpc.Address(c.Grpc.Addr))
    }
    if c.Grpc.Timeout != nil {
        opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))
    }
    srv := grpc.NewServer(opts...)
    v1.RegisterGreeterServer(srv, greeterSvc)
    v1.RegisterArticleServiceServer(srv, articleSvc)
    return srv
}

3.5、重新生成代码

cd cmd/helloworld/ && wire gen

查看cmd/helloworld/wire_gen.go是否生成了
Clipboard_Screenshot_1745843931.png

3.6、配置文件配置

configs/config.yaml

server:
  http:
    addr: 0.0.0.0:8000
    timeout: 1s
  grpc:
    addr: 0.0.0.0:9000
    timeout: 1s
data:
  database:
    driver: mysql
    source: root:root@123@tcp(127.0.0.1:3306)/journey?parseTime=True&loc=Local
  redis:
    addr: 127.0.0.1:6379
    read_timeout: 0.2s
    write_timeout: 0.2s

journey
37 声望24 粉丝