Go 框架三件套详解 | 青训营笔记

228 阅读4分钟

Go 框架三件套详解| 青训营笔记

这是我参与「第五届青训营 」笔记创作活动的第5天

今天主要对Go 框架三件套详解(Web/RPC/ORM)进行学习。

一、本堂课重点内容:

本次课程讲了以下几个方面:

1.ORM框架-Gorm

2.RPC 框架 Kitex

3.HTTP 框架 Hertz

二、详细知识点介绍:

1.ORM框架Gorm

Gorm是使用Go语言编写的ORM框架,它有着对开发者友好、支持主流数据库、文档齐全等优势。以下是使用Gorm连接MySQL数据库的Go语言程序代码:

package main
 
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
 
func main()  {
    dsn := "root:root@tcp(localhost:3306)/dbtest?charset=utf8mb4&parseTime=True&loc=Local"
	//连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	if err != nil {
		panic("连接数据库失败, error=" + err.Error())
	}
	//延时关闭数据库连接
	defer db.Close()
}

在Gorm框架中,我们需要定义模型,就拿用户表(id,name,age)举例:

//我们需要定义一个结构体User作为模型
type User struct{
  ID 	int		`gorm:"column: id"`
  Name 	string	`gorm:"column: name"`
  Age 	int		`gorm:"column: age"`
}

以下是对MySQL数据库CURD简单操作代码:

package main
 
import (
  "gorm.io/driver/mysql"
  "gorm.io/gorm"
)
//我们需要定义一个结构体User作为模型
type User struct{
  ID 	int		`gorm:"column: id"`
  Name 	string	`gorm:"column: name"`
  Age 	int		`gorm:"column: age"`
}
func main()  {
  dsn := "root:root@tcp(localhost:3306)/dbtest?charset=utf8mb4&parseTime=True&loc=Local"
  //连接MYSQL, 获得DB类型实例,用于后面的数据库读写操作。
  db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
  if err != nil {
    panic("连接数据库失败, error=" + err.Error())
  }
  //延时关闭数据库连接
  defer db.Close()
  //1.增加数据
  u1 := &User{ID:1001,Name:"Jack",Age:18}
  result := db.Create(u1) // 通过数据的指针来创建
  /*其中result中包含
    result.Error        // 返回 error
    result.RowsAffected // 返回插入记录的条数
  */
  
  //2.查询数据
  u2:=User{}
  db.First(&u2)//查询数据库中的第一条数据放入u2中
  
  var user User
  var users []User
  // 获取全部记录
  result := db.Find(&users)/查询数据库中的第一条数据放入users中
  
  //3.更新数据
  db.Model(&User{ID:1001}).Update("Lucy", 20)
  
  //4.删除数据
  db.Delete(&User{ID:1001})
}

此外,Gorm还提供事物、Hook等高级功能。

2.RPC框架Kitex

Kitex是一个高性能、强可扩展性的 Go RPC 框架,它的使用场景就是基于 thrift 定义的 IDL 来定义服务接口,实现客户端和服务端的通信。下面是一个IDL:

namespace go api
 
struct Request {
    1: string message
}
 
struct Response {
    1: string message
}
 
service KStudy {
    Response ehco(1: Request req)
}

如果我们要进行RPC,就需要知道对方的接口是什么,需要传什么参数,同时也需要知道返回值是什么样的。这时候,就需要通过IDL来约定双方的协议,就像在写代码的时候需要调用某个函数,我们需要知道函数签名一样。

由于目前Kitex不支持Windows操作系统,所以我对其知识点的学习更多停留在理论知识上,无法对其进行实践操作,在使用Go语言进行项目的微服务架构时,Kitex是一个不二之选。

Kitex的架构设计图如下:

1.png

具体文档参考:Kitex官网

3.HTTP框架Hertz

Hertz是一个 Golang 微服务 HTTP 框架,使其具有高易用性、高性能、高扩展性等特点,目前在字节跳动内部已广泛使用。下面是Hertz框架的基本使用代码:

package main

import (
	"context"

	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/common/utils"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main() {
	h := server.Default()

	h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
		ctx.JSON(consts.StatusOK, utils.H{"message": "pong"})
	})

	h.Spin()
}

此外,我们还可以使用Hert 自带的命令行工具hz配合IDL帮助我们快速生成基础代码。

具体文档:Hertz官网

三、实践练习例子:

项目功能模块如下图:

2.png

项目调用关系如下图:

3.png

Hertz关键代码如下:

// CreateNote create note info
func CreateNote(ctx context.Context, c *app.RequestContext) {
	var noteVar NoteParam
	if err := c.Bind(&noteVar); err != nil {
		SendResponse(c, errno.ConvertErr(err), nil)
		return
	}

	if len(noteVar.Title) == 0 || len(noteVar.Content) == 0 {
		SendResponse(c, errno.ParamErr, nil)
		return
	}

	claims := jwt.ExtractClaims(ctx, c)
	userID := int64(claims[constants.IdentityKey].(float64))
	err := rpc.CreateNote(context.Background(), &notedemo.CreateNoteRequest{
		UserId:  userID,
		Content: noteVar.Content, Title: noteVar.Title,
	})
	if err != nil {
		SendResponse(c, errno.ConvertErr(err), nil)
		return
	}
	SendResponse(c, errno.Success, nil)
}

Kitex的Client关键代码如下:

func initNoteRpc() {
	r, err := etcd.NewEtcdResolver([]string{constants.EtcdAddress})
	if err != nil {
		panic(err)
	}

	c, err := noteservice.NewClient(
		constants.NoteServiceName,
		client.WithMiddleware(middleware.CommonMiddleware),
		client.WithInstanceMW(middleware.ClientMiddleware),
		client.WithMuxConnection(1),                       // mux
		client.WithRPCTimeout(3*time.Second),              // rpc timeout
		client.WithConnectTimeout(50*time.Millisecond),    // conn timeout
		client.WithFailureRetry(retry.NewFailurePolicy()), // retry
		client.WithSuite(trace.NewDefaultClientSuite()),   // tracer
		client.WithResolver(r),                            // resolver
	)
	if err != nil {
		panic(err)
	}
	noteClient = c
}

// CreateNote create note info
func CreateNote(ctx context.Context, req *notedemo.CreateNoteRequest) error {
	resp, err := noteClient.CreateNote(ctx, req)
	if err != nil {
		return err
	}
	if resp.BaseResp.StatusCode != 0 {
		return errno.NewErrNo(resp.BaseResp.StatusCode, resp.BaseResp.StatusMessage)
	}
	return nil
}

Kitex的Server关键代码如下:

type CreateNoteService struct {
	ctx context.Context
}

// NewCreateNoteService new CreateNoteService
func NewCreateNoteService(ctx context.Context) *CreateNoteService {
	return &CreateNoteService{ctx: ctx}
}

// CreateNote create note info
func (s *CreateNoteService) CreateNote(req *notedemo.CreateNoteRequest) error {
	noteModel := &db.Note{
		UserID:  req.UserId,
		Title:   req.Title,
		Content: req.Content,
	}
	return db.CreateNote(s.ctx, []*db.Note{noteModel})
}

Gorm使用代码如下:

type User struct {
	gorm.Model
	UserName string `json:"user_name"`
	Password string `json:"password"`
}

func (u *User) TableName() string {
	return constants.UserTableName
}

// MGetUsers multiple get list of user info
func MGetUsers(ctx context.Context, userIDs []int64) ([]*User, error) {
	res := make([]*User, 0)
	if len(userIDs) == 0 {
		return res, nil
	}

	if err := DB.WithContext(ctx).Where("id in ?", userIDs).Find(&res).Error; err != nil {
		return nil, err
	}
	return res, nil
}

// CreateUser create user info
func CreateUser(ctx context.Context, users []*User) error {
	return DB.WithContext(ctx).Create(users).Error
}

// QueryUser query list of user info
func QueryUser(ctx context.Context, userName string) ([]*User, error) {
	res := make([]*User, 0)
	if err := DB.WithContext(ctx).Where("user_name = ?", userName).Find(&res).Error; err != nil {
		return nil, err
	}
	return res, nil
}

四、课后个人总结:

通过对Go框架三件套进行学习,让我在Go语言的学习上更上一层楼,开始接触了各种框架,了解了Gorm/Kitex/Hertz是什么,并且熟悉了三件套的基本语法,最后通过一个项目实战,将三者结合在一起进行学习,使得我在使用Go语言进行项目开发时更加标准化。