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.0

2.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: 0

4.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: [::]:8000

7. 测试与验证

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/GetUser

7.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 -dev

8.2 修改配置

configs/config.yaml 中添加Consul配置:

registry:
  consul:
    address: 127.0.0.1:8500
    scheme: http

8.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-service

10.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文档大大减少了沟通成本。

更多推荐