← 返回文章列表

Kratos 微服务框架

学习 Kratos 的项目结构、服务定义与微服务开发方式。

go

Kratos: An elegant toolkit for Go microservices.

Kratos是一个基于golang实现的面向微服务的治理框架,它提供了便捷的功能,帮助你从零开始快速构建一个可靠的应用。

他是 B 站开源的一个 golang 微服务框架,也是主流的微服务框架之一。

名字来源于:《战神》游戏以希腊神话为背景,讲述奎托斯(Kratos)由凡人成为战神并展开弑神屠杀的冒险经历。

简介

项目地址

文档地址

安装和简单使用

安装

安装前置

安装指令

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

安装之后查看相关操作

(base) ➜  GoFramwork git:(main) kratos -h     
Kratos: An elegant toolkit for Go microservices.

Usage:
  kratos [command]

Available Commands:
  changelog   生成 Kratos 变更 log
  completion  为指定的Shell生成自动补全脚本
  help        帮助指令
  new         生成一个服务模板
  proto       生成 proto 文件
  run         运行项目
  upgrade     升级 Kratos 工具

Flags:
  -h, --help      help for kratos
  -v, --version   version for kratos

Use "kratos [command] --help" for more information about a command.

Demo

根据官方的提示创建一个Kratos HelloWord

首先使用 new 创建项目

kratos new helloworld

new 完成后我们发现可以选择不同的模板,这里我们选择第一个service

go

等待生成完成,这是生成的项目结构

.
├── api  # API 定义层 (Protocol Buffers)
│   └── helloworld
│       └── v1
│           ├── error_reason.pb.go   # [生成] 错误原因的 Go 代码
│           ├── error_reason.proto   # 定义业务错误原因 (Enum)
│           ├── greeter_grpc.pb.go   # [生成] gRPC 服务端和客户端代码
│           ├── greeter_http.pb.go   # [生成] Kratos HTTP 服务代码 (路由、编解码)
│           ├── greeter.pb.go        # [生成] 请求/响应消息的 Go 结构体
│           └── greeter.proto        # 定义服务接口 (RPC) 和消息结构 (Message)
├── cmd  # 程序入口 (Entry Point)
│   └── helloworld
│       ├── main.go      # 主程序入口:负责初始化资源、依赖注入、启动服务、监听退出信号
│       ├── wire_gen.go  # [生成] Wire 依赖注入生成的具体代码 (不要手动修改)
│       └── wire.go      # Wire 依赖注入的声明文件,定义 ProviderSet
├── configs  # 配置文件
│   └── config.yaml      # 具体的配置内容 (如数据库地址、端口、日志级别等)
├── Dockerfile           # 容器化构建脚本
├── go.mod               # Go 模块依赖定义
├── go.sum               # Go 模块依赖校验和
├── internal  # 内部业务逻辑 (Go 规范:外部项目无法 import 此目录内容)
│   ├── biz   # [业务逻辑层 / 领域层] (Domain Layer)
│   │   ├── biz.go       # 定义 biz 层的 ProviderSet
│   │   ├── greeter.go   # 定义领域对象 (Entity) 和 业务接口 (Repo Interface),不依赖具体实现
│   │   └── README.md
│   ├── conf  # [配置定义层]
│   │   ├── conf.pb.go   # [生成] 配置文件的 Go 结构体
│   │   └── conf.proto   # 定义配置文件的结构 (将 YAML 映射为强类型 struct)
│   ├── data  # [数据访问层 / 基础设施层] (Infrastructure Layer)
│   │   ├── data.go      # 初始化数据库/Redis 连接,定义 data 层的 ProviderSet
│   │   ├── greeter.go   # 实现 biz 层定义的 Repo 接口 (数据库 CRUD、缓存、RPC 调用)
│   │   └── README.md
│   ├── server # [服务实例层]
│   │   ├── grpc.go      # 初始化 gRPC Server
│   │   ├── http.go      # 初始化 HTTP Server
│   │   └── server.go    # 定义 server 层的 ProviderSet
│   └── service # [应用服务层] (Application Layer)
│       ├── greeter.go   # 实现 api 定义的 protobuf 接口,负责 DTO -> DO 转换,编排 biz 逻辑
│       ├── README.md
│       └── service.go   # 定义 service 层的 ProviderSet
├── LICENSE              # 许可证文件
├── Makefile             # 项目管理脚本 (生成 API、编译、运行、测试等命令)
├── openapi.yaml         # [生成] OpenAPI/Swagger 接口文档
├── README.md            # 项目说明文档
└── third_party  # 第三方依赖的 Proto 文件 (用于生成代码)
    ├── errors           # Kratos 错误模型定义
    │   └── errors.proto
    ├── google           # Google 官方 Proto (API 注解、HTTP 映射、通用类型等)
    │   ├── api
    │   │   ├── ...
    │   └── protobuf
    │       ├── ...
    ├── openapi          # OpenAPI 生成相关的 Proto 定义
    │   └── v3
    │       ├── ...
    ├── README.md
    └── validate         # 参数校验规则 Proto 定义
        ├── README.md
        └── validate.proto

之后我们进入目录,运行依赖下载指令

cd helloworld
go mod download

我们也可以自己编写proto源码、wire等等,通过 Kratos 生成相应的代码

go generate ./...

然后是运行项目

kratos run

go

go

依赖注入 wire

参见文章:控制反转和依赖注入

Kratos的配置

配置源

  • 文件json yaml

  • 环境变量 env

  • 配置中心 apollo consul

读取配置

  • c.Load 和 c.Scan, 取值c.Value

  • c.Watch 配置热加载

  • 合并

  • Resolver 自定义

  • Codec自定义

配置结构体

internal/conf/conf.pb.go 文件中的 Bootstrap

程序添加配置的核心流程

  • 1. 修改 internal/conf/conf.proto 文件, 用pb定义配置

  • 2. 生成配置对应的Go代码项目目录下执行 make config如果没有装make命令,那么就执行

    protoc --proto_path=./internal \--proto_path=./third_party \--go_out=paths=source_relative:./internal \internal/conf/conf.proto
  • 3. 修改configs配置文件

注意事项

  • pb文件要语法写对

  • 改完pb文件一定记得要生成Go代码

  • 配置文件要跟Go代码里的结构体对应上

  • 配置文件的语法要写对

自定义HTTP返回

创建响应编码器并替换默认编码器,具体demo可见项目:httpencoder和http

package server

import (
        "net/http"

        kratoshttp "github.com/go-kratos/kratos/v2/transport/http"
        kratosstatus "github.com/go-kratos/kratos/v2/transport/http/status"
        "google.golang.org/grpc/status"
)

// 定义 HTTP 响应编码器

// 覆盖默认的  DefaultResponseEncoder, 通过  http.ResponseEncoder() 配置,注入到  http.Server() 中
type httpResponse struct {
        Code int32  `json:"code"`
        Msg  string `json:"msg"`
        Data any    `json:"data"`
}

func ResponseEncoder(w http.ResponseWriter, r *http.Request, v any) error {
        if v == nil {
                return nil
        }
        // 判断是否是重定向
        if rd, ok := v.(kratoshttp.Redirector); ok {
                url, code := rd.Redirect()
                http.Redirect(w, r, url, code)
                return nil
        }
        // 构造自定义的响应结构体
        resp := &httpResponse{
                Code: http.StatusOK,
                Msg:  "success",
                Data: v,
        }
        // 响应数据
        codec, _ := kratoshttp.CodecForRequest(r, "Accept")

        data, err := codec.Marshal(resp) // json.Marshal
        if err != nil {
                return err
        }
        // 设置响应头 Content-Type:application/json
        w.Header().Set("Content-Type", "application/"+codec.Name())
        _, err = w.Write(data)
        return err
}

// 自定义的错误响应编码器
func errorEncoder(w http.ResponseWriter, r *http.Request, err error) {
        if err == nil {
                return
        }
        resp := new(httpResponse) // 能从err里面解析出错误码的
        if gs, ok := status.FromError(err); ok {
                resp = &httpResponse{
                        Code: int32(kratosstatus.FromGRPCCode(gs.Code())),
                        Msg:  gs.Message(),
                        Data: nil,
                }
        } else {
                resp = &httpResponse{
                        Code: http.StatusInternalServerError, // 500
                        Msg:  "内部错误",
                }
        }

        codec, _ := kratoshttp.CodecForRequest(r, "Accept")
        body, err := codec.Marshal(resp)
        if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                return
        }
        w.Header().Set("Content-Type", "application/"+codec.Name())
        w.WriteHeader(int(resp.Code))
        _, _ = w.Write(body)
}
// 替换默认的 HTTP 响应编码器
opts = append(opts, http.ResponseEncoder(ResponseEncoder))
// 替换默认的错误响应编码器
opts = append(opts, http.ErrorEncoder(errorEncoder))

日志

为了方便使用,Kratos定义了两个层面的抽象,Logger统一了日志的接入方式,Helper接口统一的日志库的调用方式。

Kratos的日志库主要有如下特性:

  • Logger用于对接各种日志库或日志平台,可以用现成的或者自己实现

  • Helper是在您的项目代码中实际需要调用的,用于在业务代码里打日志

  • Filter用于对输出日志进行过滤或魔改(通常用于日志脱敏)

  • Valuer用于绑定一些全局的固定值或动态值(比如时间戳、traceID或者实例id之类的东西)到输出日志中

中间件

go

…

参考资料

Kratos

Kratos 文档

关于这篇文章的日期

旧日期 2025-01-16 与文章发布记录不符,现按旧站可确认的首次发布日 2026-02-10 整理。实际创作日期尚无法确认。