返回

深度剖析 Go-zero 开箱即用的微服务框架(进阶篇)

前端

在上一篇文章《Go-zero:开箱即用的微服务框架》中,我们简单介绍了 Go-zero 的基本概念和特性。在这篇文章中,我们将深入探讨如何使用 Go-zero 来开发一个博客的后台系统。我们将重点介绍如何使用 Go-zero 来实现 CRUD 操作、设计 API、使用 gRPC 进行服务间通信、连接 MongoDB 和 MySQL 等内容。

项目结构

我们首先来看一下项目的结构:

├── api
│   ├── blog.pb.go
│   └── blog.pb.gw.go
├── cmd
│   └── blogd
│       ├── main.go
│       └── wire.go
├── internal
│   ├── biz
│   │   ├── blog.go
│   │   └── user.go
│   ├── conf
│   │   ├── config.go
│   │   └── config.yaml
│   ├── handler
│   │   ├── blog.go
│   │   └── user.go
│   ├── model
│   │   ├── blog.go
│   │   └── user.go
│   ├── service
│   │   ├── blog.go
│   │   └── user.go
│   ├── storage
│   │   ├── blog.go
│   │   └── user.go
│   ├── transport
│   │   ├── blog.go
│   │   └── user.go
│   └── wire.go
├── scripts
│   └── gen.sh
└── test

初始化项目

我们可以使用 Go-zero 自带的脚手架工具来初始化一个项目:

go zero init blogd

配置文件

接下来,我们需要在项目根目录下创建配置文件 conf/config.yaml

Mode: debug
Service:
  Name: blogd
  Host: 0.0.0.0
  Port: 8848
  UseGrpc: true
DB:
  DataSource: "mongodb://localhost:27017/blog"
  Table: user

业务逻辑

我们首先定义一个用户模型:

type User struct {
	Id       int64  `db:"id"`
	Username string `db:"username"`
	Password string `db:"password"`
}

然后,我们定义一个用户服务接口:

type UserService interface {
	CreateUser(ctx context.Context, in *userpb.CreateUserReq) (*userpb.CreateUserResp, error)
	UpdateUser(ctx context.Context, in *userpb.UpdateUserReq) (*userpb.UpdateUserResp, error)
	GetUser(ctx context.Context, in *userpb.GetUserReq) (*userpb.GetUserResp, error)
	DeleteUser(ctx context.Context, in *userpb.DeleteUserReq) (*userpb.DeleteUserResp, error)
	ListUsers(ctx context.Context, in *userpb.ListUsersReq) (*userpb.ListUsersResp, error)
}

最后,我们实现用户服务接口:

type userServiceImpl struct {
	dao userdao.UserDao
}

func NewUserService(dao userdao.UserDao) UserService {
	return &userServiceImpl{
		dao: dao,
	}
}

func (s *userServiceImpl) CreateUser(ctx context.Context, in *userpb.CreateUserReq) (*userpb.CreateUserResp, error) {
	user := &User{
		Username: in.Username,
		Password: in.Password,
	}
	id, err := s.dao.Insert(ctx, user)
	if err != nil {
		return nil, err
	}
	return &userpb.CreateUserResp{
		Id: id,
	}, nil
}

func (s *userServiceImpl) UpdateUser(ctx context.Context, in *userpb.UpdateUserReq) (*userpb.UpdateUserResp, error) {
	user := &User{
		Id:       in.Id,
		Username: in.Username,
		Password: in.Password,
	}
	err := s.dao.Update(ctx, user)
	if err != nil {
		return nil, err
	}
	return &userpb.UpdateUserResp{}, nil
}

func (s *userServiceImpl) GetUser(ctx context.Context, in *userpb.GetUserReq) (*userpb.GetUserResp, error) {
	user, err := s.dao.FindOne(ctx, in.Id)
	if err != nil {
		return nil, err
	}
	return &userpb.GetUserResp{
		Id:       user.Id,
		Username: user.Username,
		Password: user.Password,
	}, nil
}

func (s *userServiceImpl) DeleteUser(ctx context.Context, in *userpb.DeleteUserReq) (*userpb.DeleteUserResp, error) {
	err := s.dao.Delete(ctx, in.Id)
	if err != nil {
		return nil, err
	}
	return &userpb.DeleteUserResp{}, nil
}

func (s *userServiceImpl) ListUsers(ctx context.Context, in *userpb.ListUsersReq) (*userpb.ListUsersResp, error) {
	users, err := s.dao.FindAll(ctx)
	if err != nil {
		return nil, err
	}
	var respUsers []*userpb.User
	for _, user := range users {
		respUsers = append(respUsers, &userpb.User{
			Id:       user.Id,
			Username: user.Username,
			Password: user.Password,
		})
	}
	return &userpb.ListUsersResp{
		Users: respUsers,
	}, nil
}

数据层

我们使用 MongoDB 作为数据存储,因此我们需要定义一个用户数据访问对象:

type UserDao interface {
	Insert(ctx context.Context, user *User) (int64, error)
	Update(ctx context.Context, user *User) error
	FindOne(ctx context.Context, id int64) (*User, error)
	Delete(ctx context.Context, id int64) error
	FindAll(ctx context.Context) ([]*User, error)
}

然后,我们实现用户数据访问对象:

type userDaoImpl struct {
	client *mongo.Client
}

func NewUserDao(client *mongo.Client) UserDao {
	return &userDaoImpl{
		client: client,
	}
}

func (d *userDaoImpl) Insert(ctx context.Context, user *User) (int64, error) {
	result, err := d.client.Database("blog").Collection("user").InsertOne(ctx, user)
	if err != nil {
		return 0, err
	}
	return result.InsertedID.(int64), nil
}

func (d *userDaoImpl) Update(ctx context.Context, user *User) error {
	_, err := d.client.Database("blog").Collection("user").UpdateOne(ctx, bson.M{"id": user.Id}, bson.M{"$set": user})
	return err
}

func (d *userDaoImpl) FindOne(ctx context.Context, id int64) (*User, error) {
	var user User
	err := d.client.Database("blog").Collection("user").FindOne(ctx, bson.M{"id": id}).Decode(&user)
	if err != nil {
		return nil, err
	}
	return &user, nil
}

func (d *userDaoImpl) Delete(ctx context.Context, id int64) error {
	_, err := d.client.Database("blog").Collection("user").DeleteOne(ctx, bson.M{"id": id})
	return err
}

func (d *userDaoImpl) FindAll(ctx context.Context) ([]*User, error) {
	var users []*User
	cursor, err := d.client.Database("blog").Collection("user").Find(ctx, bson.M{})
	if err != nil {
		return nil, err
	}
	for {
		var user User
		if err = cursor.Next(ctx); err == mongo.Err