Go框架|青训营笔记

97 阅读2分钟

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

课堂主要内容

  • Gorm
  • Kitex
  • Hertz

Gorm

连接数据库:

dsn := "username:password@tcp(host:port)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})

在dsn里可以设置登录数据库的用户名密码,数据库地址端口等一众配置信息。还可以在&gorm.Config{}设置数据库。就比如:

&gorm.Config{
        SkipDefaultTransaction: true, // 关闭默认事务
        PrepareStmt:            true // 使用PrepareStmt缓存预编译语句可以提高后续调用的速度
}

创建对应表的结构

type User struct {
	gorm.Model
	UserName     string
        Password     string
}
func (user User) TableName() string {
	return "user"
}

其中gorm.Model其中包括:IDCreatedAtUpdatedAt , DeletedAt等字段。TableName是该结构在数据库中的表名,如果不指定就默认使用蛇形。

增删改查

新增:

db.Create(&User{UserName: "user1", Password: "password1"})

查询:

res := []User
db.Where("UserName = ?", "no1").Find(&res) //用where条件语句
db.Where(&User{UserName: "no1", Password: ""}).Find(&res)    //这里struct中的零值会被忽略
db.Where(map[string]interface{}{"UserName": "no1", Password: ""}).Find(&res)  //而map中的零值不会被忽略掉

修改:

db.Where("UserName = ?", "no1").Updates(User{UserName: "no2", Password: "password2"})

删除: 删除分为硬删和软删

db.Where("UserName = ?", "no2").Delete(User{}) //硬
// 软删除
// 在model添加gorm.DeletedAt类型字段该model调用delete即可
// 而且可以用Unscoped()来获取被软删的数据
users := []User{}
db.Unscoped().Where("UserName = ?", "no2").Find(&users)

事务:

手动事务:

// 事务
// 事务一旦开始,你就应该使用 tx 处理数据
tx := db.Begin() // 开始事务
tx.commit() // 提交事务
tx.Rollback() // 回滚

自动事务:

db.Transaction(func(tx *gorm.DB) error {
  // 在事务中执行一些 db 操作(从这里开始,您应该使用 'tx' 而不是 'db')
  if err := tx.Create(&Animal{Name: "Giraffe"}).Error; err != nil {
    // 返回任何错误都会回滚事务
    return err
  }

  if err := tx.Create(&Animal{Name: "Lion"}).Error; err != nil {
    return err
  }

  // 返回 nil 提交事务
  return nil
})

hook:

gorm 提供hook的能力 即在执行函数之前或之后自动调用的函数

func (u *User) BeforCreate(tx *gorm.DB) (err error) {
	if u.Age < 0 {
		return errors.New("can't sace invalid data")
	}
	return nil
}

Kitex

定义idl,echo.thrift

namespace go api

struct Request {
	1: string message
}

struct Response {
	1: string message
}

service Hello {
    Response echo(1: Request req)
}

然后通过以下命令生成代码,your_module_name和your_service_name替换成go.mod文件里的module名和自己的服务名

kitex -module your_module_name -service your_service_name xxx.thrift

生成后项目目录结构如下,在这个目录下用go run .即可运行该服务

.
|-- build.sh
|-- echo.thrift
|-- handler.go
|-- kitex_gen
|   `-- api
|       |-- echo
|       |   |-- client.go
|       |   |-- echo.go
|       |   |-- invoker.go
|       |   `-- server.go
|       |-- echo.go
|       `-- k-echo.go
|-- main.go
`-- script
    |-- bootstrap.sh
    `-- settings.py

我们需要实现的接口函数逻辑,在handler.go中

package main

import (
  "context"
  "example/kitex_gen/api"
)

// EchoImpl implements the last service interface defined in the IDL.
type EchoImpl struct{}

// Echo implements the EchoImpl interface.
func (s *EchoImpl) Echo(ctx context.Context, req *api.Request) (resp *api.Response, err error) {
  // TODO: Your code here...
  return
}

至此一个简易的rpc服务端就完事了 rpc的客户端调用kitex_gen和kitex包的代码就行

package main

import (
	"context"
	"log"
	"time"

	"github.com/cloudwego/kitex-examples/hello/kitex_gen/api"
	"github.com/cloudwego/kitex-examples/hello/kitex_gen/api/hello"
	"github.com/cloudwego/kitex/client"
)

func main() {
	client, err := hello.NewClient("hello", client.WithHostPorts("0.0.0.0:8888"))
	if err != nil {
		log.Fatal(err)
	}
	for {
		req := &api.Request{Message: "my request"}
		resp, err := client.Echo(context.Background(), req)
		if err != nil {
			log.Fatal(err)
		}
		log.Println(resp)
		time.Sleep(time.Second)
	}
}

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()
}

使用Hertz官方实例的代码就可以启动一个简单web服务了 而且Hertz还支持路由分组

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	v1 := h.Group("/v1")
	v1.GET("/get", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "get")
	})
	v1.POST("/post", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "post")
	})
	v2 := h.Group("/v2")
	v2.PUT("/put", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "put")
	})
	v2.DELETE("/delete", func(ctx context.Context, c *app.RequestContext) {
		c.String(consts.StatusOK, "delete")
	})
	h.Spin()
}

Hertz的路由类型有三种:静态路由、参数路由、通配路由

路由的优先级:静态路由 > 命名路由 > 通配路由

静态路由就是上面的路由方式

参数路由:

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	// This handler will match: "/hertz/version", but will not match : "/hertz/" or "/hertz"
	h.GET("/hertz/:version", func(ctx context.Context, c *app.RequestContext) {
		version := c.Param("version")
		c.String(consts.StatusOK, "Hello %s", version)
	})
	h.Spin()
}

通配路由:

package main

import (
	"context"
	"github.com/cloudwego/hertz/pkg/app"
	"github.com/cloudwego/hertz/pkg/app/server"
	"github.com/cloudwego/hertz/pkg/protocol/consts"
)

func main(){
	h := server.Default(server.WithHostPorts("127.0.0.1:8080"))
	// However, this one will match "/hertz/v1/" and "/hertz/v2/send"
	h.GET("/hertz/:version/*action", func(ctx context.Context, c *app.RequestContext) {
		version := c.Param("version")
		action := c.Param("action")
		message := version + " is " + action
		c.String(consts.StatusOK, message)
	})
	h.Spin()
}