1. Kratos微服务框架简介
Kratos是B站开源的一款轻量级Go语言微服务框架,它提供了一套完整的微服务开发工具链和最佳实践。如果你正在寻找一个既能快速上手又能满足企业级需求的微服务框架,Kratos绝对值得一试。
我第一次接触Kratos是在开发一个电商平台的用户模块时。当时我们需要一个支持gRPC和HTTP双协议、具备服务发现、依赖注入等特性的框架,Kratos完美契合了这些需求。与其他框架相比,Kratos最大的特点是它的"工程化"理念——不是简单堆砌功能,而是通过规范化的项目结构和代码生成工具,让团队协作更加高效。
2. 环境准备与项目初始化
2.1 安装必要工具
在开始之前,你需要确保系统已经安装以下工具:
# 安装Go (版本>=1.16) brew install go # 安装protoc编译器 (用于生成gRPC代码) brew install protobuf # 安装Kratos CLI工具 go install github.com/go-kratos/kratos/cmd/kratos/v2@latest验证安装是否成功:
kratos -v # 应该输出类似: kratos version v2.7.02.2 创建用户服务项目
我们使用Kratos命令行工具初始化项目骨架:
kratos new user-service -r https://gitee.com/go-kratos/kratos-layout.git cd user-service go mod tidy这个命令会创建一个标准的Kratos项目结构,其中几个关键目录需要了解:
api/: 存放Protobuf定义的API接口文件internal/: 项目核心代码(业务逻辑、数据访问等)configs/: 配置文件cmd/: 程序入口
3. 定义用户服务API接口
3.1 编写Protobuf文件
在api/user/v1目录下创建user.proto文件:
syntax = "proto3"; package api.user.v1; option go_package = "user-service/api/user/v1;v1"; service User { rpc Register (RegisterRequest) returns (RegisterReply); rpc Login (LoginRequest) returns (LoginReply); rpc GetUser (GetUserRequest) returns (GetUserReply); } message RegisterRequest { string username = 1; string email = 2; string password = 3; } message RegisterReply { int64 id = 1; } message LoginRequest { string username = 1; string password = 2; } message LoginReply { string token = 1; } message GetUserRequest { int64 id = 1; } message GetUserReply { int64 id = 1; string username = 2; string email = 3; }3.2 生成Go代码
执行以下命令生成gRPC和HTTP代码:
# 生成gRPC代码 kratos proto client api/user/v1/user.proto # 生成服务端代码骨架 kratos proto server api/user/v1/user.proto -t internal/service # 生成所有代码 go generate ./...4. 实现数据层与业务逻辑
4.1 配置MySQL和Redis
修改configs/config.yaml文件:
server: http: addr: 0.0.0.0:8000 grpc: addr: 0.0.0.0:9000 data: database: driver: mysql source: root:password@tcp(127.0.0.1:3306)/user_service?charset=utf8mb4&parseTime=True&loc=Local redis: addr: 127.0.0.1:6379 password: "" db: 04.2 实现数据访问层
在internal/data目录下创建user.go:
package data import ( "context" "user-service/internal/biz" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" ) type User struct { gorm.Model Username string `gorm:"uniqueIndex"` Email string `gorm:"uniqueIndex"` Password string } type userRepo struct { data *Data log *log.Helper } func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo { return &userRepo{ data: data, log: log.NewHelper(logger), } } func (r *userRepo) CreateUser(ctx context.Context, u *biz.User) (*biz.User, error) { user := User{ Username: u.Username, Email: u.Email, Password: u.Password, } if err := r.data.db.WithContext(ctx).Create(&user).Error; err != nil { return nil, err } u.ID = user.ID return u, nil } func (r *userRepo) GetUser(ctx context.Context, id int64) (*biz.User, error) { var user User if err := r.data.db.WithContext(ctx).First(&user, id).Error; err != nil { return nil, err } return &biz.User{ ID: user.ID, Username: user.Username, Email: user.Email, }, nil }4.3 实现业务逻辑
在internal/biz目录下创建user.go:
package biz import ( "context" "errors" "github.com/go-kratos/kratos/v2/log" ) type User struct { ID int64 Username string Email string Password string } type UserRepo interface { CreateUser(context.Context, *User) (*User, error) GetUser(context.Context, int64) (*User, error) } type UserUsecase struct { repo UserRepo log *log.Helper } func NewUserUsecase(repo UserRepo, logger log.Logger) *UserUsecase { return &UserUsecase{repo: repo, log: log.NewHelper(logger)} } func (uc *UserUsecase) Register(ctx context.Context, u *User) (*User, error) { if u.Username == "" || u.Password == "" { return nil, errors.New("invalid username or password") } return uc.repo.CreateUser(ctx, u) } func (uc *UserUsecase) Get(ctx context.Context, id int64) (*User, error) { return uc.repo.GetUser(ctx, id) }5. 实现gRPC服务
在internal/service目录下修改user.go:
package service import ( "context" "user-service/internal/biz" v1 "user-service/api/user/v1" "github.com/go-kratos/kratos/v2/log" ) type UserService struct { v1.UnimplementedUserServer uc *biz.UserUsecase log *log.Helper } func NewUserService(uc *biz.UserUsecase, logger log.Logger) *UserService { return &UserService{ uc: uc, log: log.NewHelper(logger), } } func (s *UserService) Register(ctx context.Context, req *v1.RegisterRequest) (*v1.RegisterReply, error) { user, err := s.uc.Register(ctx, &biz.User{ Username: req.Username, Email: req.Email, Password: req.Password, }) if err != nil { return nil, err } return &v1.RegisterReply{Id: user.ID}, nil } func (s *UserService) GetUser(ctx context.Context, req *v1.GetUserRequest) (*v1.GetUserReply, error) { user, err := s.uc.Get(ctx, req.Id) if err != nil { return nil, err } return &v1.GetUserReply{ Id: user.ID, Username: user.Username, Email: user.Email, }, nil }6. 依赖注入与服务启动
6.1 配置依赖注入
Kratos使用Wire进行依赖注入。修改internal/server/wire.go:
// +build wireinject package server import ( "user-service/internal/biz" "user-service/internal/conf" "user-service/internal/data" "user-service/internal/service" "github.com/go-kratos/kratos/v2" "github.com/go-kratos/kratos/v2/log" "github.com/google/wire" ) func initApp(*conf.Server, *conf.Data, log.Logger) (*kratos.App, func(), error) { panic(wire.Build( server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp, )) }6.2 启动服务
执行以下命令启动服务:
# 生成依赖注入代码 wire ./... # 启动服务 kratos run服务启动后,你会在日志中看到类似输出:
INFO msg=config loaded: config.yaml format: yaml INFO [gRPC] server listening on: [::]:9000 INFO [HTTP] server listening on: [::]:80007. 测试与验证
7.1 使用grpcurl测试gRPC接口
# 注册用户 grpcurl -plaintext -d '{"username":"test", "email":"test@example.com", "password":"123456"}' localhost:9000 api.user.v1.User/Register # 查询用户 grpcurl -plaintext -d '{"id":1}' localhost:9000 api.user.v1.User/GetUser7.2 HTTP接口测试
Kratos会自动为gRPC服务生成HTTP接口。你可以用curl测试:
# 注册用户 curl -X POST "http://localhost:8000/api.user.v1.User/Register" \ -H "Content-Type: application/json" \ -d '{"username":"test", "email":"test@example.com", "password":"123456"}' # 查询用户 curl -X POST "http://localhost:8000/api.user.v1.User/GetUser" \ -H "Content-Type: application/json" \ -d '{"id":1}'8. 服务注册与发现(Consul集成)
8.1 安装并启动Consul
brew install consul consul agent -dev8.2 修改配置
在configs/config.yaml中添加Consul配置:
registry: consul: address: 127.0.0.1:8500 scheme: http8.3 修改服务代码
更新internal/server/server.go:
package server import ( consul "github.com/go-kratos/kratos/contrib/registry/consul/v2" consulAPI "github.com/hashicorp/consul/api" "github.com/go-kratos/kratos/v2/registry" "github.com/google/wire" ) var ProviderSet = wire.NewSet(NewGRPCServer, NewHTTPServer, NewRegistrar) func NewRegistrar(conf *conf.Registry) registry.Registrar { c := consulAPI.DefaultConfig() c.Address = conf.Consul.Address c.Scheme = conf.Consul.Scheme cli, err := consulAPI.NewClient(c) if err != nil { panic(err) } r := consul.New(cli) return r }更新cmd/user/main.go:
func newApp(logger log.Logger, gs *grpc.Server, hs *http.Server, rr registry.Registrar) *kratos.App { return kratos.New( kratos.ID(id), kratos.Name(Name), kratos.Version(Version), kratos.Metadata(map[string]string{}), kratos.Logger(logger), kratos.Server(gs, hs), kratos.Registrar(rr), ) }现在重启服务,你应该能在Consul的Web界面(http://localhost:8500)看到注册的服务。
9. 项目优化与进阶
9.1 添加JWT认证中间件
在internal/server/middleware/auth目录下创建auth.go:
package auth import ( "context" "github.com/go-kratos/kratos/v2/middleware" "github.com/go-kratos/kratos/v2/transport" "github.com/golang-jwt/jwt/v4" ) func JWTAuth(secret string) middleware.Middleware { return func(handler middleware.Handler) middleware.Handler { return func(ctx context.Context, req interface{}) (interface{}, error) { if tr, ok := transport.FromServerContext(ctx); ok { tokenString := tr.RequestHeader().Get("Authorization") if tokenString == "" { return nil, errors.New("missing authorization header") } token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) { return []byte(secret), nil }) if err != nil { return nil, err } if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { ctx = context.WithValue(ctx, "userID", claims["userID"]) } else { return nil, errors.New("invalid token") } } return handler(ctx, req) } } }9.2 添加Prometheus监控
在internal/server/metrics目录下创建metrics.go:
package metrics import ( "github.com/go-kratos/kratos/v2/metrics" "github.com/prometheus/client_golang/prometheus" ) var ( _ metrics.Counter = (*counter)(nil) _ metrics.Gauge = (*gauge)(nil) _ metrics.Histogram = (*histogram)(nil) ) type counter struct { cv *prometheus.CounterVec lvs []string } func (c *counter) Inc() { c.cv.WithLabelValues(c.lvs...).Inc() } func (c *counter) Add(delta float64) { c.cv.WithLabelValues(c.lvs...).Add(delta) } type gauge struct { gv *prometheus.GaugeVec lvs []string } func (g *gauge) Set(value float64) { g.gv.WithLabelValues(g.lvs...).Set(value) } type histogram struct { hv *prometheus.HistogramVec lvs []string } func (h *histogram) Observe(value float64) { h.hv.WithLabelValues(h.lvs...).Observe(value) }10. 部署与运维建议
10.1 Docker化部署
创建Dockerfile:
FROM golang:1.19 as builder WORKDIR /app COPY . . RUN go mod download RUN CGO_ENABLED=0 GOOS=linux go build -ldflags "-s -w" -o user-service ./cmd/user FROM alpine:latest RUN apk --no-cache add ca-certificates WORKDIR /root/ COPY --from=builder /app/user-service . COPY --from=builder /app/configs ./configs EXPOSE 8000 9000 CMD ["./user-service"]构建并运行:
docker build -t user-service . docker run -p 8000:8000 -p 9000:9000 user-service10.2 Kubernetes部署示例
创建deployment.yaml:
apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 3 selector: matchLabels: app: user-service template: metadata: labels: app: user-service spec: containers: - name: user-service image: user-service:latest ports: - containerPort: 8000 - containerPort: 9000 env: - name: ENVIRONMENT value: "production"在实际项目中,Kratos的这些特性让我们能够快速构建出高可用、易维护的微服务。特别是在团队协作中,统一的代码结构和自动生成的API文档大大减少了沟通成本。